[med-svn] [tab2mage] 10/12: New upstream version 20080626

Andreas Tille tille at debian.org
Sat Dec 30 16:40:42 UTC 2017


This is an automated email from the git hooks/post-receive script.

tille pushed a commit to branch master
in repository tab2mage.

commit 5fd54b739dca3b116587f73a456c191fb07b3bcc
Author: Andreas Tille <tille at debian.org>
Date:   Sat Dec 30 17:38:55 2017 +0100

    New upstream version 20080626
---
 COPYING                                            |    44 +
 Changes                                            |   364 +
 MAGETAB.txt                                        |   135 +
 MANIFEST                                           |   706 +
 META.yml                                           |    25 +
 Makefile.PL                                        |   163 +
 README                                             |   187 +
 README-windows.txt                                 |    62 +
 adv_inst/InstallConfig.yml-example                 |    51 +
 adv_inst/README                                    |     6 +
 adv_inst/T2MInstaller.pm                           |   321 +
 adv_inst/deploy.pl                                 |    66 +
 arraymage/arraymage.pl                             |   499 +
 arraymage/cdf2mage.pl                              |   728 +
 automation/admin/CHANGELOG                         |   604 +
 automation/admin/INSTALL                           |    68 +
 automation/admin/README                            |   153 +
 automation/admin/README_LOGIN                      |    96 +
 automation/admin/Rakefile                          |    10 +
 .../admin/app/controllers/account_controller.rb    |    52 +
 automation/admin/app/controllers/application.rb    |     7 +
 .../app/controllers/array_designs_controller.rb    |   124 +
 .../admin/app/controllers/category_controller.rb   |    70 +
 .../admin/app/controllers/design_controller.rb     |    66 +
 .../admin/app/controllers/event_controller.rb      |    21 +
 .../app/controllers/experiments_controller.rb      |   148 +
 .../admin/app/controllers/factor_controller.rb     |     6 +
 .../admin/app/controllers/material_controller.rb   |    66 +
 .../admin/app/controllers/miamexps_controller.rb   |    35 +
 .../admin/app/controllers/organism_controller.rb   |    90 +
 .../admin/app/controllers/protocols_controller.rb  |    90 +
 .../controllers/quantitation_type_controller.rb    |     6 +
 .../admin/app/controllers/tab2mages_controller.rb  |    43 +
 .../admin/app/controllers/taxon_controller.rb      |    71 +
 .../admin/app/controllers/users_controller.rb      |    89 +
 automation/admin/app/helpers/account_helper.rb     |     2 +
 automation/admin/app/helpers/application_helper.rb |     3 +
 .../admin/app/helpers/array_designs_helper.rb      |     2 +
 automation/admin/app/helpers/category_helper.rb    |     2 +
 automation/admin/app/helpers/design_helper.rb      |     2 +
 automation/admin/app/helpers/event_helper.rb       |     2 +
 automation/admin/app/helpers/experiments_helper.rb |     2 +
 automation/admin/app/helpers/factor_helper.rb      |     2 +
 automation/admin/app/helpers/material_helper.rb    |     2 +
 automation/admin/app/helpers/miamexps_helper.rb    |     2 +
 automation/admin/app/helpers/organism_helper.rb    |     2 +
 automation/admin/app/helpers/protocols_helper.rb   |     2 +
 .../admin/app/helpers/quantitation_type_helper.rb  |     2 +
 automation/admin/app/helpers/tab2mages_helper.rb   |     2 +
 automation/admin/app/helpers/taxon_helper.rb       |     2 +
 automation/admin/app/helpers/users_helper.rb       |     2 +
 automation/admin/app/models/array_design.rb        |    12 +
 automation/admin/app/models/category.rb            |     7 +
 automation/admin/app/models/data_file.rb           |     3 +
 automation/admin/app/models/design.rb              |     7 +
 automation/admin/app/models/design_instance.rb     |     4 +
 automation/admin/app/models/event.rb               |     8 +
 automation/admin/app/models/experiment.rb          |    43 +
 automation/admin/app/models/factor.rb              |     7 +
 automation/admin/app/models/material.rb            |     7 +
 automation/admin/app/models/material_instance.rb   |     4 +
 automation/admin/app/models/organism.rb            |    10 +
 automation/admin/app/models/organism_instance.rb   |     4 +
 automation/admin/app/models/permission.rb          |     6 +
 automation/admin/app/models/protocol.rb            |     4 +
 automation/admin/app/models/quantitation_type.rb   |     7 +
 automation/admin/app/models/role.rb                |     7 +
 automation/admin/app/models/spreadsheet.rb         |     3 +
 automation/admin/app/models/taxon.rb               |     8 +
 automation/admin/app/models/user.rb                |    59 +
 automation/admin/app/views/account/login.rhtml     |    22 +
 automation/admin/app/views/account/logout.rhtml    |    10 +
 automation/admin/app/views/account/signup.rhtml    |    18 +
 automation/admin/app/views/account/welcome.rhtml   |    12 +
 .../admin/app/views/array_designs/_aedwinfo.rhtml  |    18 +
 .../admin/app/views/array_designs/_form.rhtml      |    44 +
 .../admin/app/views/array_designs/_miameinfo.rhtml |     6 +
 .../admin/app/views/array_designs/_title.rhtml     |     3 +
 .../admin/app/views/array_designs/edit.rhtml       |    10 +
 .../admin/app/views/array_designs/list.rhtml       |    70 +
 .../admin/app/views/array_designs/list_all.rhtml   |    59 +
 automation/admin/app/views/array_designs/new.rhtml |     8 +
 .../admin/app/views/array_designs/show.rhtml       |    84 +
 automation/admin/app/views/category/_form.rhtml    |    34 +
 automation/admin/app/views/category/_title.rhtml   |     3 +
 automation/admin/app/views/category/edit.rhtml     |     8 +
 automation/admin/app/views/category/list.rhtml     |    42 +
 automation/admin/app/views/category/new.rhtml      |     8 +
 automation/admin/app/views/design/_form.rhtml      |    43 +
 automation/admin/app/views/design/_title.rhtml     |     3 +
 automation/admin/app/views/design/edit.rhtml       |     8 +
 automation/admin/app/views/design/list.rhtml       |    40 +
 automation/admin/app/views/design/new.rhtml        |     8 +
 automation/admin/app/views/event/_form.rhtml       |    62 +
 automation/admin/app/views/event/_title.rhtml      |     3 +
 automation/admin/app/views/event/edit.rhtml        |     2 +
 .../admin/app/views/experiments/_annotate.rhtml    |    52 +
 automation/admin/app/views/experiments/_form.rhtml |    26 +
 .../admin/app/views/experiments/_title.rhtml       |     3 +
 automation/admin/app/views/experiments/edit.rhtml  |    12 +
 automation/admin/app/views/experiments/list.rhtml  |    71 +
 automation/admin/app/views/experiments/new.rhtml   |    10 +
 automation/admin/app/views/experiments/show.rhtml  |   124 +
 .../admin/app/views/experiments/today_list.rhtml   |    61 +
 automation/admin/app/views/layouts/admin.rhtml     |   156 +
 automation/admin/app/views/material/_form.rhtml    |    33 +
 automation/admin/app/views/material/_title.rhtml   |     3 +
 automation/admin/app/views/material/edit.rhtml     |     8 +
 automation/admin/app/views/material/list.rhtml     |    38 +
 automation/admin/app/views/material/new.rhtml      |     8 +
 .../admin/app/views/miamexps/_aedwinfo.rhtml       |     9 +
 automation/admin/app/views/miamexps/_form.rhtml    |    86 +
 .../admin/app/views/miamexps/_miameinfo.rhtml      |     9 +
 automation/admin/app/views/miamexps/_title.rhtml   |     3 +
 automation/admin/app/views/miamexps/edit.rhtml     |    15 +
 automation/admin/app/views/miamexps/list.rhtml     |    72 +
 automation/admin/app/views/organism/_form.rhtml    |    25 +
 automation/admin/app/views/organism/_title.rhtml   |     3 +
 automation/admin/app/views/organism/edit.rhtml     |    10 +
 automation/admin/app/views/organism/list.rhtml     |    59 +
 automation/admin/app/views/organism/new.rhtml      |     8 +
 automation/admin/app/views/protocols/_form.rhtml   |    28 +
 automation/admin/app/views/protocols/_title.rhtml  |     3 +
 automation/admin/app/views/protocols/edit.rhtml    |    10 +
 automation/admin/app/views/protocols/list.rhtml    |    58 +
 automation/admin/app/views/protocols/new.rhtml     |     8 +
 automation/admin/app/views/protocols/show.rhtml    |     8 +
 .../admin/app/views/tab2mages/_aedwinfo.rhtml      |     9 +
 .../admin/app/views/tab2mages/_annotate.rhtml      |    52 +
 automation/admin/app/views/tab2mages/_form.rhtml   |    91 +
 .../admin/app/views/tab2mages/_miameinfo.rhtml     |     9 +
 automation/admin/app/views/tab2mages/_title.rhtml  |     3 +
 automation/admin/app/views/tab2mages/edit.rhtml    |    35 +
 automation/admin/app/views/tab2mages/list.rhtml    |    97 +
 automation/admin/app/views/tab2mages/new.rhtml     |    13 +
 automation/admin/app/views/taxon/_form.rhtml       |    34 +
 automation/admin/app/views/taxon/_title.rhtml      |     3 +
 .../admin/app/views/taxon/category_table.rhtml     |    21 +
 automation/admin/app/views/taxon/edit.rhtml        |     8 +
 automation/admin/app/views/taxon/list.rhtml        |    43 +
 automation/admin/app/views/taxon/new.rhtml         |     8 +
 automation/admin/app/views/users/_form.rhtml       |    31 +
 automation/admin/app/views/users/_title.rhtml      |     3 +
 automation/admin/app/views/users/edit.rhtml        |    10 +
 automation/admin/app/views/users/list.rhtml        |    54 +
 automation/admin/app/views/users/new.rhtml         |     8 +
 automation/admin/app/views/users/show.rhtml        |     8 +
 automation/admin/config/README                     |    45 +
 automation/admin/config/boot.rb                    |    19 +
 automation/admin/config/database.yml-mysql         |    20 +
 automation/admin/config/database.yml-sqlite        |    29 +
 automation/admin/config/environment.rb             |    66 +
 .../admin/config/environments/development.rb       |    20 +
 automation/admin/config/environments/production.rb |    19 +
 automation/admin/config/environments/test.rb       |    19 +
 automation/admin/config/lighttpd.conf              |    53 +
 automation/admin/config/routes.rb                  |    19 +
 automation/admin/db/database-mysql.dump            |   932 +
 automation/admin/db/database-mysql.schema          |   829 +
 .../admin/db/migration/migrate_2.0_to_2.1.sql      |   183 +
 .../admin/db/migration/migrate_2.1_to_2.2.sql      |   125 +
 .../admin/db/migration/migrate_csv2sqlite.pl       |   190 +
 .../admin/db/migration/migrate_sqlite2mysql.pl     |    64 +
 automation/admin/db/schema.pdf                     |   Bin 0 -> 374471 bytes
 automation/admin/db/schema.rb                      |   347 +
 automation/admin/doc/README_FOR_APP                |     2 +
 automation/admin/lib/acl_system.rb                 |    31 +
 automation/admin/lib/annotation.rb                 |    96 +
 automation/admin/lib/login_system.rb               |    87 +
 automation/admin/log/server.log                    |     0
 automation/admin/public/.htaccess                  |    40 +
 automation/admin/public/404.html                   |     8 +
 automation/admin/public/500.html                   |     8 +
 automation/admin/public/dispatch.cgi               |    10 +
 automation/admin/public/dispatch.fcgi              |    24 +
 automation/admin/public/dispatch.rb                |    10 +
 automation/admin/public/favicon.ico                |     0
 automation/admin/public/images/rails.png           |   Bin 0 -> 1786 bytes
 automation/admin/public/index.html                 |    34 +
 automation/admin/public/javascripts/controls.js    |   750 +
 automation/admin/public/javascripts/dragdrop.js    |   584 +
 automation/admin/public/javascripts/effects.js     |   854 +
 automation/admin/public/javascripts/prototype.js   |  1785 ++
 automation/admin/public/robots.txt                 |     1 +
 automation/admin/public/stylesheets/admin.css      |   106 +
 automation/admin/script/about                      |     3 +
 automation/admin/script/benchmarker                |    19 +
 automation/admin/script/breakpointer               |     3 +
 automation/admin/script/console                    |     3 +
 automation/admin/script/destroy                    |     3 +
 automation/admin/script/generate                   |     3 +
 automation/admin/script/performance/benchmarker    |     3 +
 automation/admin/script/performance/profiler       |     3 +
 automation/admin/script/plugin                     |     3 +
 automation/admin/script/process/reaper             |     3 +
 automation/admin/script/process/spawner            |     3 +
 automation/admin/script/process/spinner            |     3 +
 automation/admin/script/profiler                   |    34 +
 automation/admin/script/runner                     |     3 +
 automation/admin/script/server                     |     3 +
 automation/admin/test/fixtures/array_designs.yml   |     9 +
 automation/admin/test/fixtures/categories.yml      |     9 +
 automation/admin/test/fixtures/data_files.yml      |     5 +
 automation/admin/test/fixtures/designs.yml         |    13 +
 automation/admin/test/fixtures/events.yml          |     9 +
 automation/admin/test/fixtures/experiments.yml     |    18 +
 automation/admin/test/fixtures/factors.yml         |     7 +
 automation/admin/test/fixtures/materials.yml       |    11 +
 automation/admin/test/fixtures/organisms.yml       |    13 +
 automation/admin/test/fixtures/permissions.yml     |    17 +
 .../admin/test/fixtures/permissions_roles.yml      |    11 +
 automation/admin/test/fixtures/protocols.yml       |    11 +
 .../admin/test/fixtures/quantitation_types.yml     |     7 +
 automation/admin/test/fixtures/roles.yml           |    11 +
 automation/admin/test/fixtures/roles_users.yml     |     5 +
 automation/admin/test/fixtures/spreadsheets.yml    |     5 +
 automation/admin/test/fixtures/taxons.yml          |    11 +
 automation/admin/test/fixtures/users.yml           |    16 +
 .../test/functional/account_controller_test.rb     |    81 +
 .../functional/array_designs_controller_test.rb    |    88 +
 .../test/functional/category_controller_test.rb    |    21 +
 .../test/functional/design_controller_test.rb      |    21 +
 .../admin/test/functional/event_controller_test.rb |    21 +
 .../test/functional/experiments_controller_test.rb |    94 +
 .../test/functional/factor_controller_test.rb      |    21 +
 .../test/functional/material_controller_test.rb    |    21 +
 .../test/functional/miamexps_controller_test.rb    |    18 +
 .../test/functional/organism_controller_test.rb    |    21 +
 .../test/functional/protocols_controller_test.rb   |    88 +
 .../quantitation_type_controller_test.rb           |    21 +
 .../test/functional/tab2mages_controller_test.rb   |    18 +
 .../admin/test/functional/taxon_controller_test.rb |    18 +
 .../admin/test/functional/users_controller_test.rb |    18 +
 automation/admin/test/test_helper.rb               |    28 +
 automation/admin/test/unit/array_design_test.rb    |    14 +
 automation/admin/test/unit/category_test.rb        |    14 +
 automation/admin/test/unit/data_file_test.rb       |    10 +
 automation/admin/test/unit/design_test.rb          |    10 +
 automation/admin/test/unit/event_test.rb           |    10 +
 automation/admin/test/unit/experiment_test.rb      |    10 +
 automation/admin/test/unit/factor_test.rb          |    10 +
 automation/admin/test/unit/material_test.rb        |    10 +
 automation/admin/test/unit/organism_test.rb        |    14 +
 automation/admin/test/unit/permission_test.rb      |    10 +
 automation/admin/test/unit/protocol_test.rb        |    10 +
 .../admin/test/unit/quantitation_type_test.rb      |    10 +
 automation/admin/test/unit/role_test.rb            |    10 +
 automation/admin/test/unit/spreadsheet_test.rb     |    10 +
 automation/admin/test/unit/taxon_test.rb           |    14 +
 automation/admin/test/unit/user_test.rb            |   101 +
 automation/autosubs_checkd.pl                      |    26 +
 automation/autosubs_exportd.pl                     |    26 +
 automation/autosubs_httpd.pl                       |   298 +
 automation/calculate_celqc.pl                      |    52 +
 automation/calculate_datahashes.pl                 |   289 +
 automation/cel_qcstats.pl                          |   181 +
 automation/database_overseer.pl                    |   170 +
 automation/magetab.cgi                             |    76 +
 automation/magetab_checkd.pl                       |    26 +
 automation/magetab_exportd.pl                      |    26 +
 automation/magetab_insert_sub.pl                   |   121 +
 automation/mx_autocheck_daemon.pl                  |    30 +
 automation/mx_export_daemon.pl                     |    26 +
 automation/mx_reset_experiment.pl                  |   235 +
 automation/populate_tracking_info.pl               |   599 +
 automation/reset_experiment.pl                     |   168 +
 automation/t2m_insert_sub.pl                       |    77 +
 automation/tab2mage.cgi                            |    61 +
 bin/convert_to_mcmr.pl                             |   116 +
 bin/expt_check.pl                                  |   454 +
 bin/magetab.pl                                     |   361 +
 bin/parse_affy.pl                                  |   249 +
 bin/t2m_visualize.pl                               |   181 +
 bin/tab2mage.pl                                    |   388 +
 debian/TODO                                        |    41 -
 debian/changelog                                   |    12 -
 debian/compat                                      |     1 -
 debian/control                                     |    40 -
 debian/copyright                                   |    24 -
 debian/dirs                                        |     2 -
 debian/docs                                        |     3 -
 debian/examples                                    |     1 -
 debian/patches/add-binaries-to-make-install.patch  |    14 -
 debian/patches/series                              |     1 -
 debian/rules                                       |     8 -
 debian/source/format                               |     1 -
 debian/watch                                       |     2 -
 docs/ArrayExpress/ArrayMAGE.html                   |   476 +
 docs/ArrayExpress/AutoSubmission/Creator.html      |   185 +
 docs/ArrayExpress/AutoSubmission/DB.html           |  2905 +++
 docs/ArrayExpress/AutoSubmission/Spreadsheet.html  |   118 +
 docs/ArrayExpress/AutoSubmission/WebForm.html      |   118 +
 docs/ArrayExpress/Curator/Common.html              |   201 +
 docs/ArrayExpress/Curator/Config.html              |   462 +
 docs/ArrayExpress/Curator/Database.html            |   137 +
 docs/ArrayExpress/Curator/Entrez_list.html         |    82 +
 docs/ArrayExpress/Curator/ExperimentChecker.html   |   392 +
 docs/ArrayExpress/Curator/Logger.html              |   204 +
 docs/ArrayExpress/Curator/MAGE/Definitions.html    |   712 +
 docs/ArrayExpress/Curator/MIAMExpress.html         |   193 +
 docs/ArrayExpress/Curator/Report.html              |   105 +
 docs/ArrayExpress/Curator/Standalone.html          |    87 +
 docs/ArrayExpress/Curator/Validate.html            |   184 +
 docs/ArrayExpress/Curator/Visualize.html           |    86 +
 docs/ArrayExpress/Datafile.html                    |   478 +
 docs/ArrayExpress/Datafile/Affymetrix.html         |    96 +
 docs/ArrayExpress/Datafile/Affymetrix/CDF.html     |    79 +
 .../Datafile/Affymetrix/CDF/GDAC_CDF.html          |    80 +
 .../Datafile/Affymetrix/CDF/XDA_CDF.html           |    81 +
 .../Datafile/Affymetrix/CDFFactory.html            |    95 +
 docs/ArrayExpress/Datafile/Affymetrix/CEL.html     |   128 +
 .../Datafile/Affymetrix/CEL/CELv3.html             |    81 +
 .../Datafile/Affymetrix/CEL/CELv4.html             |   102 +
 .../Datafile/Affymetrix/CELFactory.html            |    95 +
 docs/ArrayExpress/Datafile/Affymetrix/CHP.html     |   106 +
 .../Datafile/Affymetrix/CHP/CHPv12.html            |    84 +
 .../Datafile/Affymetrix/CHP/CHPv13.html            |    84 +
 .../Datafile/Affymetrix/CHP/CHPv8.html             |    85 +
 .../Datafile/Affymetrix/CHP/GDAC_CHP.html          |   108 +
 .../Datafile/Affymetrix/CHP/XDA_CHP.html           |    84 +
 .../Datafile/Affymetrix/CHPFactory.html            |    95 +
 docs/ArrayExpress/Datafile/Affymetrix/EXP.html     |   165 +
 .../Datafile/Affymetrix/EXPFactory.html            |    94 +
 docs/ArrayExpress/Datafile/Affymetrix/Parser.html  |   183 +
 docs/ArrayExpress/Datafile/ArrayDesign.html        |   126 +
 docs/ArrayExpress/Datafile/Parser.html             |    67 +
 docs/ArrayExpress/Datafile/QT_list.html            |   248 +
 docs/ArrayExpress/MAGETAB.html                     |   286 +
 docs/ArrayExpress/MAGETAB/Checker.html             |   215 +
 docs/MAGETAB_logo.png                              |   Bin 0 -> 7493 bytes
 docs/MAGETAB_logo_small.png                        |   Bin 0 -> 4790 bytes
 docs/T2M_logo.png                                  |   Bin 0 -> 8513 bytes
 docs/T2M_logo_small.png                            |   Bin 0 -> 5804 bytes
 docs/aelogo.png                                    |   Bin 0 -> 4095 bytes
 docs/aesubs.html                                   |   182 +
 docs/datafiles.html                                |   465 +
 docs/detail.html                                   |   392 +
 docs/expt_check.html                               |   347 +
 docs/idf.html                                      |   378 +
 docs/index.html                                    |   101 +
 docs/magetab.html                                  |   235 +
 docs/magetab_docs.html                             |   429 +
 docs/magetab_subs.html                             |   589 +
 docs/magetab_vs_tab2mage.html                      |   202 +
 docs/modules.html                                  |   282 +
 docs/parse_affy.html                               |   104 +
 docs/sdrf.html                                     |   941 +
 docs/sdrf_layout.png                               |   Bin 0 -> 33976 bytes
 docs/spreadsheet.html                              |   625 +
 docs/style.css                                     |   171 +
 docs/submit.css                                    |    91 +
 docs/t2m_visualize.html                            |   103 +
 docs/tab2mage.html                                 |   289 +
 docs/usage.html                                    |   168 +
 examples/affymetrix/Data1.CEL                      |    49 +
 examples/affymetrix/Data1.CHP                      |   Bin 0 -> 11922 bytes
 examples/affymetrix/Data1.EXP                      |    49 +
 examples/affymetrix/Data2.CEL                      |   Bin 0 -> 670 bytes
 examples/affymetrix/Data2.CHP                      |   Bin 0 -> 1013 bytes
 examples/affymetrix/Data2.EXP                      |    49 +
 examples/affymetrix/Data3.CEL                      |    49 +
 examples/affymetrix/Data3.CHP                      |   Bin 0 -> 11922 bytes
 examples/affymetrix/Data3.EXP                      |    49 +
 examples/affymetrix/Data4.CEL                      |   Bin 0 -> 670 bytes
 examples/affymetrix/Data4.CHP                      |   Bin 0 -> 1013 bytes
 examples/affymetrix/Data4.EXP                      |    49 +
 examples/affymetrix/YG_S98.cdf                     |   192 +
 examples/affymetrix/affymetrix.png                 |   Bin 0 -> 14687 bytes
 examples/affymetrix/affymetrix.txt                 |    36 +
 examples/chip_chip/Data1.txt                       |    37 +
 examples/chip_chip/Data2.txt                       |    40 +
 examples/chip_chip/chip_chip.png                   |   Bin 0 -> 10378 bytes
 examples/chip_chip/chip_chip.txt                   |    39 +
 examples/final_data_matrix/Data1.txt               |    37 +
 examples/final_data_matrix/Data2.txt               |    40 +
 examples/final_data_matrix/Data3.txt               |    37 +
 examples/final_data_matrix/Data4.txt               |    40 +
 examples/final_data_matrix/E-EXML-7.xml.dot.jpg    |   Bin 0 -> 646047 bytes
 examples/final_data_matrix/FGEM.txt                |    11 +
 examples/final_data_matrix/NormData1.txt           |    11 +
 examples/final_data_matrix/NormData2.txt           |    11 +
 examples/final_data_matrix/NormData3.txt           |    11 +
 examples/final_data_matrix/NormData4.txt           |    11 +
 examples/final_data_matrix/final_data_matrix.png   |   Bin 0 -> 28295 bytes
 examples/final_data_matrix/final_data_matrix.txt   |    46 +
 examples/illumina/GeneGroupedData.txt              |    18 +
 examples/illumina/ProbeData.txt                    |    18 +
 examples/illumina/illumina.png                     |   Bin 0 -> 78951 bytes
 examples/illumina/illumina.txt                     |    46 +
 examples/loop_design/loop_design.png               |   Bin 0 -> 33136 bytes
 examples/loop_design/loop_design.txt               |    55 +
 examples/magetab/affymetrix/ATH1-121501.cdf        |   192 +
 examples/magetab/affymetrix/Data1.CEL              |    49 +
 examples/magetab/affymetrix/Data1.CHP              |   Bin 0 -> 11922 bytes
 examples/magetab/affymetrix/Data1.EXP              |    49 +
 examples/magetab/affymetrix/Data2.CEL              |   Bin 0 -> 670 bytes
 examples/magetab/affymetrix/Data2.CHP              |   Bin 0 -> 1013 bytes
 examples/magetab/affymetrix/Data2.EXP              |    49 +
 examples/magetab/affymetrix/Data3.CEL              |    49 +
 examples/magetab/affymetrix/Data3.CHP              |   Bin 0 -> 11922 bytes
 examples/magetab/affymetrix/Data3.EXP              |    49 +
 examples/magetab/affymetrix/Data4.CEL              |   Bin 0 -> 670 bytes
 examples/magetab/affymetrix/Data4.CHP              |   Bin 0 -> 1013 bytes
 examples/magetab/affymetrix/Data4.EXP              |    49 +
 examples/magetab/affymetrix/YG_S98.cdf             |   192 +
 examples/magetab/affymetrix/affy_dm.txt            |    14 +
 examples/magetab/affymetrix/affy_sdrf.txt          |     5 +
 examples/magetab/affymetrix/affymetrix.idf         |    49 +
 examples/magetab/illumina/GeneGroupedData.txt      |    18 +
 examples/magetab/illumina/ProbeData.txt            |    18 +
 examples/magetab/illumina/illumina.idf             |    28 +
 examples/magetab/illumina/illumina_sdrf.txt        |    21 +
 examples/magetab/real/E-MEXP-880.idf               |    27 +
 examples/magetab/real/E-MEXP-880_sdrf.txt          |    41 +
 examples/magetab/real/E-TABM-102.idf               |    33 +
 examples/magetab/real/E-TABM-102_sdrf.txt          |    31 +
 examples/magetab/real/E-TABM-134.idf               |    27 +
 examples/magetab/real/E-TABM-134_sdrf.txt          |    26 +
 examples/magetab/real/E-TABM-136.idf               |    24 +
 examples/magetab/real/E-TABM-136_sdrf.txt          |    81 +
 examples/magetab/real/E-TABM-140.idf               |    32 +
 examples/magetab/real/E-TABM-140_sdrf.txt          |    73 +
 examples/magetab/real/E-TABM-16.idf                |    26 +
 examples/magetab/real/E-TABM-163.idf               |    27 +
 examples/magetab/real/E-TABM-163_sdrf.txt          |    24 +
 examples/magetab/real/E-TABM-16_sdrf.txt           |   105 +
 examples/magetab/real/E-TABM-18.idf                |    26 +
 examples/magetab/real/E-TABM-18_sdrf.txt           |    56 +
 examples/magetab/real/E-TABM-22.idf                |    24 +
 examples/magetab/real/E-TABM-22_sdrf.txt           |   252 +
 examples/magetab/real/E-TABM-33.idf                |    27 +
 examples/magetab/real/E-TABM-33_sdrf.txt           |    31 +
 examples/magetab/real/E-TABM-35.idf                |    26 +
 examples/magetab/real/E-TABM-35_sdrf.txt           |    62 +
 examples/magetab/real/E-TABM-54.idf                |    21 +
 examples/magetab/real/E-TABM-54_sdrf.txt           |   275 +
 examples/magetab/real/E-TABM-66.idf                |    29 +
 examples/magetab/real/E-TABM-66_sdrf.txt           |   154 +
 examples/magetab/real/E-TABM-70.idf                |    28 +
 examples/magetab/real/E-TABM-70_sdrf.txt           |    65 +
 examples/magetab/real/solexa_example.idf           |    36 +
 examples/magetab/real/solexa_example.sdrf.txt      |    11 +
 examples/magetab/two_color/Data1.txt               |    37 +
 examples/magetab/two_color/Data2.txt               |    40 +
 examples/magetab/two_color/Data3.txt               |    37 +
 examples/magetab/two_color/Data4.txt               |    40 +
 examples/magetab/two_color/NormData1.txt           |    11 +
 examples/magetab/two_color/NormData2.txt           |    11 +
 examples/magetab/two_color/NormData3.txt           |    11 +
 examples/magetab/two_color/NormData4.txt           |    11 +
 examples/magetab/two_color/test.idf                |    49 +
 examples/magetab/two_color/test_sdrf.txt           |     6 +
 examples/normalized_data/Data1.txt                 |    37 +
 examples/normalized_data/Data2.txt                 |    40 +
 examples/normalized_data/NormData1.txt             |    11 +
 examples/normalized_data/NormData2.txt             |    11 +
 examples/normalized_data/normalized_data.png       |   Bin 0 -> 10751 bytes
 examples/normalized_data/normalized_data.txt       |    40 +
 examples/other_QTs.txt                             |    27 +
 examples/parameters/Data1.txt                      |    37 +
 examples/parameters/Data2.txt                      |    40 +
 examples/parameters/parameters.png                 |   Bin 0 -> 10138 bytes
 examples/parameters/parameters.txt                 |    39 +
 examples/real/E-MEXP-880.png                       |   Bin 0 -> 210713 bytes
 examples/real/E-MEXP-880.txt                       |    68 +
 examples/real/E-TABM-102.png                       |   Bin 0 -> 103372 bytes
 examples/real/E-TABM-102.txt                       |    56 +
 examples/real/E-TABM-134.png                       |   Bin 0 -> 67443 bytes
 examples/real/E-TABM-134.txt                       |    57 +
 examples/real/E-TABM-136.png                       |   Bin 0 -> 83344 bytes
 examples/real/E-TABM-136.txt                       |   106 +
 examples/real/E-TABM-140.png                       |   Bin 0 -> 138852 bytes
 examples/real/E-TABM-140.txt                       |   108 +
 examples/real/E-TABM-16.png                        |   Bin 0 -> 213842 bytes
 examples/real/E-TABM-16.txt                        |   166 +
 examples/real/E-TABM-163.png                       |   Bin 0 -> 73767 bytes
 examples/real/E-TABM-163.txt                       |    55 +
 examples/real/E-TABM-18.png                        |   Bin 0 -> 85899 bytes
 examples/real/E-TABM-18.txt                        |    82 +
 examples/real/E-TABM-22.png                        |   Bin 0 -> 420693 bytes
 examples/real/E-TABM-22.txt                        |   274 +
 examples/real/E-TABM-33.png                        |   Bin 0 -> 86924 bytes
 examples/real/E-TABM-33.txt                        |    55 +
 examples/real/E-TABM-35.png                        |   Bin 0 -> 118759 bytes
 examples/real/E-TABM-35.txt                        |    94 +
 examples/real/E-TABM-54.png                        |   Bin 0 -> 263753 bytes
 examples/real/E-TABM-54.txt                        |   287 +
 examples/real/E-TABM-66.png                        |   Bin 0 -> 171814 bytes
 examples/real/E-TABM-66.txt                        |   184 +
 examples/real/E-TABM-70.png                        |   Bin 0 -> 63144 bytes
 examples/real/E-TABM-70.txt                        |   101 +
 examples/reference/Data1.txt                       |    37 +
 examples/reference/Data2.txt                       |    40 +
 examples/reference/Data3.txt                       |    11 +
 examples/reference/reference.png                   |   Bin 0 -> 13757 bytes
 examples/reference/reference.txt                   |    42 +
 examples/two_color/Data1.txt                       |    37 +
 examples/two_color/Data2.txt                       |    40 +
 examples/two_color/two_color.png                   |   Bin 0 -> 11098 bytes
 examples/two_color/two_color.txt                   |    45 +
 index.html                                         |   356 +
 lib/ArrayExpress/ArrayMAGE.pm                      |   658 +
 lib/ArrayExpress/ArrayMAGE/ArrayDesign.pm          |    42 +
 lib/ArrayExpress/ArrayMAGE/BioSequence.pm          |    96 +
 lib/ArrayExpress/ArrayMAGE/CompositeGroup.pm       |    84 +
 lib/ArrayExpress/ArrayMAGE/CompositeSequence.pm    |   119 +
 lib/ArrayExpress/ArrayMAGE/Database.pm             |    53 +
 lib/ArrayExpress/ArrayMAGE/Feature.pm              |    80 +
 lib/ArrayExpress/ArrayMAGE/FeatureReporterMap.pm   |    88 +
 lib/ArrayExpress/ArrayMAGE/NullObject.pm           |    25 +
 lib/ArrayExpress/ArrayMAGE/ProgressBar.pm          |    40 +
 lib/ArrayExpress/ArrayMAGE/Reporter.pm             |   131 +
 lib/ArrayExpress/ArrayMAGE/ReporterCompositeMap.pm |    75 +
 lib/ArrayExpress/ArrayMAGE/ReporterGroup.pm        |    84 +
 lib/ArrayExpress/ArrayMAGE/Zone.pm                 |    62 +
 lib/ArrayExpress/AutoSubmission/Creator.pm         |   514 +
 lib/ArrayExpress/AutoSubmission/DB.pm              |  3539 ++++
 .../AutoSubmission/DB/Accessionable.pm             |    56 +
 lib/ArrayExpress/AutoSubmission/DB/ArrayDesign.pm  |    59 +
 .../AutoSubmission/DB/ArrayDesignExperiment.pm     |    23 +
 .../AutoSubmission/DB/ArrayDesignOrganism.pm       |    23 +
 lib/ArrayExpress/AutoSubmission/DB/Category.pm     |    33 +
 .../AutoSubmission/DB/CategoryDesign.pm            |    21 +
 .../AutoSubmission/DB/CategoryMaterial.pm          |    22 +
 .../AutoSubmission/DB/CategoryTaxon.pm             |    21 +
 lib/ArrayExpress/AutoSubmission/DB/DataFile.pm     |    23 +
 lib/ArrayExpress/AutoSubmission/DB/DataFormat.pm   |    23 +
 lib/ArrayExpress/AutoSubmission/DB/Design.pm       |    32 +
 .../AutoSubmission/DB/DesignInstance.pm            |    22 +
 lib/ArrayExpress/AutoSubmission/DB/Event.pm        |    49 +
 lib/ArrayExpress/AutoSubmission/DB/Experiment.pm   |   324 +
 .../AutoSubmission/DB/ExperimentFactor.pm          |    23 +
 .../AutoSubmission/DB/ExperimentLoadedData.pm      |    20 +
 lib/ArrayExpress/AutoSubmission/DB/ExperimentQT.pm |    23 +
 lib/ArrayExpress/AutoSubmission/DB/Factor.pm       |    24 +
 lib/ArrayExpress/AutoSubmission/DB/File.pm         |    35 +
 lib/ArrayExpress/AutoSubmission/DB/LoadedData.pm   |    44 +
 lib/ArrayExpress/AutoSubmission/DB/Material.pm     |    32 +
 .../AutoSubmission/DB/MaterialInstance.pm          |    23 +
 lib/ArrayExpress/AutoSubmission/DB/Organism.pm     |    34 +
 .../AutoSubmission/DB/OrganismInstance.pm          |    23 +
 lib/ArrayExpress/AutoSubmission/DB/Permission.pm   |    22 +
 .../AutoSubmission/DB/PermissionRole.pm            |    21 +
 lib/ArrayExpress/AutoSubmission/DB/Platform.pm     |    23 +
 lib/ArrayExpress/AutoSubmission/DB/Protocol.pm     |   115 +
 .../AutoSubmission/DB/QualityMetric.pm             |    29 +
 .../AutoSubmission/DB/QualityMetricInstance.pm     |    23 +
 .../AutoSubmission/DB/QuantitationType.pm          |    24 +
 lib/ArrayExpress/AutoSubmission/DB/Role.pm         |    27 +
 lib/ArrayExpress/AutoSubmission/DB/RoleUser.pm     |    20 +
 lib/ArrayExpress/AutoSubmission/DB/Spreadsheet.pm  |    22 +
 lib/ArrayExpress/AutoSubmission/DB/Taxon.pm        |    28 +
 lib/ArrayExpress/AutoSubmission/DB/User.pm         |    29 +
 lib/ArrayExpress/AutoSubmission/Daemon.pm          |   435 +
 lib/ArrayExpress/AutoSubmission/Daemon/Checker.pm  |   295 +
 lib/ArrayExpress/AutoSubmission/Daemon/Exporter.pm |   219 +
 .../AutoSubmission/Daemon/MAGETABChecker.pm        |    57 +
 .../AutoSubmission/Daemon/MAGETABExporter.pm       |    64 +
 .../AutoSubmission/Daemon/MXChecker.pm             |   271 +
 .../AutoSubmission/Daemon/MXExporter.pm            |   247 +
 .../AutoSubmission/Daemon/T2MChecker.pm            |    32 +
 .../AutoSubmission/Daemon/T2MExporter.pm           |    47 +
 lib/ArrayExpress/AutoSubmission/Spreadsheet.pm     |  1079 ++
 lib/ArrayExpress/AutoSubmission/WebForm.pm         |  1601 ++
 .../AutoSubmission/templates/_annotate.html        |   183 +
 .../AutoSubmission/templates/_display.html         |    48 +
 .../templates/_display_annotation.html             |    28 +
 .../AutoSubmission/templates/_foot.html            |     3 +
 .../AutoSubmission/templates/_head.html            |    62 +
 .../AutoSubmission/templates/choose.html           |    49 +
 .../AutoSubmission/templates/create.html           |    16 +
 .../AutoSubmission/templates/create_user.html      |    29 +
 .../AutoSubmission/templates/delete.html           |    14 +
 .../AutoSubmission/templates/edit.html             |    15 +
 .../templates/email_signup_confirmation.txt        |    16 +
 .../templates/email_subs_confirmation.txt          |    14 +
 .../AutoSubmission/templates/error.html            |    22 +
 .../AutoSubmission/templates/file_upload.html      |    63 +
 .../AutoSubmission/templates/insert.html           |     8 +
 .../AutoSubmission/templates/intro.html            |    65 +
 .../AutoSubmission/templates/login.html            |    65 +
 .../AutoSubmission/templates/logout.html           |    13 +
 .../AutoSubmission/templates/magetab_intro.html    |    42 +
 .../AutoSubmission/templates/magetab_template.html |    37 +
 .../AutoSubmission/templates/magetab_upload.html   |   152 +
 .../AutoSubmission/templates/signup.html           |    82 +
 .../AutoSubmission/templates/submitted.html        |    32 +
 .../templates/subs_notify_curator.txt              |    15 +
 .../AutoSubmission/templates/template.html         |    37 +
 .../AutoSubmission/templates/update.html           |     8 +
 .../AutoSubmission/templates/upload.html           |   148 +
 lib/ArrayExpress/Curator/Common.pm                 |   620 +
 lib/ArrayExpress/Curator/Config.pm                 |   589 +
 lib/ArrayExpress/Curator/Config.yml                |    89 +
 lib/ArrayExpress/Curator/Database.pm               |   672 +
 lib/ArrayExpress/Curator/Entrez_list.pm            |   121 +
 lib/ArrayExpress/Curator/Entrez_list.txt           | 19188 +++++++++++++++++++
 lib/ArrayExpress/Curator/ExperimentChecker.pm      |  2428 +++
 lib/ArrayExpress/Curator/Logger.pm                 |   409 +
 lib/ArrayExpress/Curator/MAGE.pm                   |  2504 +++
 lib/ArrayExpress/Curator/MAGE/Definitions.pm       |  1181 ++
 lib/ArrayExpress/Curator/MIAMExpress.pm            |  2509 +++
 lib/ArrayExpress/Curator/Report.pm                 |   438 +
 lib/ArrayExpress/Curator/Standalone.pm             |   167 +
 lib/ArrayExpress/Curator/Tab2MAGE.pm               |  2602 +++
 lib/ArrayExpress/Curator/Validate.pm               |  1084 ++
 lib/ArrayExpress/Curator/Visualize.pm              |   356 +
 lib/ArrayExpress/Datafile.pm                       |  3685 ++++
 lib/ArrayExpress/Datafile/Affymetrix.pm            |   166 +
 lib/ArrayExpress/Datafile/Affymetrix/CDF.pm        |   136 +
 .../Datafile/Affymetrix/CDF/GDAC_CDF.pm            |   201 +
 .../Datafile/Affymetrix/CDF/XDA_CDF.pm             |   226 +
 lib/ArrayExpress/Datafile/Affymetrix/CDFFactory.pm |   130 +
 lib/ArrayExpress/Datafile/Affymetrix/CEL.pm        |   294 +
 lib/ArrayExpress/Datafile/Affymetrix/CEL/CELv3.pm  |   281 +
 lib/ArrayExpress/Datafile/Affymetrix/CEL/CELv4.pm  |   298 +
 lib/ArrayExpress/Datafile/Affymetrix/CELFactory.pm |   131 +
 lib/ArrayExpress/Datafile/Affymetrix/CHP.pm        |   375 +
 lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv12.pm |   229 +
 lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv13.pm |   208 +
 lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv8.pm  |   115 +
 .../Datafile/Affymetrix/CHP/GDAC_CHP.pm            |   379 +
 .../Datafile/Affymetrix/CHP/XDA_CHP.pm             |   335 +
 lib/ArrayExpress/Datafile/Affymetrix/CHPFactory.pm |   159 +
 .../Datafile/Affymetrix/Calvin/Binary.pm           |   145 +
 lib/ArrayExpress/Datafile/Affymetrix/Calvin/CEL.pm |   125 +
 lib/ArrayExpress/Datafile/Affymetrix/Calvin/CHP.pm |    83 +
 .../Datafile/Affymetrix/Calvin/Component.pm        |   105 +
 .../Datafile/Affymetrix/Calvin/DataColumn.pm       |    68 +
 .../Datafile/Affymetrix/Calvin/DataGroup.pm        |   105 +
 .../Datafile/Affymetrix/Calvin/DataHeader.pm       |    80 +
 .../Datafile/Affymetrix/Calvin/DataSet.pm          |   143 +
 .../Datafile/Affymetrix/Calvin/Facade.pm           |   127 +
 .../Datafile/Affymetrix/Calvin/Generic.pm          |   128 +
 .../Datafile/Affymetrix/Calvin/Parameter.pm        |    19 +
 lib/ArrayExpress/Datafile/Affymetrix/EXP.pm        |   396 +
 lib/ArrayExpress/Datafile/Affymetrix/EXPFactory.pm |   108 +
 lib/ArrayExpress/Datafile/Affymetrix/Parser.pm     |   355 +
 lib/ArrayExpress/Datafile/ArrayDesign.pm           |   215 +
 lib/ArrayExpress/Datafile/Binary.pm                |   189 +
 lib/ArrayExpress/Datafile/DataMatrix.pm            |   762 +
 lib/ArrayExpress/Datafile/Metrics.pm               |   195 +
 lib/ArrayExpress/Datafile/Parser.pm                |  2016 ++
 lib/ArrayExpress/Datafile/QT_list.pm               |   342 +
 lib/ArrayExpress/Datafile/QT_list.txt              |  1087 ++
 lib/ArrayExpress/MAGETAB.pm                        |   911 +
 lib/ArrayExpress/MAGETAB/Checker.pm                |  2034 ++
 lib/ArrayExpress/MAGETAB/Checker/IDF.pm            |    76 +
 lib/ArrayExpress/MAGETAB/DataMatrix.pm             |   548 +
 lib/ArrayExpress/MAGETAB/IDF.pm                    |   881 +
 lib/ArrayExpress/MAGETAB/SDRF.pm                   |  3486 ++++
 lib/ArrayExpress/MAGETAB/TabFile.pm                |   926 +
 lib/ArrayExpress/Tracking/CelQC.pm                 |   171 +
 lib/ArrayExpress/Tracking/Event.pm                 |    27 +
 lib/ArrayExpress/Tracking/QCJobManager.pm          |   180 +
 lib/ArrayExpress/Tracking/QueryHandler.pm          |   769 +
 t/affy_calvin.t                                    |   205 +
 t/affy_cdf.t                                       |    51 +
 t/affy_cel.t                                       |   108 +
 t/affy_chp.t                                       |   118 +
 t/affy_exp.t                                       |   103 +
 t/affy_xda_cdf.t                                   |    26 +
 t/affy_xda_cel.t                                   |   103 +
 t/affy_xda_chp.t                                   |   127 +
 t/array_design.t                                   |    28 +
 t/arraymage.t                                      |    11 +
 t/binary.t                                         |    23 +
 t/common.t                                         |   123 +
 t/config.t                                         |    11 +
 t/creator.t                                        |    20 +
 t/daemon.t                                         |    26 +
 t/daemon_checker.t                                 |    24 +
 t/daemon_exporter.t                                |    24 +
 t/daemon_mxchecker.t                               |    23 +
 t/daemon_mxexporter.t                              |    23 +
 t/data/dummy.CDF                                   |   192 +
 t/data/test.CEL                                    |    49 +
 t/data/test.CHP                                    |   Bin 0 -> 11922 bytes
 t/data/test.EXP                                    |    49 +
 t/data/test.gpr                                    |    37 +
 t/data/test.txt                                    |     8 +
 t/data/test_calvin.CHP                             |   Bin 0 -> 19567 bytes
 t/data/test_fgem.txt                               |    11 +
 t/data/test_xda.CEL                                |   Bin 0 -> 670 bytes
 t/data/test_xda.CHP                                |   Bin 0 -> 1013 bytes
 t/data_metrics.t                                   |    11 +
 t/database.t                                       |    11 +
 t/datafile.t                                       |   467 +
 t/datafile_parser.t                                |    11 +
 t/db.t                                             |    58 +
 t/entrez_list.t                                    |    11 +
 t/experiment_checker.t                             |    11 +
 t/fgem.t                                           |   107 +
 t/logger.t                                         |    11 +
 t/mage.t                                           |    19 +
 t/mage_defs.t                                      |    11 +
 t/magetab.t                                        |    10 +
 t/magetab_checker.t                                |    11 +
 t/magetab_datamatrix.t                             |    10 +
 t/magetab_idf.t                                    |    10 +
 t/magetab_sdrf.t                                   |    10 +
 t/magetab_tabfile.t                                |    10 +
 t/miamexpress.t                                    |    22 +
 t/qt_list.t                                        |    11 +
 t/report.t                                         |    11 +
 t/spreadsheet.t                                    |    21 +
 t/standalone.t                                     |    10 +
 t/tab2mage.t                                       |    40 +
 t/tracking.t                                       |    36 +
 t/validate.t                                       |    19 +
 t/visualize.t                                      |    11 +
 t/web_form.t                                       |    25 +
 util/AE_insert_celfiles.pl                         |   202 +
 util/fillin_null_rows.pl                           |   124 +
 util/join_by_first_column.pl                       |   122 +
 util/mx_legacy_check.pl                            |   155 +
 util/mx_qt_update.pl                               |    94 +
 util/t2m_legacy_check.pl                           |   138 +
 util/t2m_update_docs.pl                            |    93 +
 719 files changed, 118226 insertions(+), 150 deletions(-)

diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..4fc5c8b
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,44 @@
+Copyright (c) 2007 The European Bioinformatics Institute. All rights reserved.
+  
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+  
+   1. Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer. 
+  
+   2. Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in
+      the documentation and/or other materials provided with the
+      distribution.
+  
+   3. The names "Tab2MAGE" and "MAGETabulator" must not be used to
+      endorse or promote products derived from this software without
+      prior written permission. For written permission, please contact
+      arrayexpress at ebi.ac.uk
+  
+   4. Products derived from this software may not be called "Tab2MAGE"
+      or "MAGETabulator", nor may "Tab2MAGE" or "MAGETabulator" appear
+      in their names without prior written permission of the Tab2MAGE
+      developers.
+  
+   5. Redistributions of any form whatsoever must retain the following
+      acknowledgment:
+      "This product includes software developed by ArrayExpress 
+       (http://www.ebi.ac.uk/arrayexpress)"
+  
+   THIS SOFTWARE IS PROVIDED BY THE ARRAYEXPRESS GROUP ``AS IS'' AND ANY
+   EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+   PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE ARRAYEXPRESS GROUP OR
+   ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+   OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The European Bioinformatics Institute may publish revised and/or new 
+versions of this license with new releases of the Tab2MAGE software. 
diff --git a/Changes b/Changes
new file mode 100644
index 0000000..f691a78
--- /dev/null
+++ b/Changes
@@ -0,0 +1,364 @@
+Changelog for Tab2MAGE
+----------------------
+
+2.2.2   Tab2MAGE-20080626.tar.gz        Minor bug fixes for:
+                                          - Reporter Identifier of 0 (zero);
+                                          - DED generation for Affymetrix SNP CHP files;
+                                          - empty headings in Tab2MAGE documents;
+                                          - using cdf2mage.pl to generate CS-only MAGE-ML.
+
+2.2.1   Tab2MAGE-20080604.tar.gz        Various bug fixes.
+
+2.2.0	Tab2MAGE-20080428.tar.gz	Support for MAGE-TAB version 1.1.
+					Improvements to memory handling during experiment checking.
+					Support for Affymetrix Command Console ("Calvin") data
+					  file formats.
+					Updated for use with Rails 2.0.x (tested with 2.0.2).
+					Better reporting of Text::CSV_XS parsing errors.
+					Improved support for files with Mac-style line endings.
+					Added graph visualization to magetab.pl.
+					Bug fixes for MAGE-TAB data matrix parsing, allowing
+					  parentheses in QT and hyb/scan/norm names.
+					Removed obsolete mx_add_affy.pl script.
+
+2.1.0	Tab2MAGE-20080110.tar.gz	Various bug fixes for MAGE-TAB and Tab2MAGE parsing.
+					Bug fixes for automated submissions processing system.
+					Refactoring of Affymetrix parsing code and API.
+					Modified support for BeadStudio data files following
+					  consultation with Illumina.
+					Extended submissions tracking database.
+					Included an extended installer for use in complex
+					  deployment situations.
+
+2.0.0	Tab2MAGE-20071002.tar.gz	Bug fixes for MAGE-TAB parsing and validation.
+					Added Assay Name, Technology Type to SDRF parser.
+					Added documentation for MAGE-TAB document creation and
+					  submission to ArrayExpress.
+					Improvements to submissions tracking system.
+					Support for ImaGene 3.0 data files.
+
+1.9.9   Tab2MAGE-20070629.tar.gz	MAGE-TAB validation support now included as an option
+					  for the expt_check.pl script.
+					Added greater leniency to MAGE-TAB IDF and SDRF parsing,
+					  e.g. allowing extra spaces in SDRF headings/IDF tags.
+					SDRF header parsing now enforces the overall order
+					  for columns:  material -> hyb -> data.
+					Mac linebreaks now supported when using Text::CSV_XS 0.27
+					  or later.
+					Added default attachment point for pooling protocol
+					  (to extract) where this protocol is otherwise
+					  unused (Tab2MAGE).
+					Improved support for GEO Affymetrix tab-delimited data.
+					Now defaults to Affymetrix-style DesignElement identifiers
+					  for all ArrayExpress A-AFFY-* array designs.
+					Initial work on automation of MAGE-TAB submissions
+					  processing, spreadsheet template generation. Added
+					  support for new, combined IDF+SDRF documents.
+					Improved MAGE-ML output when parsing MAGE-TAB with -x option
+					  (Affymetrix data files now coded as native format).
+					Added support for CSIRO Spot data files.
+
+1.9.8.5 Tab2MAGE-20070531.tar.gz	Basic support for ImaGene data files.
+					Improved Tab2MAGE handling of single-channel scans
+					  of two-channel hybs.
+					Support for ID_REF-style data files downloaded from GEO.
+					Ported the autosubmissions system from SQLite to MySQL.
+					Bug fixes to accommodate recent Text::CSV_XS changes.
+
+1.9.8	Tab2MAGE-20070424.tar.gz	Bug fixes for MAGE-TAB SDRF parsing.
+					Added "Term Accession Number", "Protocol REF" prefixes,
+					  ImageFormat detection based on filename extensions,
+					  relaxed "Scan Name" column requirement for SDRF
+					  parsing.
+					Bug fixes for two-color MAGE coding in Tab2MAGE.
+					Improvements to web form submissions template selection,
+					  tracking of MIAMExpress Batch Loader submissions.
+					Automated submissions system log for MIAMExpress
+					  MAGE-ML generation.
+					Added -C, -R options to tab2mage.pl, magetab.pl and
+					  expt_check.pl to allow specification of
+					  non-MIAMExpress Reporter/CompositeSequence
+					  identifier prefixes.
+					Removed obsolete workaround for MIAMExpress EXP file
+					  requirement (see version 1.9.4).
+
+1.9.7	Tab2MAGE-20070323.tar.gz	Initial support for MAGE-TAB documents; a new parser
+					  script, magetab.pl, is included for testing purposes
+					  only at this stage.
+					Best-practice coding of two-color hybridizations in
+					  Tab2MAGE MAGE-ML output.
+					Better support for packaging using the PAR system.
+
+1.9.5	Tab2MAGE-20070427.tar.gz	Minor bug fixes, better support for building under PAR
+					  (backported from 1.9.7).
+
+1.9.4	Tab2MAGE-20070305.tar.gz	Support for multi-hyb Illumina data files (all but
+					  "differential expression" data files).
+					Refactored ArrayExpress::Datafile and Parser APIs,
+					  using Class::Std. New Datafile test suite.
+					Improved handling of "ignored" data file columns,
+					  with improved log reporting.
+					Data matrix files now reported in expt_check.pl graph
+					  generation.
+					Basic support for CHPv8 Affymetrix data files (coded in
+					  native format only; basic header information is
+					  extracted).
+					Bug fixes and memory savings for processing of large
+					  data files.
+					Workarounds for MIAMExpress 2.0.1 EXP file requirement
+					  for Affymetrix submissions.
+
+1.9.3	Tab2MAGE-20070122.tar.gz	Various bug-fixes and interface improvements to
+					  submissions automation system.
+					Added "URI" field for experiment, changed publication
+					  URI field to "publication_URI".
+					Minor bug fixes.
+
+1.9.2	Tab2MAGE-20061221.tar.gz	Updated for compatibility with MIAMExpress 2.0.1 and
+					  batch loader.
+					Enhancements to curation autosubmissions tracking
+					  web pages, including search facility.
+					Support for "NASA-style" Nimblegen data files.
+					Initial support for per-hyb Illumina data files.
+					Improved ScanAlyze, QuantArray and ScanArray support.
+					Fix for GenePix files using arrays with only one
+					  MetaColumn.
+					Added BioSourceDescription column to Tab2MAGE format
+					  for free-text descriptions.
+					Improved AEDW criteria checking.
+					Real-world example spreadsheets added to documentation.
+					Added -c ("cs-only") mode to cdf2mage.pl for use with
+					  very large Affymetrix arrays.
+					Many bug fixes.
+
+1.9.1	Tab2MAGE-20061013.tar.gz	Added logs listing problems rendering submissions
+					  unsuitable for the ArrayExpress Data Warehouse. 
+					Added a basic implementation of MIAME checking for
+					  automatic tagging of incoming submissions; note
+					  that this is not final and will change with the
+					  emerging specification.
+					Improved handling of annotation-only checks (skipping
+					  data file checking).
+					Improvements to autosubmissions curation web interface.
+					Various bug fixes.
+
+1.9.0	Tab2MAGE-20060928.tar.gz	Moved MIAMExpress submissions automation from DBD::CSV
+					  to DBD::Sqlite-based system.
+					Added new automated submissions system for spreadsheet-
+					  based submissions; includes back-end processing daemons,
+					  user-facing submissions web forms with automated
+					  spreadsheet template generation, and a submissions
+					  management curation web interface.
+					Protocol log file for expt_check.pl when run in
+					  MIAMExpress mode.
+					New YAML-based configuration system, with optional
+					  per-user config file option.
+					New checks implemented for data warehouse suitability.
+					Support for CodeLink Expression Analysis data files.
+					Added -p and -d options to scripts to allow redirection
+					  of log file output and data file input directory.
+					Support for Affymetrix 100k Mapping (SNP) CHP files.
+					Many bug fixes.
+
+1.8.5	Tab2MAGE-20060903.tar.gz	Repointed array design download URLs to work with new
+					  ArrayExpress web interface.
+					Added support for ArrayVision "lg2" data files.
+					Other minor bug fixes.
+
+1.8.4	Tab2MAGE-20060729.tar.gz	Native file format encoding now the default for Affy CEL
+					  and NimbleScan files. New -P option to force plain-text
+					  encoding. Old -b option retired.
+					Enabled use of absolute or relative file paths in
+					  spreadsheet.
+					New -x option to skip data file checks for quick validation
+					  of annotation.
+					Modified Spotfinder file parsing to remove R and C columns.
+					Lightweight support for At-MIAMExpress schema (omitting
+  					  sample annotation and factor value reporting).
+					Various bug fixes, QuantitationType update.
+					Added dummy data files to affymetrix example.
+
+1.8.3	Tab2MAGE-20060629.tar.gz	Support for Affymetrix SNP data.
+					Added new lightweight CDF to MAGE-ML script, cdf2mage.pl
+					Improved test suite.
+					Enabled native format encoding for Nimblegen data files.
+					Various bug fixes.
+
+1.8.2	Tab2MAGE-20060412.tar.gz	Memory and CPU-saving changes to data file parsing code.
+					Automated MIAMExpress submissions checking and accession
+					  number assignment system (documentation to follow).
+					Added "-k" option to tab2mage.pl, allowing the user to
+					  keep all unrecognised QTs without having to specify
+					  them separately.
+					Log files backed up before overwriting (one copy only).
+					Raw data files having Reporter/CompSeq identifiers but
+					  no feature coordinates can now be processed.
+					Improved factor value checking for MX submissions; new
+					  sample annotation reports.
+					New lightweight (and very basic) array ADF to MAGE-ML
+ 					  converter script.
+					Fix for ScanArray Express data file parsing bug.
+					Added BlueFuse QuantitationTypes.
+					Initial work on Applied Biosystems and Nimblegen
+					  data file support.
+					Standalone checking modified to check Tab2MAGE-supported
+					  software types, rather than just those supported by
+					  MIAMExpress.
+					Miscellaneous bug fixes.
+
+1.8.1   Tab2MAGE-20060126.tar.gz        Reworked spreadsheet parsing to use Text::CSV_XS, so that
+                                          spreadsheet fields (e.g. protocol text) can now contain
+                                          newlines.
+                                        Release and submission dates now created as ArrayExpress-
+                                          approved NVTs in MAGE-ML, rather than OntologyEntry terms.
+                                        Major code clean-up.
+
+1.8.0	Tab2MAGE-20051215.tar.gz	Native Affymetrix file coding in MAGE-ML using -b option 
+					  with tab2mage.pl and mx_add_affy.pl
+					Protocol accession cache mechanism to allow the automatic 
+					  reassignment of protocol accessions in tab2mage
+					  spreadsheets.
+					Constants.pm setting for central Affy library file
+					  (CDF) directory.
+					Minor documentation update.
+
+1.7.4	Tab2MAGE-20060411.tar.gz	Fix for ScanArray Express data file parsing bug,
+					  backported from 1.8.2.
+
+1.7.3	Tab2MAGE-20051214.tar.gz	Brief all vs. all check on data files to look for
+					  duplicated file contents.
+					Various minor bug fixes.
+					QuantitationTypes update.
+
+1.7.2	Tab2MAGE-20051123.tar.gz	Added investigator role, email and address fields to
+					  Tab2MAGE spreadsheet format.
+					Basic support for Spotfinder, BlueFuse and UCSF Spot files.
+					Experiment checker now reports on MIAMExpress FactorValues
+					  and checks for potentially truncated data files.
+					Included standalone data file conversion script
+					  convert_to_mcmr.pl
+					Bug fixes for final data matrix processing.
+					Fix for bug in checker script where the data file/ADF
+					  has zero as a feature coordinate.
+					Fix for BioMaterialCharacteristics with value of zero.
+
+1.7.1	Tab2MAGE-20051007.tar.gz	Added support for final data matrix files in Tab2MAGE
+					  spreadsheet validation and parsing.
+					Documentation update, including notes on supported data
+					  file formats.
+					Updated QT definitions, minor bug fixes.
+
+1.7	Tab2MAGE-20050906.tar.gz	New -Q option to add QuantitationType information, rather
+					  than replacing the built-in values (-q option).
+					Changed to create more LSID-like identifiers.
+					Basic support for indicating data_coder and curator roles
+					  in experiment section.
+
+1.6.5	Tab2MAGE-20051004.tar.gz	Improved ScanAlyze data file parsing.
+					Relaxed rules on creating FactorValue Measurement_assns.
+					Experiment checker now reports on "Other" values used
+					  in MIAMExpress annotation.
+					Updated for compatibility with MIAMExpress v2 release.
+					Minor QT bug fixes.
+
+1.6.4	Tab2MAGE-20050818.tar.gz	QT parsing bug fixes.
+					Improved support for QuantArray/ScanArray data files.
+					Minor changes in scanning/feature extraction protocol
+					  and software handling.
+					Removed support for use of "m" to indicate meters in
+					  measurements (this clashed with "m" for minutes; we
+					  find that minutes are used far more often than meters).
+
+1.6.3	Tab2MAGE-20050727.tar.gz	Added Descriptions to generated QTs where available.
+					New format QT identifiers incorporating namespaces.
+					Disambiguated quantitation type parsing for column headings
+					  shared between multiple software manufacturers.
+					Improved memory efficiency for CEL parsing subroutines.
+					Optimization of tab-delimited data file parsing.
+
+1.6.2	Tab2MAGE-20050627.tar.gz	Support for QuantArray data files.
+					Updated Entrez/PubMed publication list (Apr 27 2005 list).
+					Initial AIDA quantitation type support.
+					Added checks for large data files (e.g., Affy tiling path array 
+					  CEL files); scripts now skip these to avoid out of memory errors.
+					Switched module dependencies from LWP::Simple to LWP::UserAgent.
+					Scripts now return graded error codes - see Constants.pm 
+					  for details of the error levels. These are intended for
+					  use in automated execution of submission pipelines.
+					New -f option for specifying PNG font.
+					Bug fixes (parameters, factor values).
+
+1.6.1	Tab2MAGE-20050610.tar.gz	Various bug fixes.
+
+1.6	Tab2MAGE-20050513.tar.gz	Array Feature identifiers now retrieved from
+					  ArrayExpress database, where available (optional
+					  stand-alone mode included).
+					Affymetrix parsing fixed to run on big-endian
+					  architectures (e.g. MacOSX).
+					Test suite for Affymetrix parsing included.
+					Major rewrite of core data file parsing module.
+					Initial support for ScanArray data files.
+					Other minor bug fixes.
+
+1.5.1	Tab2MAGE-20050425.tar.gz	Finalized Affymetrix parsing API, documentation.
+					ScanAlyze data file parsing support.
+					Enabled expt_check.pl parsing of all Tab2MAGE-supported
+					  text data file formats.
+
+1.5	Tab2MAGE-20050414.tar.gz	Removed EXP file requirement for Affymetrix data.
+					Added Organization field to Experiment section.
+					Documentation of Affymetrix parsing methods.
+
+1.4.9	Tab2MAGE-20050407.tar.gz	Support for Affymetrix CHP files containing comparison data.
+					Initial release of standalone platform-specific
+					  binary packages.
+
+1.4.8	Tab2MAGE-20050404.tar.gz	Initial spreadsheet validation support in
+					  experiment checker script.
+					QuantitationType information updated.
+					Changed PUBLIC ID in the output MAGE-ML to 
+					  reflect MAGE version.
+					Various bug fixes.
+
+1.4.7	Tab2MAGE-20050314.tar.gz	QT information for GenePix and Agilent updated.
+
+1.4.6	Tab2MAGE-20050311.tar.gz	Better support for Affymetrix-derived normalized 
+					  text data files (e.g. from RMA analysis).
+					Fixed bug in normalized BioAssayDimension assignment.
+
+1.4.5	Tab2MAGE-20050309.tar.gz	Experiment visualization routine added.
+
+1.4.2	Tab2MAGE-20050211.tar.gz	Full documentation including examples.
+					Fixed support for new Affymetrix XDA CHP files.
+					Added parsing of normalized data files with "Reporter Identifier"
+					  column and no other Feature coordinates.
+					Various bug fixes.
+					Cleaned up module interfaces.
+
+1.4	Tab2MAGE-20050117.tar.gz	Support for user-defined parameters.
+					Improved documentation.
+					Now works on MS Windows (see README for details).
+
+1.3.1   Tab2MAGE-20050112.tar.gz        Further documentation.
+                                        Added check for publication details to expt_check.pl.
+
+1.3	Tab2MAGE-20041211.tar.gz	Support for Affymetrix data files. 
+					  Tab2MAGE now exports expression data, QC and parameter 
+					  information from CEL, CHP and EXP files and the library CDF file.
+					Added support for Software objects 
+					  (Scanning, Feature Extraction and Normalization).
+					Enhanced QuantitationType support; 
+					  Tab2MAGE now creates correct QT subclasses.
+					Various bug fixes and other improvements.
+
+1.2	Tab2MAGE-20041028.tar.gz	Fix for Feature identifier bug.
+					Support for image file URIs.
+					Support for bibliographic reference URIs.
+					Initial support for MAGE Software objects.
+					Support for multiple ExperimentDesignTypes
+					 and QualityControlDescriptionTypes.
+
+1.1	Tab2MAGE-20041007.tar.gz	Agilent data file support added.
+					Minor bug fixes.
+
+1.0	Tab2MAGE-20040928.tar.gz	Initial public release.
diff --git a/MAGETAB.txt b/MAGETAB.txt
new file mode 100644
index 0000000..9c40f45
--- /dev/null
+++ b/MAGETAB.txt
@@ -0,0 +1,135 @@
+Implementation notes on ArrayExpress MAGE-TAB $Date:: 2007-09-20 #$
+===================================================================
+
+1. This implementation relies on a recent Perl MAGEstk
+installation. At the moment ArrayExpress uses a release based on the
+subversion repository dated 2006-09-16. This release may be downloaded
+from the following location, pending a more formal release on
+CPAN/mged.sourceforge.net:
+
+http://www.ebi.ac.uk/systems-srv/mp/file-exchange/Bio-MAGE-20060916.tar.gz
+
+2. Some parts of the MAGE-TAB specification have not yet been
+   implemented:
+
+  - ADF parsing has been deferred until a later release.
+
+  - Automatic generation of virtual ArrayDesigns for data matrices
+  which reference database IDs or chromosome coordinates instead of
+  the more usual Reporter or Composite Element names/IDs. Such data
+  matrices are not yet supported.
+
+  - Multiple SDRFs split on any node column.
+
+  - REF:namespaces are currently accepted but discarded by the parser
+  (update: support now added for Array Design and Protocol
+  namespaces).
+
+3. Some parts of the MAGE-TAB specification do not sit well with the
+   MAGE object model:
+
+  - Measurement Characteristics associated with BioMaterials (we have
+  attempted to incorporate these as nested OEs).
+
+  - A single MAGE-TAB Parameter can be associated with many Units, but
+  MAGE ParameterTypes can only have a single Unit association (via
+  DefaultValue); this is handled by creating new MAGE ParameterTypes
+  on the fly whenever a new Unit is encountered, and linking them back
+  to the Protocol.
+
+  - Unit OEs (at the moment these are shoehorned into MAGE Unit classes).
+
+  - Protocol REF / Array Design REF Term Sources. These are ignored at
+  the moment.
+
+  - Protocol Contact (added as a NVT association to Protocol).
+
+  - Arbitrary mapping of any "* Name" column to the DerivedBioAssay
+  represented by a data matrix. MAGE limits this mapping to BioAssays
+  only (i.e., Hybridization, Scan, Normalization).
+
+  - In MAGE, Image must have Format_assn; there is nowhere to specify
+  this in MAGE-TAB at the moment; the parser attempts to determine the
+  image format by its filename extension (e.g., "JPEG" for
+  "filename.jpg"). If this is not possible the format is encoded as
+  "unknown".
+
+4. This MAGE-TAB implementation currently has a few limitations:
+
+  - Since feature extraction does not exist as a separate entity in
+  MAGE-TAB, the corresponding feature extraction protocol has to be
+  included in the scanning (image acquisition) protocol. This
+  limitation could be resolved in a number of ways, but each has a
+  disadvantage:
+
+    - add "Feature Extraction Name" (or some variant thereof) to
+    MAGE-TAB; this slightly complicates the format further, but is
+    probably the cleanest solution for MAGEv1 support. This would also
+    support the use-case where a single image is feature-extracted
+    multiple times using different software (this is currently
+    unsupported by the SDRF).
+
+    - infer the presence of FE from Array Data, and allow FE "Protocol
+    REF" columns to fall between Scan Name and Array Data File
+    columns. This would require reworking the parser protocol code
+    into something more complex and error-prone; side effects would
+    include lack of support for 1..n Scan->FE mapping.
+
+    - infer FE protocol from protocol type. This is also error-prone
+    as it is vulnerable to misspellings or outright omission of
+    protocol type in the IDF.
+
+  - For Affymetrix experiments, EXP files (where available) should be
+  added as a "Comment[EXP]" column. following the "Array Data File"
+  column.
+
+  - Also for Affy experiment, while the CDF file to use for CHP
+  parsing can be derived from the CHP file itself, this is not the
+  case for "Derived Array Data Matrix File". It is possible to parse
+  these files without any CDF information, but the parser needs a hint
+  as to the chip name (e.g. HG-U133A) for use in CompositeSequence
+  identifiers (otherwise the array accession will be used instead). At
+  the moment this hint is given using a "Comment[CDF]" column
+  following the "Derived Array Data Matrix File" column.
+
+  - Currently the parser requires all Parameters to be declared in the
+  IDF alongside their respective Protocols.
+
+5. Other questions and proposed additions to the MAGE-TAB
+   specification (see also the notes above for others):
+
+  - How to handle authority/namespace support; also, how to call out
+  more "global" objects e.g. Database, LabelCompound. Currently these
+  latter default to the ebi.ac.uk namespace, and authority and
+  namespace are hardcoded (although it would be trivial to make this
+  configurable).
+
+  - Should we allow separate annotation file(s) linked from IDF (and
+  which formats do we support)?
+
+  - If supplied in the same file, how do we delimit IDF from SDRF? How
+  about the ADF header and body sections? For combined IDF+SDRF
+  documents these scripts assume that IDF and SDRF sections begin with
+  "[IDF]" and "[SDRF]" respectively.
+
+  - "Term Accession Number" columns may follow "Term Source REF"
+  columns in the SDRF, and should contain the ontology accession
+  number for a given ontology term. No such mechanism is yet supported
+  in the IDF. Neither of these features are in the v1.0 specification.
+
+  - "Experimental Design Term Source REF" is supported as a field in
+  the IDF, although it is not in the v1.0 specification.
+
+  - Lines beginning with a '#' as comments are ignored by the parser;
+  this is not in the v1.0 specification.
+
+  - Comment[] fields currently attached to top-level Experiment only
+  for IDF.
+
+  - Hardware, Software types not captured by MAGE-TAB.
+
+  - PublicationType not captured by MAGE-TAB.
+
+  - How to code the following in MAGE: Protocol Contact; Experiment
+  Date (currently coded as NVTs)?
+
diff --git a/MANIFEST b/MANIFEST
new file mode 100644
index 0000000..864473a
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,706 @@
+MANIFEST
+Makefile.PL
+README
+README-windows.txt
+MAGETAB.txt
+COPYING
+Changes
+adv_inst/README
+adv_inst/deploy.pl
+adv_inst/T2MInstaller.pm
+adv_inst/InstallConfig.yml-example
+bin/tab2mage.pl
+bin/magetab.pl
+bin/expt_check.pl
+bin/t2m_visualize.pl
+bin/parse_affy.pl
+bin/convert_to_mcmr.pl
+automation/mx_autocheck_daemon.pl
+automation/mx_export_daemon.pl
+automation/mx_reset_experiment.pl
+automation/reset_experiment.pl
+automation/autosubs_httpd.pl
+automation/autosubs_checkd.pl
+automation/autosubs_exportd.pl
+automation/magetab_checkd.pl
+automation/magetab_exportd.pl
+automation/tab2mage.cgi
+automation/magetab.cgi
+automation/t2m_insert_sub.pl
+automation/magetab_insert_sub.pl
+automation/database_overseer.pl
+automation/populate_tracking_info.pl
+automation/calculate_datahashes.pl
+automation/calculate_celqc.pl
+automation/cel_qcstats.pl
+arraymage/arraymage.pl
+arraymage/cdf2mage.pl
+util/mx_qt_update.pl
+util/t2m_update_docs.pl
+util/mx_legacy_check.pl
+util/t2m_legacy_check.pl
+util/join_by_first_column.pl
+util/AE_insert_celfiles.pl
+util/fillin_null_rows.pl
+lib/ArrayExpress/ArrayMAGE.pm
+lib/ArrayExpress/ArrayMAGE/ArrayDesign.pm
+lib/ArrayExpress/ArrayMAGE/BioSequence.pm
+lib/ArrayExpress/ArrayMAGE/CompositeGroup.pm
+lib/ArrayExpress/ArrayMAGE/CompositeSequence.pm
+lib/ArrayExpress/ArrayMAGE/Database.pm
+lib/ArrayExpress/ArrayMAGE/Feature.pm
+lib/ArrayExpress/ArrayMAGE/FeatureReporterMap.pm
+lib/ArrayExpress/ArrayMAGE/NullObject.pm
+lib/ArrayExpress/ArrayMAGE/ProgressBar.pm
+lib/ArrayExpress/ArrayMAGE/Reporter.pm
+lib/ArrayExpress/ArrayMAGE/ReporterCompositeMap.pm
+lib/ArrayExpress/ArrayMAGE/ReporterGroup.pm
+lib/ArrayExpress/ArrayMAGE/Zone.pm
+lib/ArrayExpress/Datafile/Affymetrix.pm
+lib/ArrayExpress/Datafile/Affymetrix/CEL.pm
+lib/ArrayExpress/Datafile/Affymetrix/CHP.pm
+lib/ArrayExpress/Datafile/Affymetrix/CDF.pm
+lib/ArrayExpress/Datafile/Affymetrix/EXP.pm
+lib/ArrayExpress/Datafile/Affymetrix/CDF/GDAC_CDF.pm
+lib/ArrayExpress/Datafile/Affymetrix/CDF/XDA_CDF.pm
+lib/ArrayExpress/Datafile/Affymetrix/CDFFactory.pm
+lib/ArrayExpress/Datafile/Affymetrix/CEL/CELv3.pm
+lib/ArrayExpress/Datafile/Affymetrix/CEL/CELv4.pm
+lib/ArrayExpress/Datafile/Affymetrix/CELFactory.pm
+lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv12.pm
+lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv13.pm
+lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv8.pm
+lib/ArrayExpress/Datafile/Affymetrix/CHP/GDAC_CHP.pm
+lib/ArrayExpress/Datafile/Affymetrix/CHP/XDA_CHP.pm
+lib/ArrayExpress/Datafile/Affymetrix/CHPFactory.pm
+lib/ArrayExpress/Datafile/Affymetrix/EXPFactory.pm
+lib/ArrayExpress/Datafile/Affymetrix/Parser.pm
+lib/ArrayExpress/Datafile/Affymetrix/Calvin/Binary.pm
+lib/ArrayExpress/Datafile/Affymetrix/Calvin/CEL.pm
+lib/ArrayExpress/Datafile/Affymetrix/Calvin/CHP.pm
+lib/ArrayExpress/Datafile/Affymetrix/Calvin/Component.pm
+lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataColumn.pm
+lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataGroup.pm
+lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataHeader.pm
+lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataSet.pm
+lib/ArrayExpress/Datafile/Affymetrix/Calvin/Facade.pm
+lib/ArrayExpress/Datafile/Affymetrix/Calvin/Generic.pm
+lib/ArrayExpress/Datafile/Affymetrix/Calvin/Parameter.pm
+lib/ArrayExpress/Datafile/Binary.pm
+lib/ArrayExpress/Datafile/ArrayDesign.pm
+lib/ArrayExpress/Curator/Common.pm
+lib/ArrayExpress/Curator/Config.pm
+lib/ArrayExpress/Datafile.pm
+lib/ArrayExpress/Datafile/Parser.pm
+lib/ArrayExpress/Datafile/Metrics.pm
+lib/ArrayExpress/Curator/Database.pm
+lib/ArrayExpress/Curator/ExperimentChecker.pm
+lib/ArrayExpress/Curator/Logger.pm
+lib/ArrayExpress/Datafile/DataMatrix.pm
+lib/ArrayExpress/Curator/MAGE.pm
+lib/ArrayExpress/Curator/MAGE/Definitions.pm
+lib/ArrayExpress/Curator/MIAMExpress.pm
+lib/ArrayExpress/Curator/Entrez_list.pm
+lib/ArrayExpress/Datafile/QT_list.pm
+lib/ArrayExpress/Curator/Report.pm
+lib/ArrayExpress/Curator/Tab2MAGE.pm
+lib/ArrayExpress/Curator/Validate.pm
+lib/ArrayExpress/Curator/Standalone.pm
+lib/ArrayExpress/Curator/Visualize.pm
+lib/ArrayExpress/Datafile/QT_list.txt
+lib/ArrayExpress/Curator/Entrez_list.txt
+lib/ArrayExpress/Curator/Config.yml
+lib/ArrayExpress/MAGETAB.pm
+lib/ArrayExpress/MAGETAB/TabFile.pm
+lib/ArrayExpress/MAGETAB/IDF.pm
+lib/ArrayExpress/MAGETAB/SDRF.pm
+lib/ArrayExpress/MAGETAB/DataMatrix.pm
+lib/ArrayExpress/MAGETAB/Checker.pm
+lib/ArrayExpress/AutoSubmission/WebForm.pm
+lib/ArrayExpress/AutoSubmission/DB.pm
+lib/ArrayExpress/AutoSubmission/Spreadsheet.pm
+lib/ArrayExpress/AutoSubmission/Daemon.pm
+lib/ArrayExpress/AutoSubmission/Daemon/Checker.pm
+lib/ArrayExpress/AutoSubmission/Daemon/Exporter.pm
+lib/ArrayExpress/AutoSubmission/Daemon/MXChecker.pm
+lib/ArrayExpress/AutoSubmission/Daemon/MXExporter.pm
+lib/ArrayExpress/AutoSubmission/Creator.pm
+lib/ArrayExpress/AutoSubmission/templates/_annotate.html
+lib/ArrayExpress/AutoSubmission/templates/_display.html
+lib/ArrayExpress/AutoSubmission/templates/_display_annotation.html
+lib/ArrayExpress/AutoSubmission/templates/_foot.html
+lib/ArrayExpress/AutoSubmission/templates/_head.html
+lib/ArrayExpress/AutoSubmission/templates/choose.html
+lib/ArrayExpress/AutoSubmission/templates/create.html
+lib/ArrayExpress/AutoSubmission/templates/create_user.html
+lib/ArrayExpress/AutoSubmission/templates/delete.html
+lib/ArrayExpress/AutoSubmission/templates/edit.html
+lib/ArrayExpress/AutoSubmission/templates/insert.html
+lib/ArrayExpress/AutoSubmission/templates/intro.html
+lib/ArrayExpress/AutoSubmission/templates/magetab_intro.html
+lib/ArrayExpress/AutoSubmission/templates/login.html
+lib/ArrayExpress/AutoSubmission/templates/logout.html
+lib/ArrayExpress/AutoSubmission/templates/signup.html
+lib/ArrayExpress/AutoSubmission/templates/update.html
+lib/ArrayExpress/AutoSubmission/templates/upload.html
+lib/ArrayExpress/AutoSubmission/templates/magetab_upload.html
+lib/ArrayExpress/AutoSubmission/templates/error.html
+lib/ArrayExpress/AutoSubmission/templates/file_upload.html
+lib/ArrayExpress/AutoSubmission/templates/submitted.html
+lib/ArrayExpress/AutoSubmission/templates/email_signup_confirmation.txt
+lib/ArrayExpress/AutoSubmission/templates/email_subs_confirmation.txt
+lib/ArrayExpress/AutoSubmission/templates/subs_notify_curator.txt
+lib/ArrayExpress/AutoSubmission/templates/template.html
+lib/ArrayExpress/AutoSubmission/templates/magetab_template.html
+lib/ArrayExpress/AutoSubmission/Daemon/MAGETABChecker.pm
+lib/ArrayExpress/AutoSubmission/Daemon/MAGETABExporter.pm
+lib/ArrayExpress/AutoSubmission/Daemon/T2MChecker.pm
+lib/ArrayExpress/AutoSubmission/Daemon/T2MExporter.pm
+lib/ArrayExpress/AutoSubmission/DB/Accessionable.pm
+lib/ArrayExpress/AutoSubmission/DB/ArrayDesign.pm
+lib/ArrayExpress/AutoSubmission/DB/ArrayDesignExperiment.pm
+lib/ArrayExpress/AutoSubmission/DB/ArrayDesignOrganism.pm
+lib/ArrayExpress/AutoSubmission/DB/Category.pm
+lib/ArrayExpress/AutoSubmission/DB/CategoryDesign.pm
+lib/ArrayExpress/AutoSubmission/DB/CategoryMaterial.pm
+lib/ArrayExpress/AutoSubmission/DB/CategoryTaxon.pm
+lib/ArrayExpress/AutoSubmission/DB/DataFile.pm
+lib/ArrayExpress/AutoSubmission/DB/Design.pm
+lib/ArrayExpress/AutoSubmission/DB/DesignInstance.pm
+lib/ArrayExpress/AutoSubmission/DB/Event.pm
+lib/ArrayExpress/AutoSubmission/DB/Experiment.pm
+lib/ArrayExpress/AutoSubmission/DB/ExperimentFactor.pm
+lib/ArrayExpress/AutoSubmission/DB/ExperimentQT.pm
+lib/ArrayExpress/AutoSubmission/DB/Factor.pm
+lib/ArrayExpress/AutoSubmission/DB/File.pm
+lib/ArrayExpress/AutoSubmission/DB/Material.pm
+lib/ArrayExpress/AutoSubmission/DB/MaterialInstance.pm
+lib/ArrayExpress/AutoSubmission/DB/Organism.pm
+lib/ArrayExpress/AutoSubmission/DB/OrganismInstance.pm
+lib/ArrayExpress/AutoSubmission/DB/Permission.pm
+lib/ArrayExpress/AutoSubmission/DB/PermissionRole.pm
+lib/ArrayExpress/AutoSubmission/DB/Protocol.pm
+lib/ArrayExpress/AutoSubmission/DB/QuantitationType.pm
+lib/ArrayExpress/AutoSubmission/DB/Role.pm
+lib/ArrayExpress/AutoSubmission/DB/RoleUser.pm
+lib/ArrayExpress/AutoSubmission/DB/Spreadsheet.pm
+lib/ArrayExpress/AutoSubmission/DB/Taxon.pm
+lib/ArrayExpress/AutoSubmission/DB/User.pm
+lib/ArrayExpress/AutoSubmission/DB/DataFormat.pm
+lib/ArrayExpress/AutoSubmission/DB/ExperimentLoadedData.pm
+lib/ArrayExpress/AutoSubmission/DB/LoadedData.pm
+lib/ArrayExpress/AutoSubmission/DB/QualityMetric.pm
+lib/ArrayExpress/AutoSubmission/DB/QualityMetricInstance.pm
+lib/ArrayExpress/AutoSubmission/DB/Platform.pm
+lib/ArrayExpress/MAGETAB/Checker/IDF.pm
+lib/ArrayExpress/Tracking/Event.pm
+lib/ArrayExpress/Tracking/QueryHandler.pm
+lib/ArrayExpress/Tracking/CelQC.pm
+lib/ArrayExpress/Tracking/QCJobManager.pm
+examples/other_QTs.txt
+examples/two_color/two_color.txt
+examples/two_color/two_color.png
+examples/two_color/Data1.txt
+examples/two_color/Data2.txt
+examples/affymetrix/affymetrix.txt
+examples/affymetrix/affymetrix.png
+examples/affymetrix/Data1.EXP
+examples/affymetrix/Data2.EXP
+examples/affymetrix/Data3.EXP
+examples/affymetrix/Data4.EXP
+examples/affymetrix/Data1.CEL
+examples/affymetrix/Data2.CEL
+examples/affymetrix/Data3.CEL
+examples/affymetrix/Data4.CEL
+examples/affymetrix/Data1.CHP
+examples/affymetrix/Data2.CHP
+examples/affymetrix/Data3.CHP
+examples/affymetrix/Data4.CHP
+examples/affymetrix/YG_S98.cdf
+examples/chip_chip/chip_chip.txt
+examples/chip_chip/chip_chip.png
+examples/chip_chip/Data1.txt
+examples/chip_chip/Data2.txt
+examples/normalized_data/normalized_data.txt
+examples/normalized_data/normalized_data.png
+examples/normalized_data/Data1.txt
+examples/normalized_data/Data2.txt
+examples/normalized_data/NormData1.txt
+examples/normalized_data/NormData2.txt
+examples/final_data_matrix/final_data_matrix.txt
+examples/final_data_matrix/final_data_matrix.png
+examples/final_data_matrix/E-EXML-7.xml.dot.jpg
+examples/final_data_matrix/Data1.txt
+examples/final_data_matrix/Data2.txt
+examples/final_data_matrix/Data3.txt
+examples/final_data_matrix/Data4.txt
+examples/final_data_matrix/NormData1.txt
+examples/final_data_matrix/NormData2.txt
+examples/final_data_matrix/NormData3.txt
+examples/final_data_matrix/NormData4.txt
+examples/final_data_matrix/FGEM.txt
+examples/reference/reference.txt
+examples/reference/reference.png
+examples/reference/Data1.txt
+examples/reference/Data2.txt
+examples/reference/Data3.txt
+examples/parameters/parameters.txt
+examples/parameters/parameters.png
+examples/parameters/Data1.txt
+examples/parameters/Data2.txt
+examples/loop_design/loop_design.txt
+examples/loop_design/loop_design.png
+examples/illumina/illumina.txt
+examples/illumina/illumina.png
+examples/illumina/ProbeData.txt
+examples/illumina/GeneGroupedData.txt
+examples/real/E-TABM-102.png
+examples/real/E-TABM-102.txt
+examples/real/E-TABM-134.png
+examples/real/E-TABM-134.txt
+examples/real/E-TABM-136.png
+examples/real/E-TABM-136.txt
+examples/real/E-TABM-140.png
+examples/real/E-TABM-140.txt
+examples/real/E-TABM-16.png
+examples/real/E-TABM-16.txt
+examples/real/E-TABM-163.png
+examples/real/E-TABM-163.txt
+examples/real/E-TABM-18.png
+examples/real/E-TABM-18.txt
+examples/real/E-TABM-22.png
+examples/real/E-TABM-22.txt
+examples/real/E-TABM-33.png
+examples/real/E-TABM-33.txt
+examples/real/E-TABM-35.png
+examples/real/E-TABM-35.txt
+examples/real/E-TABM-54.png
+examples/real/E-TABM-54.txt
+examples/real/E-TABM-66.png
+examples/real/E-TABM-66.txt
+examples/real/E-TABM-70.png
+examples/real/E-TABM-70.txt
+examples/real/E-MEXP-880.png
+examples/real/E-MEXP-880.txt
+examples/magetab/affymetrix/affymetrix.idf
+examples/magetab/affymetrix/affy_sdrf.txt
+examples/magetab/affymetrix/affy_dm.txt
+examples/magetab/affymetrix/ATH1-121501.cdf
+examples/magetab/affymetrix/Data1.CEL
+examples/magetab/affymetrix/Data1.CHP
+examples/magetab/affymetrix/Data1.EXP
+examples/magetab/affymetrix/Data2.CEL
+examples/magetab/affymetrix/Data2.CHP
+examples/magetab/affymetrix/Data2.EXP
+examples/magetab/affymetrix/Data3.CEL
+examples/magetab/affymetrix/Data3.CHP
+examples/magetab/affymetrix/Data3.EXP
+examples/magetab/affymetrix/Data4.CEL
+examples/magetab/affymetrix/Data4.CHP
+examples/magetab/affymetrix/Data4.EXP
+examples/magetab/affymetrix/YG_S98.cdf
+examples/magetab/two_color/test.idf
+examples/magetab/two_color/test_sdrf.txt
+examples/magetab/two_color/Data1.txt
+examples/magetab/two_color/Data2.txt
+examples/magetab/two_color/Data3.txt
+examples/magetab/two_color/Data4.txt
+examples/magetab/two_color/NormData1.txt
+examples/magetab/two_color/NormData2.txt
+examples/magetab/two_color/NormData3.txt
+examples/magetab/two_color/NormData4.txt
+examples/magetab/illumina/GeneGroupedData.txt
+examples/magetab/illumina/illumina.idf
+examples/magetab/illumina/illumina_sdrf.txt
+examples/magetab/illumina/ProbeData.txt
+examples/magetab/real/E-TABM-102.idf
+examples/magetab/real/E-TABM-102_sdrf.txt
+examples/magetab/real/E-TABM-134.idf
+examples/magetab/real/E-TABM-134_sdrf.txt
+examples/magetab/real/E-TABM-136.idf
+examples/magetab/real/E-TABM-136_sdrf.txt
+examples/magetab/real/E-TABM-140.idf
+examples/magetab/real/E-TABM-140_sdrf.txt
+examples/magetab/real/E-TABM-16.idf
+examples/magetab/real/E-TABM-163.idf
+examples/magetab/real/E-TABM-163_sdrf.txt
+examples/magetab/real/E-TABM-16_sdrf.txt
+examples/magetab/real/E-TABM-18.idf
+examples/magetab/real/E-TABM-18_sdrf.txt
+examples/magetab/real/E-TABM-22.idf
+examples/magetab/real/E-TABM-22_sdrf.txt
+examples/magetab/real/E-TABM-33.idf
+examples/magetab/real/E-TABM-33_sdrf.txt
+examples/magetab/real/E-TABM-35.idf
+examples/magetab/real/E-TABM-35_sdrf.txt
+examples/magetab/real/E-TABM-54.idf
+examples/magetab/real/E-TABM-54_sdrf.txt
+examples/magetab/real/E-TABM-66.idf
+examples/magetab/real/E-TABM-66_sdrf.txt
+examples/magetab/real/E-TABM-70.idf
+examples/magetab/real/E-TABM-70_sdrf.txt
+examples/magetab/real/E-MEXP-880.idf
+examples/magetab/real/E-MEXP-880_sdrf.txt
+examples/magetab/real/solexa_example.idf
+examples/magetab/real/solexa_example.sdrf.txt
+index.html
+docs/expt_check.html
+docs/index.html
+docs/tab2mage.html
+docs/magetab.html
+docs/usage.html
+docs/spreadsheet.html
+docs/detail.html
+docs/datafiles.html
+docs/aesubs.html
+docs/magetab_subs.html
+docs/magetab_docs.html
+docs/magetab_vs_tab2mage.html
+docs/idf.html
+docs/sdrf.html
+docs/t2m_visualize.html
+docs/parse_affy.html
+docs/modules.html
+docs/ArrayExpress/Datafile/QT_list.html
+docs/ArrayExpress/Curator/MIAMExpress.html
+docs/ArrayExpress/Curator/Config.html
+docs/ArrayExpress/Datafile.html
+docs/ArrayExpress/Datafile/Parser.html
+docs/ArrayExpress/Curator/ExperimentChecker.html
+docs/ArrayExpress/Curator/Validate.html
+docs/ArrayExpress/Curator/Standalone.html
+docs/ArrayExpress/AutoSubmission/DB.html
+docs/ArrayExpress/AutoSubmission/Creator.html
+docs/ArrayExpress/AutoSubmission/Spreadsheet.html
+docs/ArrayExpress/AutoSubmission/WebForm.html
+docs/ArrayExpress/Curator/Database.html
+docs/ArrayExpress/Curator/Logger.html
+docs/ArrayExpress/MAGETAB.html
+docs/ArrayExpress/MAGETAB/Checker.html
+docs/ArrayExpress/Curator/MAGE/Definitions.html
+docs/ArrayExpress/Datafile/Affymetrix.html
+docs/ArrayExpress/Datafile/Affymetrix/CEL.html
+docs/ArrayExpress/Datafile/Affymetrix/CHP.html
+docs/ArrayExpress/Datafile/Affymetrix/CDF.html
+docs/ArrayExpress/Datafile/Affymetrix/EXP.html
+docs/ArrayExpress/Datafile/Affymetrix/CDF/GDAC_CDF.html
+docs/ArrayExpress/Datafile/Affymetrix/CDF/XDA_CDF.html
+docs/ArrayExpress/Datafile/Affymetrix/CDFFactory.html
+docs/ArrayExpress/Datafile/Affymetrix/CEL/CELv3.html
+docs/ArrayExpress/Datafile/Affymetrix/CEL/CELv4.html
+docs/ArrayExpress/Datafile/Affymetrix/CELFactory.html
+docs/ArrayExpress/Datafile/Affymetrix/CHP/CHPv12.html
+docs/ArrayExpress/Datafile/Affymetrix/CHP/CHPv13.html
+docs/ArrayExpress/Datafile/Affymetrix/CHP/CHPv8.html
+docs/ArrayExpress/Datafile/Affymetrix/CHP/GDAC_CHP.html
+docs/ArrayExpress/Datafile/Affymetrix/CHP/XDA_CHP.html
+docs/ArrayExpress/Datafile/Affymetrix/CHPFactory.html
+docs/ArrayExpress/Datafile/Affymetrix/EXPFactory.html
+docs/ArrayExpress/Datafile/Affymetrix/Parser.html
+docs/ArrayExpress/ArrayMAGE.html
+docs/ArrayExpress/Curator/Common.html
+docs/ArrayExpress/Curator/Entrez_list.html
+docs/ArrayExpress/Curator/Report.html
+docs/ArrayExpress/Curator/Visualize.html
+docs/ArrayExpress/Datafile/ArrayDesign.html
+docs/style.css
+docs/submit.css
+docs/sdrf_layout.png
+docs/T2M_logo.png
+docs/T2M_logo_small.png
+docs/MAGETAB_logo.png
+docs/MAGETAB_logo_small.png
+docs/aelogo.png
+t/db.t
+t/spreadsheet.t
+t/daemon.t
+t/daemon_checker.t
+t/daemon_exporter.t
+t/daemon_mxchecker.t
+t/daemon_mxexporter.t
+t/creator.t
+t/web_form.t
+t/affy_cel.t
+t/affy_chp.t
+t/affy_exp.t
+t/affy_xda_cel.t
+t/affy_xda_chp.t
+t/affy_cdf.t
+t/affy_xda_cdf.t
+t/affy_calvin.t
+t/array_design.t
+t/arraymage.t
+t/binary.t
+t/common.t
+t/config.t
+t/database.t
+t/datafile_parser.t
+t/datafile.t
+t/data_metrics.t
+t/entrez_list.t
+t/experiment_checker.t
+t/fgem.t
+t/logger.t
+t/mage_defs.t
+t/mage.t
+t/magetab.t
+t/magetab_tabfile.t
+t/magetab_sdrf.t
+t/magetab_idf.t
+t/magetab_datamatrix.t
+t/magetab_checker.t
+t/miamexpress.t
+t/qt_list.t
+t/report.t
+t/tab2mage.t
+t/tracking.t
+t/validate.t
+t/standalone.t
+t/visualize.t
+t/data/dummy.CDF
+t/data/test.CEL
+t/data/test.CHP
+t/data/test.EXP
+t/data/test_xda.CEL
+t/data/test_xda.CHP
+t/data/test_calvin.CHP
+t/data/test.txt
+t/data/test_fgem.txt
+t/data/test.gpr
+META.yml                                 Module meta-data (added by MakeMaker)
+automation/admin/db/migration/migrate_csv2sqlite.pl
+automation/admin/db/migration/migrate_sqlite2mysql.pl
+automation/admin/db/migration/migrate_2.0_to_2.1.sql
+automation/admin/db/migration/migrate_2.1_to_2.2.sql
+automation/admin/app/controllers/account_controller.rb
+automation/admin/app/controllers/application.rb
+automation/admin/app/controllers/array_designs_controller.rb
+automation/admin/app/controllers/category_controller.rb
+automation/admin/app/controllers/design_controller.rb
+automation/admin/app/controllers/material_controller.rb
+automation/admin/app/controllers/organism_controller.rb
+automation/admin/app/controllers/protocols_controller.rb
+automation/admin/app/controllers/experiments_controller.rb
+automation/admin/app/controllers/taxon_controller.rb
+automation/admin/app/controllers/miamexps_controller.rb
+automation/admin/app/controllers/tab2mages_controller.rb
+automation/admin/app/controllers/users_controller.rb
+automation/admin/app/helpers/account_helper.rb
+automation/admin/app/helpers/application_helper.rb
+automation/admin/app/helpers/array_designs_helper.rb
+automation/admin/app/helpers/category_helper.rb
+automation/admin/app/helpers/design_helper.rb
+automation/admin/app/helpers/material_helper.rb
+automation/admin/app/helpers/organism_helper.rb
+automation/admin/app/helpers/protocols_helper.rb
+automation/admin/app/helpers/experiments_helper.rb
+automation/admin/app/helpers/taxon_helper.rb
+automation/admin/app/helpers/miamexps_helper.rb
+automation/admin/app/helpers/tab2mages_helper.rb
+automation/admin/app/helpers/users_helper.rb
+automation/admin/app/models/array_design.rb
+automation/admin/app/models/category.rb
+automation/admin/app/models/design.rb
+automation/admin/app/models/design_instance.rb
+automation/admin/app/models/material.rb
+automation/admin/app/models/material_instance.rb
+automation/admin/app/models/organism.rb
+automation/admin/app/models/organism_instance.rb
+automation/admin/app/models/permission.rb
+automation/admin/app/models/protocol.rb
+automation/admin/app/models/role.rb
+automation/admin/app/models/experiment.rb
+automation/admin/app/models/taxon.rb
+automation/admin/app/models/user.rb
+automation/admin/app/models/data_file.rb
+automation/admin/app/models/spreadsheet.rb
+automation/admin/app/views/account/login.rhtml
+automation/admin/app/views/account/logout.rhtml
+automation/admin/app/views/account/signup.rhtml
+automation/admin/app/views/account/welcome.rhtml
+automation/admin/app/views/array_designs/_form.rhtml
+automation/admin/app/views/array_designs/_title.rhtml
+automation/admin/app/views/array_designs/_aedwinfo.rhtml
+automation/admin/app/views/array_designs/_miameinfo.rhtml
+automation/admin/app/views/array_designs/edit.rhtml
+automation/admin/app/views/array_designs/list.rhtml
+automation/admin/app/views/array_designs/new.rhtml
+automation/admin/app/views/array_designs/show.rhtml
+automation/admin/app/views/category/_form.rhtml
+automation/admin/app/views/category/_title.rhtml
+automation/admin/app/views/category/edit.rhtml
+automation/admin/app/views/category/list.rhtml
+automation/admin/app/views/category/new.rhtml
+automation/admin/app/views/design/_form.rhtml
+automation/admin/app/views/design/_title.rhtml
+automation/admin/app/views/design/edit.rhtml
+automation/admin/app/views/design/list.rhtml
+automation/admin/app/views/design/new.rhtml
+automation/admin/app/views/layouts/admin.rhtml
+automation/admin/app/views/material/_form.rhtml
+automation/admin/app/views/material/_title.rhtml
+automation/admin/app/views/material/edit.rhtml
+automation/admin/app/views/material/list.rhtml
+automation/admin/app/views/material/new.rhtml
+automation/admin/app/views/organism/_form.rhtml
+automation/admin/app/views/organism/_title.rhtml
+automation/admin/app/views/organism/edit.rhtml
+automation/admin/app/views/organism/list.rhtml
+automation/admin/app/views/organism/new.rhtml
+automation/admin/app/views/protocols/_form.rhtml
+automation/admin/app/views/protocols/_title.rhtml
+automation/admin/app/views/protocols/edit.rhtml
+automation/admin/app/views/protocols/list.rhtml
+automation/admin/app/views/protocols/new.rhtml
+automation/admin/app/views/protocols/show.rhtml
+automation/admin/app/views/experiments/_form.rhtml
+automation/admin/app/views/experiments/_annotate.rhtml
+automation/admin/app/views/experiments/_title.rhtml
+automation/admin/app/views/experiments/edit.rhtml
+automation/admin/app/views/experiments/list.rhtml
+automation/admin/app/views/experiments/new.rhtml
+automation/admin/app/views/experiments/today_list.rhtml
+automation/admin/app/views/taxon/_form.rhtml
+automation/admin/app/views/taxon/_title.rhtml
+automation/admin/app/views/taxon/edit.rhtml
+automation/admin/app/views/taxon/list.rhtml
+automation/admin/app/views/taxon/new.rhtml
+automation/admin/app/views/taxon/category_table.rhtml
+automation/admin/app/views/miamexps/_form.rhtml
+automation/admin/app/views/miamexps/_title.rhtml
+automation/admin/app/views/miamexps/edit.rhtml
+automation/admin/app/views/miamexps/list.rhtml
+automation/admin/app/views/miamexps/_aedwinfo.rhtml
+automation/admin/app/views/miamexps/_miameinfo.rhtml
+automation/admin/app/views/tab2mages/_form.rhtml
+automation/admin/app/views/tab2mages/_annotate.rhtml
+automation/admin/app/views/tab2mages/_title.rhtml
+automation/admin/app/views/tab2mages/edit.rhtml
+automation/admin/app/views/tab2mages/list.rhtml
+automation/admin/app/views/tab2mages/_aedwinfo.rhtml
+automation/admin/app/views/tab2mages/_miameinfo.rhtml
+automation/admin/app/views/tab2mages/new.rhtml
+automation/admin/app/views/users/_form.rhtml
+automation/admin/app/views/users/_title.rhtml
+automation/admin/app/views/users/edit.rhtml
+automation/admin/app/views/users/list.rhtml
+automation/admin/app/views/users/new.rhtml
+automation/admin/app/views/users/show.rhtml
+automation/admin/app/controllers/event_controller.rb
+automation/admin/app/controllers/factor_controller.rb
+automation/admin/app/controllers/quantitation_type_controller.rb
+automation/admin/app/helpers/event_helper.rb
+automation/admin/app/helpers/factor_helper.rb
+automation/admin/app/helpers/quantitation_type_helper.rb
+automation/admin/app/models/event.rb
+automation/admin/app/models/factor.rb
+automation/admin/app/models/quantitation_type.rb
+automation/admin/app/views/array_designs/list_all.rhtml
+automation/admin/app/views/event/_form.rhtml
+automation/admin/app/views/event/_title.rhtml
+automation/admin/app/views/event/edit.rhtml
+automation/admin/app/views/experiments/show.rhtml
+automation/admin/CHANGELOG
+automation/admin/config/README
+automation/admin/config/boot.rb
+automation/admin/config/database.yml-sqlite
+automation/admin/config/database.yml-mysql
+automation/admin/config/environment.rb
+automation/admin/config/environments/development.rb
+automation/admin/config/environments/production.rb
+automation/admin/config/environments/test.rb
+automation/admin/config/routes.rb
+automation/admin/config/lighttpd.conf
+automation/admin/doc/README_FOR_APP
+automation/admin/db/database-mysql.dump
+automation/admin/db/database-mysql.schema
+automation/admin/db/schema.pdf
+automation/admin/db/schema.rb
+automation/admin/INSTALL
+automation/admin/lib/acl_system.rb
+automation/admin/lib/annotation.rb
+automation/admin/lib/login_system.rb
+automation/admin/log/server.log
+automation/admin/public/.htaccess
+automation/admin/public/404.html
+automation/admin/public/500.html
+automation/admin/public/dispatch.cgi
+automation/admin/public/dispatch.fcgi
+automation/admin/public/dispatch.rb
+automation/admin/public/favicon.ico
+automation/admin/public/images/rails.png
+automation/admin/public/index.html
+automation/admin/public/javascripts/controls.js
+automation/admin/public/javascripts/dragdrop.js
+automation/admin/public/javascripts/effects.js
+automation/admin/public/javascripts/prototype.js
+automation/admin/public/robots.txt
+automation/admin/public/stylesheets/admin.css
+automation/admin/Rakefile
+automation/admin/README
+automation/admin/README_LOGIN
+automation/admin/script/about
+automation/admin/script/benchmarker
+automation/admin/script/breakpointer
+automation/admin/script/console
+automation/admin/script/destroy
+automation/admin/script/generate
+automation/admin/script/performance/benchmarker
+automation/admin/script/performance/profiler
+automation/admin/script/plugin
+automation/admin/script/process/reaper
+automation/admin/script/process/spawner
+automation/admin/script/process/spinner
+automation/admin/script/profiler
+automation/admin/script/runner
+automation/admin/script/server
+automation/admin/test/fixtures/array_designs.yml
+automation/admin/test/fixtures/categories.yml
+automation/admin/test/fixtures/designs.yml
+automation/admin/test/fixtures/materials.yml
+automation/admin/test/fixtures/organisms.yml
+automation/admin/test/fixtures/permissions.yml
+automation/admin/test/fixtures/permissions_roles.yml
+automation/admin/test/fixtures/protocols.yml
+automation/admin/test/fixtures/roles.yml
+automation/admin/test/fixtures/roles_users.yml
+automation/admin/test/fixtures/experiments.yml
+automation/admin/test/fixtures/taxons.yml
+automation/admin/test/fixtures/users.yml
+automation/admin/test/functional/account_controller_test.rb
+automation/admin/test/functional/array_designs_controller_test.rb
+automation/admin/test/functional/category_controller_test.rb
+automation/admin/test/functional/design_controller_test.rb
+automation/admin/test/functional/material_controller_test.rb
+automation/admin/test/functional/organism_controller_test.rb
+automation/admin/test/functional/protocols_controller_test.rb
+automation/admin/test/functional/experiments_controller_test.rb
+automation/admin/test/functional/taxon_controller_test.rb
+automation/admin/test/test_helper.rb
+automation/admin/test/unit/array_design_test.rb
+automation/admin/test/unit/category_test.rb
+automation/admin/test/unit/design_test.rb
+automation/admin/test/unit/material_test.rb
+automation/admin/test/unit/organism_test.rb
+automation/admin/test/unit/permission_test.rb
+automation/admin/test/unit/protocol_test.rb
+automation/admin/test/unit/role_test.rb
+automation/admin/test/unit/experiment_test.rb
+automation/admin/test/unit/taxon_test.rb
+automation/admin/test/unit/user_test.rb
+automation/admin/test/fixtures/data_files.yml
+automation/admin/test/fixtures/spreadsheets.yml
+automation/admin/test/functional/miamexps_controller_test.rb
+automation/admin/test/functional/tab2mages_controller_test.rb
+automation/admin/test/functional/users_controller_test.rb
+automation/admin/test/unit/data_file_test.rb
+automation/admin/test/unit/spreadsheet_test.rb
+automation/admin/test/fixtures/events.yml
+automation/admin/test/fixtures/factors.yml
+automation/admin/test/fixtures/quantitation_types.yml
+automation/admin/test/functional/event_controller_test.rb
+automation/admin/test/functional/factor_controller_test.rb
+automation/admin/test/functional/quantitation_type_controller_test.rb
+automation/admin/test/unit/event_test.rb
+automation/admin/test/unit/factor_test.rb
+automation/admin/test/unit/quantitation_type_test.rb
diff --git a/META.yml b/META.yml
new file mode 100644
index 0000000..bf13f9b
--- /dev/null
+++ b/META.yml
@@ -0,0 +1,25 @@
+# http://module-build.sourceforge.net/META-spec.html
+#XXXXXXX This is a prototype!!!  It will change in the future!!! XXXXX#
+name:         Tab2MAGE
+version:      20080626
+version_from: 
+installdirs:  site
+requires:
+    Bio::MAGE:                     20030502.3
+    Bio::MAGE::XML::Writer:        20060815
+    Class::Std:                    0.0.8
+    Config::YAML:                  1.42
+    DBD::mysql:                    2.1017
+    DBI:                           1.48
+    Digest::MD5:                   2.33
+    IO::File:                      1.10
+    List::MoreUtils:               0.17
+    LWP::UserAgent:                2.031
+    Parse::RecDescent:             1.94
+    Readonly:                      1.03
+    Storable:                      1.014
+    Text::CSV_XS:                  0.28
+    Tie::IxHash:                   1.21
+
+distribution_type: module
+generated_by: ExtUtils::MakeMaker version 6.30
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644
index 0000000..79962e2
--- /dev/null
+++ b/Makefile.PL
@@ -0,0 +1,163 @@
+#!/usr/bin/perl
+#
+# Makefile.PL for use with MakeMaker-style module installation
+#
+# $Id: Makefile.PL 1619 2007-06-18 22:57:59Z tfrayner $
+#
+#
+# Notes on building under PAR:
+#
+# This *can* be made to work, at least on Mac OS X, using something
+# similar to the following:
+#
+#  PERL5LIB=./lib pp -o expt_check -c -l libxerces-c.26.dylib -A par_ext_files bin/expt_check.pl
+#
+# Note that the libxerces library file must be named exactly right; if
+# not you will get errors listing the correct name to use when the
+# pp'd executable is run. This has been tested using PAR 0.973 and
+# perl 5.8.8 on Mac OS X 10.4.8 (Intel), with the libxerces file in
+# the Tab2MAGE top-level directory. Note also that the par_ext_files
+# file is not distributed as part of the package and must be retrieved
+# from the svn repository.
+
+use strict;
+use warnings;
+use ExtUtils::MakeMaker;
+
+my ( $d, $m, $y ) = ( localtime(time) )[ 3 .. 5 ];
+
+$y += 1900;
+$m = sprintf( "%02d", ++$m );
+$d = sprintf( "%02d", $d );
+
+###########
+# GLOBALS #
+###########
+
+my $VERSION = "$y$m$d";
+my @SCRIPTS = qw(
+    bin/tab2mage.pl
+    bin/magetab.pl
+    bin/expt_check.pl
+    bin/convert_to_mcmr.pl
+);
+my @LIBS    = qw(xerces-c);
+
+########
+# SUBS #
+########
+
+sub MY::postamble
+{    # Makefile target to create standalone binaries using PAR/pp
+
+    my $suffix    = '';
+    my $installer = 'INSTALL';
+    if ( $^O =~ m/^mswin/i )
+    {    # Win2000 ActiveState Perl 5.8.6 registers as "MSWin32"
+        $suffix = '.exe';
+        $installer .= '.bat';
+    }
+    else {
+        $installer .= '.sh';    # Unix assumed to have Bourne-like shell
+    }
+
+    my %binaries = map {
+        my ($bin) = ( $_ =~ m!^bin/(.*)\.p[lmh]$!i );
+        $_ => "$bin$suffix";
+    } @SCRIPTS;
+
+    my $libraries = '';
+    foreach my $lib (@LIBS) { $libraries .= "-l $lib "; }
+
+    my @target;
+
+    push( @target, "\nstandalone: " );
+    push( @target, join( " \\\n\t", @SCRIPTS ), "\n\t" );
+    push( @target,
+        join( " \n\t", map {"pp -o $binaries{$_} $libraries -c -A par_ext_files $_"} @SCRIPTS ),
+        "\n" );
+
+    push( @target, "\nstandalone-clean: \n\t" );
+    push( @target,
+        join( " \n\t", map { '$(RM_F)' . " $_" } values %binaries ), "\n" );
+
+    push( @target, "\nstandalone-distdir: standalone\n\t" );
+
+    push(
+        @target,
+        (   '$(RM_RF) $(DISTVNAME)-' . $^O,
+            "\n\t",
+            '$(PERLRUN) "-MExtUtils::Manifest=manicopy,maniread" \\',
+            "\n\t",
+            '-e "manicopy(maniread(),\'$(DISTVNAME)-' . $^O
+                . '\', \'$(DIST_CP)\');"',
+            "\n\t",
+        )
+    );
+
+    push(
+        @target,
+        join( " \n\t",
+            map { '$(CP)' . " $_ " . '$(DISTVNAME)-' . $^O }
+                values %binaries ),
+        "\n"
+    );
+    push( @target,
+        "\t" . '$(CP)' . " $installer " . '$(DISTVNAME)-' . $^O, "\n" );
+
+    push( @target, "\nstandalone-dist: standalone-distdir \n\t" );
+    push(
+        @target,
+        join( " \n\t",
+            '$(PREOP)',
+            '$(ZIP) $(ZIPFLAGS) $(DISTVNAME)-' . $^O
+                . '.zip $(DISTVNAME)-'
+                . $^O,
+            '$(RM_RF) $(DISTVNAME)-' . $^O,
+            '$(POSTOP)',
+        ),
+        "\n"
+    );
+
+    push( @target, "\nmy-all: all standalone\n" );
+
+    push( @target, "\nmy-dist: dist standalone\n" );
+
+    push( @target, "\nmy-clean: clean standalone-clean\n" );
+
+    return join( '', @target );
+
+}
+
+########
+# MAIN #
+########
+
+WriteMakefile(
+    NAME     => 'Tab2MAGE',
+    DISTNAME => 'Tab2MAGE',
+    AUTHOR   => 'Tim Rayner <rayner at ebi.ac.uk>',
+    ABSTRACT =>
+        "Scripts and modules for data file checking and MAGE-ML generation.",
+    VERSION   => $VERSION,
+    PREREQ_PM => {
+        'Bio::MAGE'              => "20030502.3",
+        'Bio::MAGE::XML::Writer' => "20060815",
+        'LWP::UserAgent'    => "2.031",
+        'DBI'               => "1.48",
+        'DBD::mysql'        => "2.1017",
+        'Storable'          => "1.014",
+        'IO::File'          => "1.10",
+        'Digest::MD5'       => "2.33",
+        'Tie::IxHash'       => "1.21",
+        'Readonly'          => "1.03",
+        'Text::CSV_XS'      => "0.28",
+        'List::MoreUtils'   => "0.17",
+	'Config::YAML'      => "1.42",
+	'Class::Std'        => "0.0.8",
+	'Parse::RecDescent' => "1.94",
+    },
+    EXE_FILES => \@SCRIPTS,
+    dist      => { COMPRESS => 'gzip', SUFFIX => 'gz' },
+);
+
diff --git a/README b/README
new file mode 100644
index 0000000..76abc62
--- /dev/null
+++ b/README
@@ -0,0 +1,187 @@
+ArrayExpress Tab2MAGE
+=====================
+
+Introduction
+------------
+
+The ArrayExpress Tab2MAGE package consists of a set of scripts and
+modules which can read in microarray experiment annotation in defined
+spreadsheet formats, combine it with a set of experiment data files,
+and output the result in MAGE-ML format. Tools are included for
+validation of these spreadsheet documents, and automation of the
+entire process.
+
+This package can handle two distinct spreadsheet formats: MAGE-TAB,
+the new community-designed spreadsheet format for microarray data
+representation and interchange, and the eponymous Tab2MAGE, an older,
+simpler format upon which the design for MAGE-TAB was based.
+
+Please see the 'docs' and 'examples' directories for detailed usage
+information and some simple spreadsheet examples. This information is
+also available online at http://tab2mage.sourceforge.net/
+
+Automated experiment data checking
+----------------------------------
+
+Included in the package is an "experiment checker" script
+(expt_check.pl) which can examine the data files and annotation for an
+experiment and report on common errors. This checker script can be run
+in one of four modes, either to parse a Tab2MAGE spreadsheet, a
+MAGE-TAB document, check a submission in a local MIAMExpress database,
+or scan a series of data files in stand-alone mode.
+
+Spreadsheet visualization
+-------------------------
+
+The tab2mage.pl, magetab.pl, expt_check.pl and t2m_visualize.pl
+scripts will, as part of their operation, use the Graphviz 'dot'
+application if it has been installed, to produce a PNG format file
+showing the links between biomaterials, hybridizations and data
+files. If Graphviz has not been installed then this step will be
+skipped, and only the dot input file will be generated.
+
+Installation from source
+------------------------
+
+The Tab2MAGE Perl scripts currently require at least Perl version
+5.8.0 (version 5.8.1 or above is recommended), and that the following
+non-core Perl modules are installed:
+
+- Bio::MAGE (a.k.a. Perl MAGEstk. Note that this module requires
+             XML::Xerces and the Xerces C++ library from Apache
+             to be installed.)
+- Bio::MAGE::XML::Writer (part of the Bio-MAGE-Utils package).
+- LWP::UserAgent
+- DBI
+- DBD::mysql
+- Digest::MD5
+- Tie::IxHash
+- List::MoreUtils
+- Text::CSV_XS
+- Readonly   (and preferably Readonly::XS, if available)
+- Config::YAML
+- Class::Std
+- Parse::RecDescent
+
+Note that DBI and DBD::mysql are only required if you wish to use this
+module with a local installation of MIAMExpress (in which case you
+will have already had to install this module anyway).
+
+This package has been tested with Bio::MAGE versions 20020902.5 and
+newer; version 20030502.3 is strongly recommended, though, as it fixes
+several bugs in the generated XML which can render the output MAGE-ML
+invalid.
+
+In the special case that you have your own local instance of the
+ArrayExpress Oracle database, you may also benefit from installing
+DBD::Oracle.
+
+Currently the scripts in the Tab2MAGE package requires access to a
+unix-like 'sort' utility on the execution path. Linux and other Unix
+operating systems provides this utility by default; Microsoft Windows
+also provides a sort utility with a slightly different invocation
+syntax. Tab2MAGE is fully supported on all unix-like systems, and has
+been found to work on at least one Windows system (MS Windows 2000
+ActiveState Perl 5.8.6 (build 811) using XML-Xerces 2.3.0). Note that
+the order in which DesignElements are referenced in the
+DesignElementDimensions will differ between Unix and Windows systems
+due to differences in the sort algorithm. This does not affect the
+validity of the output MAGE-ML.
+
+After unpacking the distribution, install this module by typing
+
+        perl Makefile.PL
+        make
+	make test
+        make install
+
+To install the module in a location other than the main site-perl
+modules directory, please use the following command (suitably
+modified) to generate the makefile:
+
+perl Makefile.PL LIB=<perl module dir> INSTALLSCRIPT=<binary executable dir>
+
+You should also set INSTALLMAN1DIR and INSTALLMAN3DIR if you need to
+install man pages in non-standard locations. Please see the perldoc or
+man page for ExtUtils::MakeMaker for further information.
+
+The modules were originally written to provide functions supporting
+our MIAMExpress experiment checker script and our spreadsheet MAGE-ML
+generating scripts. These scripts are included in the package and will
+be installed in the system binary executable directory, or the
+directory to which INSTALLSCRIPT points. There is perldoc
+documentation available for the main scripts. Please see the
+'examples' subdirectory for an example of the experiment summary
+spreadsheet format to be used with the tab2mage.pl script. Also
+included in the examples directory is a sample listing of non-default
+QuantitationTypes for use with the -q option of tab2mage.pl and
+expt_check.pl.
+
+Automated Submissions System
+----------------------------
+
+As of Tab2MAGE version 1.9.0 an automated submissions system is
+included in the downloadable package. While this is not needed to use
+the expt_check.pl, magetab.pl and tab2mage.pl scripts, it may be of
+interest for sites which wish to automate processing of MAGE-TAB,
+Tab2MAGE or MIAMExpress submissions. The system consists of a number
+of components:
+
+1. A MySQL database to store submissions info and status.
+
+2. Web submissions forms for MAGE-TAB and Tab2MAGE spreadsheets, and
+data files.
+
+3. A back-end web interface for curators to monitor and control
+incoming submissions. This is a Ruby on Rails application - see the
+file automation/admin/INSTALL for details.
+
+4. A set of daemon scripts which automatically run the experiment
+checker scripts, assign scores, and export MAGE-ML from those
+submissions which pass the tests.
+
+There are many prerequisite Perl modules for this system:
+
+Archive::Any
+Archive::Tar
+Class::DBI
+CGI::Application
+CGI::Application::Plugin::Authenticate
+CGI::Application::Plugin::Authenticate::Driver::CDBI
+CGI::Application::Plugin::Session
+CGI::Application::Plugin::ValidateRM
+CGI::Upload
+Data::FormValidator
+Date::Manip
+DBD::mysql
+Email::Valid
+IO::Zlib
+MIME::Lite
+Proc::Daemon
+
+Several of these modules have their own complex dependencies, so it is
+strongly recommended that you use a system such as CPAN or CPANPLUS to
+automate the installation of these modules.
+
+Note: Previous versions of the Tab2MAGE package used a SQLite back-end
+database. As of version 1.9.9, however, we have switched over to using
+MySQL. A migration script, migrate_sqlite2mysql.pl, is included in the
+util/ directory to help you convert a SQLite dump file into MySQL
+syntax.
+
+ArrayMAGE
+---------
+
+Also included in this package are two lightweight scripts for
+generating array design MAGE-ML: arraymage.pl and cdf2mage.pl. If you
+wish to use these scripts, please install the following modules:
+
+Readonly::XS
+
+Note that these scripts are primarily designed for use with very large
+array designs which cannot be exported to MAGE-ML by other
+means. These scripts do not yet support the full MAGE model for array
+designs, and should only be used where other approaches have failed.
+
+---------------------------------------------------------------------
+Copyright Tim Rayner <rayner at ebi.ac.uk> ArrayExpress Team, EBI, 2007.
diff --git a/README-windows.txt b/README-windows.txt
new file mode 100644
index 0000000..a53060a
--- /dev/null
+++ b/README-windows.txt
@@ -0,0 +1,62 @@
+Unfortunately I am unable to fully support MS Windows installations,
+simply due to lack of time and the relative complexity of doing
+so. However, users wishing to try Tab2MAGE on Windows may find the
+following instructions useful.
+
+The following procedure has been found to work on a MS Windows 2000
+installation without using either administrator rights or a compiler.
+
+1. Install ActiveState Perl version 5.8.6 (build 811) or newer. Make a
+note of the location of the perl executable. The default location is
+in C:\Perl\bin. You may need to add this location to your PATH
+variable.
+
+2. Download XML-Xerces from the following directory:
+
+http://www.apache.org/dist/xml/xerces-p/binaries/
+
+You will need the file named "XML-Xerces-2.3.0-4.win32.zip". Newer
+versions are untested, but may work. Unzip the download. The Readme
+file which is extracted should help you install the module, but if it
+is absent or unclear, continue as follows:
+
+Copy or move the unzipped .dll file into the same directory as the
+perl executable (e.g., C:\Perl\bin).
+
+Also unzipped from this package are two directories, "perl5.6" and
+"perl5.8". Open a command prompt, change to the perl5.8 directory, and
+execute the following command:
+
+    ppm install XML-Xerces.ppd
+
+This should install the XML::Xerces module and supporting C library.
+
+3. Use ppm to install the following modules and their dependencies:
+
+    ppm install Tie-IxHash
+    ppm install Bio-MAGE
+    ppm install Readonly
+    ppm install List-MoreUtils
+    ppm install Text-CSV_XS
+    ppm install Config-YAML
+    ppm install Class-Std
+    ppm install Parse-RecDescent
+
+4. Install nmake, downloaded from here:
+
+http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe
+
+5. Since you've got this far, we'll assume you already have the
+Tab2MAGE package. If not, download it and uncompress it.
+
+6. In the Tab2MAGE top level directory, install the package by
+executing the following:
+
+    perl Makefile.PL
+    nmake
+    nmake install
+
+You should now have a working installation of Tab2MAGE. Congratulations.
+
+---------------------------------------------------------------------
+Copyright Tim Rayner <rayner at ebi.ac.uk> ArrayExpress Team, EBI, 2007.
diff --git a/adv_inst/InstallConfig.yml-example b/adv_inst/InstallConfig.yml-example
new file mode 100644
index 0000000..26b1e4e
--- /dev/null
+++ b/adv_inst/InstallConfig.yml-example
@@ -0,0 +1,51 @@
+--- !site-config
+
+# This file contains settings for a customised deployment of Tab2MAGE
+# and associated components into a production environment. This is
+# used internally by ArrayExpress, and is included here solely for
+# completeness. In most cases you should instead follow the
+# installation instructions in the main Tab2MAGE README file.
+
+# Location of the main source tree to install (defaults to the parent
+# directory).
+source_directories:
+    - ..
+
+# Scripts installed by Makefile.pl (this list is post-processed to set
+# "use lib" pragmas and repoint to use the desired perl interpreter).
+scripts_to_install:
+    - tab2mage.pl
+    - magetab.pl
+    - expt_check.pl
+    - convert_to_mcmr.pl
+
+# Other scripts not installed by default, only used for this installation.
+auxilliary_scripts:
+    - /full/path/to/script1.pl
+    - /full/path/to/script2.pl
+
+# Which perl interpreter to use.
+perl_location: /full/path/to/bin/perl
+
+# Directories to be included in the $PATH environmental variable.
+# Currently needs: dot, sort, perl (the latter only for automated MIAMExpress export).
+path: '/enter/your/full/PATH/var/here'
+
+# The value of the $ORACLE_HOME variable to use (obviously this will
+# vary with your Oracle install).
+oracle_home: /full/path/to/oracle/product/9.2.0
+ora_nls33:   /full/path/to/oracle/product/9.2.0/ocommon/nls/admin/data
+
+# The value of $LD_LIBRARY_PATH to use. This path needs to include
+# libraries such as Xerces-C++, MySQL, Oracle, depending on which
+# package components you are using.
+ld_library_path: '/enter/your/full/LD_LIBRARY_PATH/here'
+
+# Location of main Tab2MAGE site config file, to be used in Config.pm
+site_config_location: /path/to/your/tab2mage/config/file/here
+
+# Placeholder strings used in the scripts. These are unlikely to
+# change, as they are now embedded throughout the Tab2MAGE codebase.
+use_lib_placeholder: '# use lib /path/to/directory/containing/Curator/modules;'
+shebang_placeholder: '#!/usr/bin/env perl'
+
diff --git a/adv_inst/README b/adv_inst/README
new file mode 100644
index 0000000..f3c47b6
--- /dev/null
+++ b/adv_inst/README
@@ -0,0 +1,6 @@
+This directory contains a tool that may be used for customised
+deployment of Tab2MAGE and associated components into a production
+environment. This is used internally by ArrayExpress, and is included
+here solely for completeness. In most situations you should instead
+follow the (much simpler) installation procedure described in the main
+Tab2MAGE README file.
diff --git a/adv_inst/T2MInstaller.pm b/adv_inst/T2MInstaller.pm
new file mode 100644
index 0000000..a934da1
--- /dev/null
+++ b/adv_inst/T2MInstaller.pm
@@ -0,0 +1,321 @@
+#!/usr/bin/env perl
+#
+# $Id: T2MInstaller.pm 2069 2008-06-04 14:33:52Z tfrayner $
+#
+# Module used in automated advanced deployment of tab2mage and related
+# code into the ArrayExpress production environment. This is *not*
+# needed for basic installation of the Tab2MAGE package.
+
+package T2MInstaller;
+
+use strict;
+use warnings;
+
+use File::Spec;
+use File::Basename qw(basename);
+use File::Copy qw(copy);
+
+use Config::YAML;
+use Config;
+
+use Carp;
+
+use base qw(Exporter);
+our @EXPORT_OK = qw(install);
+
+sub get_config {
+
+    my ( $args ) = @_;
+
+    # Default to using a config file named "InstallConfig.yml" in the
+    # same directory as this module. If a config_file argument is
+    # passed, use that instead.
+    my $config_file;
+    unless ( $config_file = $args->{config_file} ) {
+	my @module_dir_array = File::Spec->splitpath(__FILE__);
+	$config_file         = File::Spec->catpath(
+	    @module_dir_array[ 0, 1 ], 'InstallConfig.yml',
+	);
+    }
+
+    unless ( -f $config_file ) {
+	if ( $args->{config_file} ) {
+	    croak(qq{Error: config file "$config_file" not found.\n});
+	}
+	else {
+	    croak(qq{Config file InstallConfig.yml not found. Please ensure that you}
+		. qq{ have edited the example config and renamed it to "$config_file".\n});
+	}
+    }
+
+    my $config = Config::YAML->new( config => $config_file );
+
+    return $config;
+}
+
+sub install_aux {
+
+    my ( $auxilliary_scripts, $bindir ) = @_;
+
+    foreach my $aux ( @$auxilliary_scripts ) {
+        my $targetfile = basename( $aux );     # just the filename
+        my $targetpath = File::Spec->catfile( $bindir, $targetfile );
+        print "Copying $aux to $targetpath...\n";
+        copy( $aux, $targetpath )
+            or croak("Error: Unable to copy file $aux: $!\n");
+    }
+
+    return;
+}
+
+sub install_modules {
+
+    my ( $source_directories, $args ) = @_;
+
+    my $perlmakefile_command = 'perl Makefile.PL';
+    $perlmakefile_command .=
+        $args->{inst_lib}
+        ? " LIB=$args->{inst_lib}"
+        : q{};
+
+    $perlmakefile_command .=
+        $args->{bin}
+        ? " INSTALLSCRIPT=$args->{bin}"
+        : q{};
+
+    $perlmakefile_command .=
+        $args->{man1}
+        ? " INSTALLMAN1DIR=$args->{man1} INSTALLSITEMAN1DIR=$args->{man1}"
+        : q{};
+
+    $perlmakefile_command .=
+        $args->{man3}
+        ? " INSTALLMAN3DIR=$args->{man3} INSTALLSITEMAN3DIR=$args->{man3}"
+        : q{};
+
+    foreach my $sourcedir (@$source_directories) {
+
+        chdir($sourcedir)
+            or croak("Unable to change directory to $sourcedir: $!\n");
+
+        if ( -f 'Makefile' ) {
+            system('make distclean') == 0
+                or croak("make distclean failed: $?");
+        }
+
+        system($perlmakefile_command) == 0
+            or croak("perl Makefile.PL failed: $?");
+
+        system('make') == 0
+            or croak("make failed: $?");
+
+        system('make test') == 0
+            or croak("make test failed: $?");
+
+        system('make install') == 0
+            or croak("make install failed: $?");
+
+    }
+}
+
+sub get_bindir {
+
+    my ( $args ) = @_;
+
+    my $bindir = $args->{bin} || $Config{installsitebin};
+
+    return $bindir;
+}
+
+sub get_inst_libdir {
+
+    my ( $args ) = @_;
+
+    my $inst_libdir = $args->{inst_lib} || $Config{installsitelib};
+
+    return $inst_libdir;
+}
+
+sub postprocess_scripts {
+
+    my ( $config, $args ) = @_;
+
+    my $auxilliary_scripts = $config->get_auxilliary_scripts() || [];
+    my $scripts_to_install = $config->get_scripts_to_install() || [];
+
+    my $perl_location       = $args->{perl_location}
+	                   || $config->get_perl_location();
+    my $use_lib_placeholder = $config->get_use_lib_placeholder();
+    my $shebang_placeholder = $config->get_shebang_placeholder();
+
+    my $perl_libdir = $args->{perl_lib}
+                    ? join(q{','}, split /:/, $args->{perl_lib})
+		    : $Config{installsitelib};
+    my $inst_libdir = get_inst_libdir( $args );
+    my $bindir      = get_bindir( $args );
+
+    my $path            = $args->{path}            || $config->get_path()            || q{};
+    my $oracle_home     = $args->{oracle_home}     || $config->get_oracle_home()     || q{};
+    my $ora_nls33       = $args->{ora_nls33}       || $config->get_ora_nls33()       || q{};
+    my $ld_library_path = $args->{ld_library_path} || $config->get_ld_library_path() || q{};
+
+    my $ld_lib_components = join(", ", map { qq{'$_'} } split /:/, $ld_library_path);
+
+    my $use_lib_declaration = (<<"END_USE_LIB");
+use lib '$perl_libdir';
+use lib '$inst_libdir';  # This takes precedence
+
+# We need to control the environment. We do so with this unholy kludge.
+BEGIN {
+
+    # With thanks to www.perlmonks.org/?node_id=126587 and following, monks tye and mikeraz
+    # (slightly modified from the originals).
+    my \@needed = ($ld_lib_components);
+
+    # Get our LD_LIBRARY_PATH before we mess around with the environment.
+    my \$ld_path = \$ENV{LD_LIBRARY_PATH} || q{};
+
+    # Set the easy things here.
+    my \$tmp_home = \$ENV{HOME};
+    \%ENV = ();
+    \$ENV{PATH}            = "$path";
+    \$ENV{HOME}            = \$tmp_home;
+    \$ENV{ORACLE_HOME}     = "$oracle_home";
+    \$ENV{ORA_NLS33}       = "$ora_nls33";
+
+    # Check for the existence of each element of \@needed in the path;
+    # if not found, make a note.
+    my \$changed_path = 0;
+    foreach my \$dir  ( \@needed ) {
+	if (  \$ld_path !~ m/(^|:)\\Q\$dir\\E(:|\$)/  ) {
+	    \$changed_path = 1;
+	}
+    }
+
+    # If the path needs modification, do so and re-execute the script.
+    if ( \$changed_path ) {
+	\$ENV{LD_LIBRARY_PATH} = join ":", \@needed;
+	exec 'env', \$^X, \$0, \@ARGV;
+    }
+
+    # Otherwise, we're home free! Start our engines...
+}
+
+
+END_USE_LIB
+
+    SCRIPT:
+    foreach my $script ( @$scripts_to_install, @$auxilliary_scripts, ) {
+
+	my $target = basename( $script );
+
+        my $orig = File::Spec->catfile( $bindir, $target );
+        my $temp = File::Spec->catfile( $bindir, "$target.tmp" );
+
+        printf STDOUT ( "Post-processing script: %-25s...", $target );
+
+	open( my $orig_fh, '<', $orig )
+	    or do {
+		carp("Error opening $orig (skipping): $!\n");
+		next SCRIPT;
+	    };
+        open( my $temp_fh, '>', $temp )
+            or croak("Error opening temporary file $temp: $!\n");
+
+        while ( my $line = <$orig_fh> ) {
+
+            if (   $use_lib_placeholder
+                && $line =~ s!$use_lib_placeholder!$use_lib_declaration! ) {
+                print STDOUT qq{ "use lib" declaration fixed.};
+            }
+
+            if (   $perl_location
+                && $shebang_placeholder
+                && $line =~ s[$shebang_placeholder][#!$perl_location --] ) {
+                print STDOUT qq{ shebang line redirected.};
+            }
+
+            print $temp_fh $line;
+        }
+
+        unlink($orig) or croak("Unable to unlink original file $orig: $!\n");
+        rename( $temp, $orig )
+            or croak("Unable to rename $temp to $orig: $!\n");
+
+        system("chmod 0755 $orig") == 0
+            or croak "chmod 0755 $orig failed: $?";
+
+        close $temp_fh or croak("$!\n");
+        close $orig_fh or croak("$!\n");
+
+	print STDOUT "\n";
+    }
+
+    return;
+}
+
+sub postprocess_siteconfig {
+
+    my ( $config, $args ) = @_;
+
+    my $inst_libdir = get_inst_libdir( $args );
+
+    my $config_pm   = "$inst_libdir/ArrayExpress/Curator/Config.pm";
+    my $config_temp = "$config_pm.tmp";
+    my $site_config = $args->{site_config_location}
+	|| $config->get_site_config_location;
+
+    if ( -f $config_pm && $site_config ) {
+	print STDOUT "Fixing Config.pm module...";
+
+	open( my $origpm_fh, '<', $config_pm )
+	    or croak("Error opening $config_pm: $!\n");
+	open( my $temppm_fh, '>', $config_temp )
+	    or croak("Error opening $config_temp: $!\n");
+
+	while ( my $line = <$origpm_fh> ) {
+	    $line =~ s/(\$siteconf\s*=\s*)q\{\}/$1q\{$site_config\}/g;
+	    print $temppm_fh ($line);
+	}
+
+	unlink($config_pm)
+	    or croak("Unable to unlink original file $config_pm: $!\n");
+	rename( $config_temp, $config_pm )
+	    or croak("Unable to rename $config_temp to $config_pm: $!\n");
+
+	close $temppm_fh or croak("$!\n");
+	close $origpm_fh or croak("$!\n");
+
+	print STDOUT " Done\n\n";
+    }
+    return;
+}
+
+sub install {
+
+    my ( $args ) = @_;
+
+    my $config = get_config( $args );
+    my $bindir = get_bindir( $args );
+
+    # Scripts not in the Makefile.PL. This is a more likely point of
+    # failure, so we try it first.
+    my $auxilliary_scripts = $config->get_auxilliary_scripts() || [];
+    install_aux( $auxilliary_scripts, $bindir );
+
+    # Run the main install.
+    my $source_directories = $args->{source_directories}
+        || $config->get_source_directories()
+	    or croak("Error: No source directories\n");
+    install_modules( $source_directories, $args );
+
+    # Post-install changes.
+    postprocess_scripts( $config, $args );
+
+    # Repoint the main Config.pm module to our site config file.
+    postprocess_siteconfig( $config, $args );
+
+    return;
+}
+
+1;
diff --git a/adv_inst/deploy.pl b/adv_inst/deploy.pl
new file mode 100755
index 0000000..59ed2e5
--- /dev/null
+++ b/adv_inst/deploy.pl
@@ -0,0 +1,66 @@
+#!/usr/bin/env perl
+#
+# $Id: deploy.pl 1864 2008-01-02 09:58:24Z tfrayner $
+#
+# Script to automate advanced deployment of tab2mage and related code
+# into the ArrayExpress production environment. This is *not* needed
+# for basic installation of the Tab2MAGE package.
+
+use strict;
+use warnings;
+
+use Getopt::Long;
+use T2MInstaller qw(install);
+
+my ( $config, $wanthelp );
+
+GetOptions(
+    "c|config=s" => \$config,
+    "h|help"     => \$wanthelp,
+);
+
+if ( $wanthelp ) {
+
+    print <<"USAGE";
+
+    Usage:   ./deploy.pl
+
+    Options:
+
+	-c or --config     Deployment configuration file to use.
+	-h or --help       Display this help message.
+
+    The script must be launched from within the same directory as the
+    T2MInstaller.pm module. If the config file is not specified the
+    script will look for a file named InstallConfig.yml in the same
+    directory, and fail if it is not found or is unreadable.
+
+USAGE
+
+    exit 255;
+}
+
+install({
+
+    # Optional config file argument.
+    config_file => $config,
+
+    # These options can be used to fine-tune the install
+    # location. Different scripts can be set up to deploy into
+    # e.g. testing and production environments. Uncomment to set these
+    # options.
+
+    # The location to install the perl modules themselves.
+    #    inst_lib  => '/full/path/to/local/lib/perl5/site_perl/',
+
+    # Where to install the scripts.
+    #    bin       => '/full/path/to/bin/',
+
+    # The location for the man pages.
+    #    man1      => '/full/path/to/local/man/man1',
+    #    man3      => '/full/path/to/local/man/man3',
+
+    # Any non-standard locations for the perl modules on which this
+    # package depends should be included here.
+    #    perl_lib  => '/all/paths:/needed/in/PERL5LIB:/environmental/variable',
+});
diff --git a/arraymage/arraymage.pl b/arraymage/arraymage.pl
new file mode 100755
index 0000000..ff70cea
--- /dev/null
+++ b/arraymage/arraymage.pl
@@ -0,0 +1,499 @@
+#!/usr/bin/env perl
+#
+# Fast ADF to MAGE-ML converter
+#
+# Tim Rayner 2005, EBI Microarray Informatics Team
+#
+# $Id: arraymage.pl 1938 2008-02-10 18:34:20Z tfrayner $
+
+use strict;
+use warnings;
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use Readonly;
+use ArrayExpress::ArrayMAGE qw(:ALL);
+
+###########
+# GLOBALS #
+###########
+
+# These are shared with ArrayExpress::ArrayMAGE
+ArrayExpress::ArrayMAGE->set_programname('ArrayMAGE');
+ArrayExpress::ArrayMAGE->set_namespace('ebi.ac.uk:ArrayMAGE');
+
+# We do this ourselves, nothing fancy.
+my $svn_revision = '$Revision: 1938 $';
+our ($VERSION) = ( $svn_revision =~ /^\$Revision: ([\d\.]*)/ );
+
+##########################
+###### Main Package ######
+##########################
+
+package main;
+
+use Getopt::Long;
+use File::Spec;
+use Benchmark;
+
+Readonly my $META_COLUMN         => 'MetaColumn';
+Readonly my $META_ROW            => 'MetaRow';
+Readonly my $COLUMN              => 'Column';
+Readonly my $ROW                 => 'Row';
+Readonly my $REPORTER_IDENTIFIER => 'Reporter Identifier';
+Readonly my $REPORTER_NAME       => 'Reporter Name';
+Readonly my $REPORTER_COMMENT    => 'Reporter Comment';
+
+Readonly my $BIOSEQUENCE_TYPE        => 'Reporter BioSequence Type';
+Readonly my $BIOSEQUENCE_POLYMERTYPE => 'Reporter BioSequence PolymerType';
+Readonly my $REPORTER_CONTROL_TYPE   => 'Reporter Control Type';
+Readonly my $REPORTER_FAIL_TYPE      => 'Reporter Fail Type';
+Readonly my $REPORTER_WARNING_TYPE   => 'Reporter Warning Type';
+
+Readonly my $REPORTER_ACTUAL_SEQUENCE =>
+    'Reporter BioSequence [Actual sequence]';
+
+Readonly my $REPORTER_FWD_PRIMER => 'Reporter BioSequence [Forward primer]';
+
+Readonly my $REPORTER_REV_PRIMER => 'Reporter BioSequence [Reverse primer]';
+
+Readonly my $BIOSEQUENCE_DB_ENTRY_PATTERN =>
+    qr{Reporter\s*BioSequence\s*Database\s*Entry\s*\[\s*(.*?)\s*\]}ixms;
+
+Readonly my $REPORTER_DB_ENTRY_PATTERN =>
+    qr{Reporter\s*Description\s*Database\s*Entry\s*\[\s*(.*?)\s*\]}ixms;
+
+Readonly my $REPORTER_GROUP_PATTERN =>
+    qr{Reporter\s*Group\s*\[\s*(.*?)\s*\]}ixms;
+
+sub parse_args {
+
+    # Parse script arguments, do some preprocessing.
+
+    my ( %args, $output, $wantversion );
+
+    GetOptions(
+        "i|adf=s"       => \$args{adf},
+        "a|accession=s" => \$args{design},
+        "o|output=s"    => \$output,
+        "v|version"     => \$wantversion,
+    );
+
+    if ($wantversion) {
+	my $programname = ArrayExpress::ArrayMAGE->get_programname();
+        print STDERR <<VERSION;
+  This is $programname, SVN revision $VERSION
+VERSION
+        exit;
+    }
+
+    unless ( $args{adf} && $output && $args{design} ) {
+        print STDERR <<NOINPUT;
+
+  Usage: $0 -i <ADF filename> -a <accession number> -o <output file>
+
+NOINPUT
+        exit 255;
+    }
+
+    open( $args{output}, '>', $output )
+        or die("Error: unable to open output file $output: $!\n");
+
+    ArrayExpress::ArrayMAGE->set_design($args{design});
+
+    return \%args;
+}
+
+sub coerce_headings {
+
+    # Match up our headings with the set of known constant headings,
+    # warn for unrecognized headings.
+
+    my ($line) = @_;
+    $line =~ s/[\r\n]*$//;
+    my @headings = split /\t/, $line;
+
+    foreach my $name (@headings) {
+
+        my $test = $name;
+        $test =~ s/ //g;
+
+        HEADNAME: {
+
+            ( $test =~ /^metacolumn$/i )
+                && do { $name = $META_COLUMN; last HEADNAME; };
+
+            ( $test =~ /^metarow$/i )
+                && do { $name = $META_ROW; last HEADNAME; };
+
+            ( $test =~ /^column$/i )
+                && do { $name = $COLUMN; last HEADNAME; };
+
+            ( $test =~ /^row$/i )
+                && do { $name = $ROW; last HEADNAME; };
+
+            ( $test =~ /^reporteridentifier$/i )
+                && do { $name = $REPORTER_IDENTIFIER; last HEADNAME; };
+
+            ( $test =~ /^reportername$/i )
+                && do { $name = $REPORTER_NAME; last HEADNAME; };
+
+            ( $test =~ /^reportercomment$/i )
+                && do { $name = $REPORTER_COMMENT; last HEADNAME; };
+
+            ( $test =~ /biosequencetype$/i )
+                && do { $name = $BIOSEQUENCE_TYPE; last HEADNAME; };
+
+            ( $test =~ /biosequencepolymertype$/i )
+                && do { $name = $BIOSEQUENCE_POLYMERTYPE; last HEADNAME; };
+
+            ( $test =~ /controltype$/i )
+                && do { $name = $REPORTER_CONTROL_TYPE; last HEADNAME; };
+
+            ( $test =~ /failtype$/i )
+                && do { $name = $REPORTER_FAIL_TYPE; last HEADNAME; };
+
+            ( $test =~ /warningtype$/i )
+                && do { $name = $REPORTER_WARNING_TYPE; last HEADNAME; };
+
+            ( $test =~ /actualsequence/i )
+                && do { $name = $REPORTER_ACTUAL_SEQUENCE; last HEADNAME; };
+
+            ( $test =~ /(forward|fwd)primer/i )
+                && do { $name = $REPORTER_FWD_PRIMER; last HEADNAME; };
+
+            ( $test =~ /(reverse|rev)primer/i )
+                && do { $name = $REPORTER_REV_PRIMER; last HEADNAME; };
+
+            ( $test =~ $BIOSEQUENCE_DB_ENTRY_PATTERN )
+                && do { last HEADNAME };
+
+            ( $test =~ $REPORTER_DB_ENTRY_PATTERN )
+                && do { last HEADNAME };
+
+            ( $test =~ $REPORTER_GROUP_PATTERN )
+                && do { last HEADNAME };
+
+            warn("WARNING: unrecognized column heading \"$name\"\n");
+
+        }
+
+    }
+
+    return \@headings;
+
+}
+
+sub sort_rows {
+
+    # Use the sort utility on unix OSes to quickly sort on Reporter
+    # Identifier (and CS ID FIXME)
+
+    my ( $fh, $headings, $name ) = @_;
+
+    my $colno;
+    for ( 0 .. $#$headings ) {
+        $colno = $_ if ( $headings->[$_] eq $REPORTER_IDENTIFIER );
+    }
+    unless ($colno) {
+        die(      "Error: File contains no "
+                . $REPORTER_IDENTIFIER
+                . " column.\n" );
+    }
+
+    # map 0..(n-1) onto 1..n
+    $colno++;
+
+    my $linecount = 0;
+    open( my $sorted, '|-', "sort -b -t \'\t\' -k $colno -o \"$name\"" )
+        or die("Error opening temporary sort file $name: $!\n");
+
+    print STDOUT ("Sorting ADF on column $colno...\n");
+    while ( my $line = <$fh> ) {
+        print $sorted $line;
+        $linecount++;
+    }
+
+    close $sorted or die("$!\n");
+    open( $sorted, '<', $name ) or die("$!\n");
+
+    return ( $sorted, $linecount );
+}
+
+sub assign_headings {
+
+    # Given a line string and a list of headings, return a heading =>
+    # value hash
+
+    my ( $row, $headings ) = @_;
+
+    $row =~ s/[\r\n]*$//;
+    my @rowlist = split /\t/, $row;
+    my %rowhash;
+
+    for ( 0 .. $#rowlist ) {
+        unless ( $headings->[$_] ) {
+            warn(
+                "WARNING: Skipping entry without column heading $rowlist[$_]\n"
+            );
+            next;
+        }
+        $rowhash{ $headings->[$_] } = $rowlist[$_];
+    }
+    return \%rowhash;
+}
+
+########
+# MAIN #
+########
+
+# Get our arguments
+my $args = parse_args;
+
+# Initialise some stuff
+my $starttime = new Benchmark;
+
+# Open the ADF and scan down to the header row
+open( my $adf, "<", $args->{adf} )
+    or die("Error: unable to open ADF file $args->{adf}: $!\n");
+my $line = q{};
+until ( $line =~ m/\bMeta\s*Column\b/i ) { $line = <$adf> }
+
+# Generate a $headings arrayref with standardized values
+my $headings = coerce_headings($line);
+
+# Sort the file to allow FRM and RCM shortcuts, keeping memory usage
+# down.
+my $tempfile
+    = File::Spec->catpath( File::Spec->tmpdir, "$args->{adf}.temporary" );
+my ( $sorted, $num_lines ) = sort_rows( $adf, $headings, $tempfile );
+
+close $adf;
+
+# Create anon temp file objects (only the compulsory classes are
+# listed here).
+my @classes = qw(
+    Feature
+    Reporter
+    BioSequence
+    FeatureReporterMap
+    ArrayDesign
+    Zone
+);
+my %object;
+
+foreach my $type (@classes) {
+    if ( $type eq 'ArrayDesign' ) {
+        $object{$type} = "ArrayExpress::ArrayMAGE::$type"->new({
+	    accession => $args->{design},
+	});
+    }
+    else {
+	$object{$type} = "ArrayExpress::ArrayMAGE::$type"->new();
+    }
+}
+
+# Create database objects
+my %dbtags;
+foreach my $heading (@$headings) {
+    foreach my $pattern ( $BIOSEQUENCE_DB_ENTRY_PATTERN,
+        $REPORTER_DB_ENTRY_PATTERN ) {
+        if ( my ($tag) = ( $heading =~ $pattern ) ) {
+            $dbtags{$tag}++;
+        }
+    }
+}
+if ( scalar( grep { defined $_ } values %dbtags ) ) {
+    $object{Database} = ArrayExpress::ArrayMAGE::Database->new();
+}
+foreach my $tag ( keys %dbtags ) {
+    $object{Database}->add({ tag => $tag });
+}
+
+# Set up a few tracker variables
+my %zone_tracker;      # hash to track Zone creation
+my %reporter_group;    # hash of ReporterGroup objects
+my $last_reporter = q{};
+my $last_compseq  = q{};
+my @frm_features;      # will be undefed in the loop
+my @rcm_reporters;     # will be undefed in the loop
+
+# Map "other" BioSequences to their respective Types from MO
+my %bioseq_mapping = (
+    $REPORTER_FWD_PRIMER => 'PCR_primer_forward',
+    $REPORTER_REV_PRIMER => 'PCR_primer_reverse',
+);
+
+#############
+# Main loop #
+#############
+
+print STDOUT ("Writing MAGE-ML to temporary files...\n");
+my $progress_bar = ArrayExpress::ArrayMAGE::ProgressBar->new(
+    width   => 40,
+    max_val => $num_lines
+);
+my $linecount = 0;
+while ( my $line = <$sorted> ) {
+
+    $linecount++;
+
+    $progress_bar->draw($linecount);
+
+    # skip blank lines
+    next if $line =~ m/^\s*$/;
+
+    $line =~ s/>/&gt;/g;
+    $line =~ s/</&lt;/g;
+    $line =~ s/"/"/g;
+    $line =~ s/'/'/g;
+    $line =~ s/&/&amp;/g;
+
+    # {heading => value} hashref
+    my $row = assign_headings( $line, $headings );
+
+    # Zone
+    unless ( $zone_tracker{ ( $row->{$META_COLUMN} . $row->{$META_ROW} ) } ) {
+        $object{Zone}->add({
+            row    => $row->{$META_ROW},
+            column => $row->{$META_COLUMN},
+        });
+        $zone_tracker{ ( $row->{$META_COLUMN} . $row->{$META_ROW} ) }++;
+    }
+
+    # Feature
+    $object{Feature}->add({
+        metacolumn => $row->{$META_COLUMN},
+        metarow    => $row->{$META_ROW},
+        column     => $row->{$COLUMN},
+        row        => $row->{$ROW},
+    });
+
+    # FeatureReporterMap, Reporter, BioSequence.
+    # Only do these if we're processing a new reporter id.
+    unless ( $row->{$REPORTER_IDENTIFIER} eq $last_reporter ) {
+
+        # Database or ReporterGroup headings;
+        my ( %bs_dbids, %r_dbids );
+        while ( my ( $rname, $rvalue ) = each %$row ) {
+            if ( my ($type) = ( $rname =~ $REPORTER_GROUP_PATTERN ) ) {
+                my $key = "$type." . $rvalue;
+                unless ( $reporter_group{$key} ) {
+                    $reporter_group{$key}
+                        = ArrayExpress::ArrayMAGE::ReporterGroup->new({
+                        identifier => $key,
+                        tag        => $rvalue,
+                        is_species => ( $type =~ m/species/i ) || 0,
+                    });
+                }
+                $reporter_group{$key}
+                    ->add({ id_ref => $row->{$REPORTER_IDENTIFIER}, });
+            }
+            if ( my ($tag) = ( $rname =~ $BIOSEQUENCE_DB_ENTRY_PATTERN ) ) {
+
+                # keys are accessions, values are db tags
+                $bs_dbids{$rvalue} = $tag if length($rvalue);
+            }
+            if ( my ($tag) = ( $rname =~ $REPORTER_DB_ENTRY_PATTERN ) ) {
+
+                # keys are accessions, values are db tags
+                $r_dbids{$rvalue} = $tag if length($rvalue);
+            }
+        }
+
+        # BioSequence
+        # Flag to indicate whether BS creation should occur
+        my $has_biosequence = ( $row->{$BIOSEQUENCE_POLYMERTYPE}
+                || $row->{$BIOSEQUENCE_TYPE}
+                || $row->{$REPORTER_ACTUAL_SEQUENCE}
+                || scalar( grep { defined $_ } values %bs_dbids ) );
+        my @biosequences;
+        if ($has_biosequence) {
+            $object{BioSequence}->add({
+                identifier  => $row->{$REPORTER_IDENTIFIER},
+                dbrefs      => \%bs_dbids,
+                polymertype => $row->{$BIOSEQUENCE_POLYMERTYPE},
+                type        => $row->{$BIOSEQUENCE_TYPE},
+                sequence    => $row->{$REPORTER_ACTUAL_SEQUENCE},
+            });
+            push( @biosequences, $row->{$REPORTER_IDENTIFIER} );
+        }
+
+        # Primer sequences
+        foreach my $bs_type ( $REPORTER_FWD_PRIMER, $REPORTER_REV_PRIMER ) {
+            if ( $row->{$bs_type} ) {
+                $object{BioSequence}->add({
+                    identifier => $row->{$REPORTER_IDENTIFIER}
+                        . ".$bioseq_mapping{$bs_type}",
+                    polymertype => 'DNA',
+                    type        => $bioseq_mapping{$bs_type},
+                    sequence    => $row->{$bs_type},
+                });
+                push( @biosequences,
+                    $row->{$REPORTER_IDENTIFIER}
+                        . ".$bioseq_mapping{$bs_type}" );
+                $has_biosequence++;
+            }
+        }
+
+        # Reporter
+        $object{Reporter}->add({
+            identifier   => $row->{$REPORTER_IDENTIFIER},
+            name         => $row->{$REPORTER_NAME},
+            comment      => $row->{$REPORTER_COMMENT},
+            biosequences => \@biosequences,
+            controltype  => $row->{$REPORTER_CONTROL_TYPE},
+            warningtype  => $row->{$REPORTER_WARNING_TYPE},
+            failtype     => $row->{$REPORTER_FAIL_TYPE},
+            dbrefs       => \%r_dbids,
+        });
+
+        # FeatureReporterMap
+
+        # Skip over creation of first FRM; the last is created outside
+        # the end of this loop.
+        if ($last_reporter) {
+            $object{FeatureReporterMap}->add({
+                identifier => $last_reporter,
+                features   => \@frm_features,
+            });
+        }
+        undef @frm_features;
+
+    }
+
+    push( @frm_features,
+        "$row->{$META_COLUMN}.$row->{$META_ROW}.$row->{$COLUMN}.$row->{$ROW}"
+    );
+    $last_reporter = $row->{$REPORTER_IDENTIFIER};
+
+    # same for compseq FIXME
+
+}
+
+# Deal with the last FRM/RCM
+$object{FeatureReporterMap}->add({
+    identifier => $last_reporter,
+    features   => \@frm_features,
+});
+
+unlink($tempfile)
+    or warn("Warning: could not delete temporary file $tempfile: $!\n");
+
+# Empty hash is composite_group placeholder FIXME
+ArrayExpress::ArrayMAGE->combine_parts(
+    \%object,
+    \%reporter_group,
+    {},
+    $args->{output},
+);
+
+print STDOUT ("Done.\n");
+
+my $endtime = new Benchmark;
+print STDOUT (
+    "\nTotal run time = ",
+    timestr( timediff( $endtime, $starttime ) ), "\n\n"
+);
+
diff --git a/arraymage/cdf2mage.pl b/arraymage/cdf2mage.pl
new file mode 100755
index 0000000..8cff95d
--- /dev/null
+++ b/arraymage/cdf2mage.pl
@@ -0,0 +1,728 @@
+#!/usr/bin/env perl
+#
+# Fast CDF to MAGE-ML converter; a work in progress.
+#
+# Tim Rayner 2006, EBI Microarray Informatics Team
+#
+# $Id: cdf2mage.pl 1938 2008-02-10 18:34:20Z tfrayner $
+
+use strict;
+use warnings;
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use ArrayExpress::ArrayMAGE qw(:ALL);
+use ArrayExpress::ArrayMAGE::NullObject;
+
+###########
+# GLOBALS #
+###########
+
+ArrayExpress::ArrayMAGE->set_programname('CDF2MAGE');
+ArrayExpress::ArrayMAGE->set_namespace('Affymetrix');
+ArrayExpress::ArrayMAGE->set_separator(':');
+
+# We do this ourselves, nothing fancy.
+my $svn_revision = '$Revision: 1938 $';
+our ($VERSION) = ( $svn_revision =~ /^\$Revision: ([\d\.]*)/ );
+
+# Global flag to determine whether we're CS-only or not
+my $CS_ONLY    = 0;
+
+# This is a default, may be overwritten upon initial parsing of the CDF.
+my $CDF_LINEBREAK = '\015\012';
+
+use Getopt::Long;
+use File::Spec;
+use Benchmark;
+use English qw( -no_match_vars );
+
+use ArrayExpress::Curator::Common qw(check_linebreaks);
+
+sub parse_args {
+
+    # Parse script arguments, do some preprocessing.
+
+    my ( %args, $output, $wantversion );
+
+    GetOptions(
+        "i|cdf=s"       => \$args{cdf},
+        "a|accession=s" => \$args{accession},
+        "o|output=s"    => \$output,
+	"c|compseq"     => \$CS_ONLY,
+        "v|version"     => \$wantversion,
+    );
+
+    if ($wantversion) {
+	my $programname = ArrayExpress::ArrayMAGE->get_programname();
+        print STDERR <<VERSION;
+  This is $programname, SVN revision $VERSION
+VERSION
+        exit;
+    }
+
+    unless ( $args{cdf} && $output && $args{accession} ) {
+        print STDERR <<NOINPUT;
+
+  Usage: $0 -i <CDF filename> -a <accession number> -o <output file>
+
+For very large CDF files, use the -c option to generate only the CompositeSequences.
+
+NOINPUT
+        exit 255;
+    }
+
+    # Basic sanity checks on the CDF file.
+    if ( ! -f $args{cdf} ) {
+        die("Error: CDF file not found: $args{cdf}\n");
+    }
+    elsif ( -B $args{cdf} ) {
+        die("Error: Binary GCOS CDF files not yet supported.\n");
+    }
+
+    open( $args{output}, '>', $output )
+        or die("Error: unable to open output file $output: $!\n");
+
+    return \%args;
+}
+
+sub cs_initialize {
+
+    my ( $accession, $reporter_group_id, $composite_group_id, $design_id ) = @_;
+
+    # Create anon temp file objects (only the compulsory classes are
+    # listed here).
+    my @classes = qw(
+        BioSequence
+        CompositeSequence
+        ArrayDesign
+    );
+    my @null_classes = qw(
+        Feature
+        Reporter
+        FeatureReporterMap
+        ReporterCompositeMap
+        Zone
+    );
+    my %object;
+
+    # Construct a single ReporterGroup
+    my $reporter_group = ArrayExpress::ArrayMAGE::NullObject->new;
+
+    # Construct a single CompositeGroup
+    my $composite_group = ArrayExpress::ArrayMAGE::CompositeGroup->new({
+        identifier => $composite_group_id,
+        tag        => $design_id,
+    });
+
+    # Initialize the main classes.
+    foreach my $type (@classes) {
+	if ( $type eq 'ArrayDesign' ) {
+	    $object{$type} = "ArrayExpress::ArrayMAGE::$type"->new({
+		accession => $accession,
+	    });
+	}
+	else {
+	    $object{$type} = "ArrayExpress::ArrayMAGE::$type"->new();
+	}
+    }
+
+    # Initialize the null classes.
+    foreach my $type (@null_classes) {
+	$object{$type} = ArrayExpress::ArrayMAGE::NullObject->new;
+    }
+
+    # NetAffx database used for ProbeSet ids
+    $object{Database} = ArrayExpress::ArrayMAGE::Database->new();
+    $object{Database}->add({ tag => 'netaffx' });
+
+    return ( \%object, $reporter_group, $composite_group, $reporter_group_id,
+        $composite_group_id );
+}
+
+sub initialize {
+
+    my ( $accession, $reporter_group_id, $composite_group_id, $design_id ) = @_;
+
+    # Create anon temp file objects (only the compulsory classes are
+    # listed here).
+    my @classes = qw(
+        Feature
+        Reporter
+        CompositeSequence
+        BioSequence
+        FeatureReporterMap
+        ReporterCompositeMap
+        ArrayDesign
+        Zone
+    );
+    my %object;
+
+    # Construct a single ReporterGroup
+    my $reporter_group = ArrayExpress::ArrayMAGE::ReporterGroup->new({
+        identifier => $reporter_group_id,
+        tag        => $design_id,
+    });
+
+    # Construct a single CompositeGroup
+    my $composite_group = ArrayExpress::ArrayMAGE::CompositeGroup->new({
+        identifier => $composite_group_id,
+        tag        => $design_id,
+    });
+
+    # Initialize the main classes.
+    foreach my $type (@classes) {
+	if ( $type eq 'ArrayDesign' ) {
+	    $object{$type} = "ArrayExpress::ArrayMAGE::$type"->new({
+		accession => $accession,
+	    });
+	}
+	else {
+	    $object{$type} = "ArrayExpress::ArrayMAGE::$type"->new();
+	}
+    }
+
+    # Only one zone for Affy arrays.
+    $object{Zone}->add({
+        row    => 1,
+        column => 1,
+    });
+
+    # NetAffx database used for ProbeSet ids
+    $object{Database} = ArrayExpress::ArrayMAGE::Database->new();
+    $object{Database}->add({ tag => 'netaffx' });
+
+    return ( \%object, $reporter_group, $composite_group, $reporter_group_id,
+        $composite_group_id );
+}
+
+sub parse_unit {
+
+    my ( $lines, $object, $reporter_group, $composite_group, $cdf ) = @_;
+
+    # Get the probeset name - useful for SNP arrays
+    my ($unit_probeset) = ( $lines->[1] =~ m/\A Name= ([\w\#\/\-\+\.\,]+) \z/xms );
+    my ($unit_type)     = ( $lines->[6] =~ m/\A UnitType=         (\d+) \z/xms );
+    my ($number_blocks) = ( $lines->[7] =~ m/\A NumberBlocks=     (\d+) \z/xms );
+
+    # Add all the probeset reporters.
+    # $previous_atom only scoped at this level for SNP chips, as those
+    # CDFs often have more than one block per unit.
+    my ( %reporter, %mismatch, $previous_atom );
+    my $compseq = {};
+    foreach my $blocknum ( 1 .. $number_blocks ) {
+        my $chunk = <$cdf>;
+        $chunk =~ s/^($CDF_LINEBREAK)*//;
+        my @lines = split /$CDF_LINEBREAK/, $chunk;
+
+        # $compseq should be a hash of probeset ids => [arrayref of
+        # reporter ids]
+        if ( $unit_type == 3 ) {
+
+            # Gene Expression chip
+            $compseq
+                = parse_ge_block( \@lines, \%reporter, \%mismatch, $object );
+        }
+        elsif ( $unit_type == 2 ) {
+
+            # SNP chip
+            $previous_atom
+                = parse_snp_block( \@lines, \%reporter, \%mismatch, $object,
+                $unit_probeset, $previous_atom );
+            $compseq->{$unit_probeset} = [ keys %reporter ];
+        }
+        elsif ( $unit_type == 7 ) {
+
+            # TAG/GeneFlex chip
+            $compseq
+                = parse_tag_block( \@lines, \%reporter, \%mismatch, $object );
+        }
+        elsif ( $unit_type == 0 ) {
+
+            # Control blocks as found in TAG chip CDFs
+            $compseq = parse_control_block( \@lines, \%reporter, \%mismatch,
+                $object );
+        }
+        elsif ( $unit_type == 1 ) {
+            die("UnitType 1 (resequencing) not yet supported.");
+        }
+        else {
+            die("Unrecognised unit type $unit_type ($unit_probeset).");
+        }
+    }
+
+    # Post-process all the reporters in the compseq (probeset).
+    foreach my $id ( sort keys %reporter ) {
+        $object->{Reporter}->add({
+            identifier   => $id,
+            name         => $reporter{$id}{name},
+            biosequences => $reporter{$id}{biosequences},
+        });
+        $object->{FeatureReporterMap}->add({
+            identifier    => $id,
+            features      => $reporter{$id}{features},
+            mismatch_info => \%mismatch,
+        });
+        $reporter_group->add({ id_ref => $id, });
+    }
+
+    foreach my $id ( sort keys %$compseq ) {
+
+        # Add the compseq (probeset) info.
+	$object->{BioSequence}->add({
+	    identifier  => $id,
+	    polymertype => 'nucleic_acid',
+	    type        => 'located_sequence_feature',
+	    dbrefs      => { $id => 'netaffx' },
+	});
+        $object->{CompositeSequence}->add({
+            identifier   => $id,
+            name         => $id,
+	    biosequences => [$id],
+	    cs_only      => $CS_ONLY,
+        });
+        $object->{ReporterCompositeMap}->add({
+            identifier => $id,
+            reporters  => [ sort @{ $compseq->{$id} } ],
+        });
+        $composite_group->add({ id_ref => $id });
+    }
+
+    return;
+}
+
+sub parse_control_block {
+
+    my ( $lines, $reporter, $mismatch, $object ) = @_;
+
+    # TAG chip control units appear to also allow multiple probesets
+    # per block. Here we detect them by sequence.
+    my %probeset_cache;
+
+    # Track the sequences in the block.
+    my @sequences;
+
+    PROBE_LINE:
+    foreach my $line (@$lines) {
+
+        my ( $row, $col, $feature_id )
+            = add_feature( $line, $object->{Feature} );
+
+        next PROBE_LINE unless ( $row && $col );
+
+        my ($x,        $y,     $probe,      $feat,
+            $probeset, $expos, $pos,        $cbase,
+            $pbase,    $tbase, $atom,       $index,
+            $codonind, $codon, $regiontype, $region
+            )
+            = split /\s+/, $line;
+
+	# $reporter_id is empty string to prevent uninit warnings below.
+        my $reporter_id = q{};
+        my $biosequence_id;
+
+        # Sequence-based probe pair detection.
+        if ( length($probe) > 1 ) {
+
+            my $probe_substr = $probe;
+            substr( $probe_substr, $pos - 1, 1, q{} );
+
+            SEQUENCE:
+            foreach my $index ( 0 .. $#sequences ) {
+                my $prev_substr = $sequences[$index];
+                my $pm_base     = substr( $prev_substr, $pos - 1, 1, q{} );
+
+                # Where the context sequence and tbase match the
+                # PM probe, add the probe to that reporter.
+                if (   ( $probe_substr eq $prev_substr )
+                    && ( $tbase eq $pm_base ) ) {
+                    $reporter_id    = "$probeset:ProbePair$index";
+                    $biosequence_id = "$probeset:$index";
+                    last SEQUENCE;
+                }
+            }
+
+            unless ( ( $reporter_id && $biosequence_id )
+			 || $CS_ONLY ) {
+
+                # In case we see the MM probe first, rebuild the
+                # PM probe sequence.
+                my $pm_probe = $probe;
+                substr( $pm_probe, $pos - 1, 1, $tbase );
+                push( @sequences, $pm_probe );
+                $reporter_id    = "$probeset:ProbePair" . $#sequences;
+                $biosequence_id = "$probeset:" . $#sequences;
+
+                $object->{BioSequence}->add({
+                    identifier  => $biosequence_id,
+                    polymertype => 'DNA',
+                    type        => 'ss_oligo',
+                    sequence    => $pm_probe,
+                    dbrefs      => { $probeset => 'netaffx' },
+                });
+                push(
+                    @{ $reporter->{$reporter_id}{biosequences} },
+                    $biosequence_id
+                );
+            }
+        }
+        else {
+            die("Error: UnitType 0 parsing only supported for units with sequence."
+            );
+        }
+
+        # All probes need a feature and a reporter
+        push( @{ $reporter->{$reporter_id}{features} }, $feature_id );
+        $reporter->{$reporter_id}{name} = $reporter_id;
+
+        # Cache the probeset -> probepair mapping for later.
+        $probeset_cache{$probeset}{$reporter_id}++;
+
+        unless ( $pbase eq $tbase ) {
+
+            # MM probe, just record the mismatch info.
+            $mismatch->{$feature_id} = {
+                start_coord     => $pos,
+                new_sequence    => lc($pbase),
+                replaced_length => 1,
+            };
+        }
+    }
+    my %compseq;
+    while ( my ( $id, $reporter_id_hash ) = each %probeset_cache ) {
+        $compseq{$id} = [ keys %$reporter_id_hash ];
+    }
+    return \%compseq;
+}
+
+sub parse_tag_block {
+
+    my ( $lines, $reporter, $mismatch, $object ) = @_;
+
+    # Track the probe pairs with this variable.
+    my $previous_probeset = q{};
+    my %probeset_cache;
+
+    PROBE_LINE:
+    foreach my $line (@$lines) {
+
+        my ( $row, $col, $feature_id )
+            = add_feature( $line, $object->{Feature} );
+
+        next PROBE_LINE unless ( $row && $col );
+
+        my ($x,        $y,     $probe,      $feat,
+            $probeset, $expos, $pos,        $cbase,
+            $pbase,    $tbase, $atom,       $index,
+            $codonind, $codon, $regiontype, $region
+            )
+            = split /\s+/, $line;
+
+        my $reporter_id    = "$probeset:ProbePair$atom";
+        my $biosequence_id = "$probeset:$atom";
+        $probeset_cache{$probeset}{$reporter_id}++;
+
+        # All probes need a feature and a reporter
+        push( @{ $reporter->{$reporter_id}{features} }, $feature_id );
+        $reporter->{$reporter_id}{name} = $reporter_id;
+
+        # TAG probesets can vary within blocks; don't add BS if probe
+        # seq not known.
+        unless ( $probeset eq $previous_probeset
+		     || length($probe) <= 1
+		     || $CS_ONLY ) {
+
+            # We rebuild the PM probe here, in case the MM probe
+            # comes first.
+            my $pm_probe = $probe;
+            substr( $pm_probe, $pos - 1, 1, $tbase ) if $pm_probe;
+
+            $object->{BioSequence}->add({
+                identifier  => $biosequence_id,
+                polymertype => 'DNA',
+                type        => 'ss_oligo',
+                sequence    => $pm_probe,
+                dbrefs      => { $probeset => 'netaffx' },
+            });
+            push(
+                @{ $reporter->{$reporter_id}{biosequences} },
+                $biosequence_id
+            );
+        }
+
+        # Sort out match and mismatch.
+        unless ( $pbase eq $tbase ) {
+
+            # MM probe, just record the mismatch info.
+            $mismatch->{$feature_id} = {
+                start_coord     => $pos,
+                new_sequence    => lc($pbase),
+                replaced_length => 1,
+            };
+        }
+
+        $previous_probeset = $probeset;
+    }
+
+    my %compseq;
+    while ( my ( $id, $reporter_id_hash ) = each %probeset_cache ) {
+        $compseq{$id} = [ keys %$reporter_id_hash ];
+    }
+    return \%compseq;
+}
+
+sub parse_snp_block {
+
+    my ( $lines, $reporter, $mismatch, $object, $probeset, $previous_atom )
+        = @_;
+
+    foreach my $line (@$lines) {
+        $previous_atom
+            = parse_probeline( $line, $probeset, $reporter, $mismatch,
+            $object, $previous_atom );
+    }
+    return $previous_atom;
+}
+
+sub parse_ge_block {
+
+    my ( $lines, $reporter, $mismatch, $object ) = @_;
+
+    # Probeset name stored in the block, not the unit, for GE units.
+    my $probeset;
+    if ( $lines->[0] =~ m/^\[Unit\d+_Block\d+\]$/ ) {
+
+        # Get the block name if the unit name was not set.
+        ($probeset) = ( $lines->[1] =~ m/\A Name= ([\w\#\/\-\+\.\,]+) \z/xms );
+    }
+    unless ($probeset) {
+        die(qq{Error: Unparseable block starts with line $lines->[0].});
+    }
+
+    # Track the probe pairs with this variable. Must start as undef.
+    my $previous_atom;
+
+    foreach my $line (@$lines) {
+        $previous_atom
+            = parse_probeline( $line, $probeset, $reporter, $mismatch,
+            $object, $previous_atom );
+    }
+    return { $probeset => [ keys %$reporter ] };
+}
+
+sub parse_probeline {
+
+    my ( $line, $probeset, $reporter, $mismatch, $object, $previous_atom )
+        = @_;
+
+    my ( $row, $col, $feature_id ) = add_feature( $line, $object->{Feature} );
+
+    return $previous_atom unless ( $row && $col );
+
+    my ($x,        $y,     $probe,      $feat,  $qual, $expos,
+        $pos,      $cbase, $pbase,      $tbase, $atom, $index,
+        $codonind, $codon, $regiontype, $region
+        )
+        = split /\s+/, $line;
+
+    my $reporter_id = "$probeset:ProbePair$atom";
+    my $biosequence_id;
+
+    # All probes need a feature and a reporter
+    push( @{ $reporter->{$reporter_id}{features} }, $feature_id );
+    $reporter->{$reporter_id}{name} = $reporter_id;
+
+    # Create the BioSequences where sequence known.
+    if ( length($probe) > 1 && ! $CS_ONLY ) {
+
+        $biosequence_id = "$probeset:$atom";
+
+        # Sequence given, first probe in the pair.
+        unless ( defined($previous_atom) && ( $atom eq $previous_atom ) ) {
+
+            # We rebuild the PM probe here, in case the MM probe
+            # comes first.
+            my $pm_probe = $probe;
+            substr( $pm_probe, $pos - 1, 1, $tbase ) if $pm_probe;
+
+            $object->{BioSequence}->add({
+                identifier  => $biosequence_id,
+                polymertype => 'DNA',
+                type        => 'ss_oligo',
+                sequence    => $pm_probe,
+                dbrefs      => { $probeset => 'netaffx' },
+            });
+
+	    # Reference the BS once for each atom.
+	    push( @{ $reporter->{$reporter_id}{biosequences} }, $biosequence_id );
+        }
+    }
+
+    # Sort out match and mismatch.
+    unless ( $pbase eq $tbase ) {
+
+        # MM probe, just record the mismatch info.
+        $mismatch->{$feature_id} = {
+            start_coord     => $pos,
+            new_sequence    => lc($pbase),
+            replaced_length => 1,
+        };
+    }
+
+    return $atom;
+}
+
+sub add_feature {
+    my ( $line, $mageobj ) = @_;
+
+    if ( my ( $row, $col ) = ( $line =~ m/Cell\d+=(\d+)\s+(\d+)/ ) ) {
+        my $feature_id = "Probe($row,$col)";
+        $mageobj->add({
+            identifier => $feature_id,
+            metacolumn => 1,
+            metarow    => 1,
+            column     => $col,
+            row        => $row,
+        });
+        return ( $row, $col, $feature_id );
+    }
+    return;
+}
+
+########
+# MAIN #
+########
+
+# Get our arguments
+my $args = parse_args();
+
+# Initialise some stuff
+my $starttime = new Benchmark;
+
+# Get the line endings.
+warn("Checking CDF linebreaks...\n");
+my $linebreak_counts;
+($linebreak_counts, $CDF_LINEBREAK) = check_linebreaks( $args->{cdf} );
+
+unless ($CDF_LINEBREAK) {
+    use Data::Dumper;
+    print Dumper $linebreak_counts;
+    die("Error: inconsistent line breaks in CDF.\n");
+}
+
+# Open the CDF and scan down to the header row
+open( my $cdf, "<", $args->{cdf} )
+    or die("Error: unable to open CDF file $args->{cdf}: $!\n");
+
+my ( $object, $reporter_group, $composite_group, $design_id );
+my $reporter_group_id  = 'All';
+my $composite_group_id = 'All';
+
+{
+
+    # Paragraph mode.
+    local $INPUT_RECORD_SEPARATOR = $CDF_LINEBREAK . $CDF_LINEBREAK;
+
+    my ( $total_units, $progress_bar );
+
+    # Parse the header.
+    HEADER_LINE:
+    while ( my $chunk = <$cdf> ) {
+
+        # Strip leading empty lines
+        $chunk =~ s/^($CDF_LINEBREAK)*//;
+        my @lines = split /$CDF_LINEBREAK/, $chunk;
+
+        next unless @lines;
+
+        if ( $lines[0] eq q{[Chip]} ) {
+            ($design_id)   = ( $lines[1] =~ /^Name=(.*)$/ );
+            ($total_units) = ( $lines[4] =~ /^NumberOfUnits=(\d+)$/ );
+            die("Error: Unable to parse number of units from [Chip] section")
+                unless defined($total_units);
+	    ArrayExpress::ArrayMAGE->set_design( $design_id );
+            $progress_bar = ArrayExpress::ArrayMAGE::ProgressBar->new(
+                width   => 40,
+                max_val => $total_units
+            );
+            last HEADER_LINE;
+        }
+    }
+
+    # Quick sanity check.
+    unless ( defined($total_units) ) {
+	die("Error parsing [Chip] section.\n");
+    }
+
+    # Initialize the MAGE objects (needs $design_id to be set)
+    if ( $CS_ONLY ) {
+
+	# More lightweight CS-only MAGE-ML to be generated.
+	( $object, $reporter_group, $composite_group )
+	    = cs_initialize(
+		$args->{accession},
+		$reporter_group_id,
+		$composite_group_id,
+		$design_id,
+	    );
+
+    }
+    else {
+
+	# Business as usual (full MAGE-ML generation).
+	( $object, $reporter_group, $composite_group )
+	    = initialize(
+		$args->{accession},
+		$reporter_group_id,
+		$composite_group_id,
+		$design_id,
+	    );
+    }
+
+    # Parse the rest of the file.
+    my $unit_num;
+    while ( my $chunk = <$cdf> ) {
+
+        # Strip leading empty lines
+        $chunk =~ s/^($CDF_LINEBREAK)*//;
+        my @lines = split /$CDF_LINEBREAK/, $chunk;
+
+        next unless @lines;
+
+        if ( $lines[0] =~ m/^\[QC\d+\]$/ ) {
+
+            # QC cell
+            foreach my $line (@lines) {
+                add_feature( $line, $object->{Feature} );
+            }
+        }
+        elsif ( $lines[0] =~ m/^\[Unit\d+\]$/ ) {
+
+            $progress_bar->draw( $unit_num++ ) if $progress_bar;
+
+            parse_unit( \@lines, $object, $reporter_group, $composite_group,
+                $cdf );
+        }
+    }
+}
+
+close $cdf;
+
+my $rg_hashref = $CS_ONLY
+               ? {}
+               : { $reporter_group_id  => $reporter_group };
+ArrayExpress::ArrayMAGE->combine_parts(
+    $object,
+    $rg_hashref,
+    { $composite_group_id => $composite_group },
+    $args->{output},
+);
+
+print STDOUT ("Done.\n");
+
+my $endtime = new Benchmark;
+print STDOUT (
+    "\nTotal run time = ",
+    timestr( timediff( $endtime, $starttime ) ), "\n\n"
+);
diff --git a/automation/admin/CHANGELOG b/automation/admin/CHANGELOG
new file mode 100644
index 0000000..cc0cabe
--- /dev/null
+++ b/automation/admin/CHANGELOG
@@ -0,0 +1,604 @@
+*0.13.1* (11 July, 2005)
+
+* Fixed that each request with the WEBrick adapter would open a new database connection #1685 [Sam Stephenson]
+
+* Added support for SQL Server in the database rake tasks #1652 [ken.barker at gmail.com] Note: osql and scptxfr may need to be installed on your development environment. This involves getting the .exes and a .rll (scptxfr) from a production SQL Server (not developer level SQL Server). Add their location to your Environment PATH and you are all set.
+
+* Added a VERSION parameter to the migrate task that allows you to do "rake migrate VERSION=34" to migrate to the 34th version traveling up or down depending on the current version
+
+* Extend Ruby version check to include RUBY_RELEASE_DATE >= '2005-12-25', the final Ruby 1.8.2 release #1674 [court3nay at gmail.com]
+
+* Improved documentation for environment config files #1625 [court3nay at gmail.com]
+
+
+*0.13.0* (6 July, 2005)
+
+* Changed the default logging level in config/environment.rb to INFO for production (so SQL statements won't be logged)
+
+* Added migration generator: ./script/generate migration add_system_settings
+
+* Added "migrate" as rake task to execute all the pending migrations from db/migrate
+
+* Fixed that model generator would make fixtures plural, even if ActiveRecord::Base.pluralize_table_names was false #1185 [Marcel Molina]
+
+* Added a DOCTYPE of HTML transitional to the HTML files generated by Rails #1124 [Michael Koziarski]
+
+* SIGTERM also gracefully exits dispatch.fcgi.  Ignore SIGUSR1 on Windows.
+
+* Add the option to manually manage garbage collection in the FastCGI dispatcher.  Set the number of requests between GC runs in your public/dispatch.fcgi [skaes at web.de]
+
+* Allow dynamic application reloading for dispatch.fcgi processes by sending a SIGHUP. If the process is currently handling a request, the request will be allowed to complete first. This allows production fcgi's to be reloaded without having to restart them.
+
+* RailsFCGIHandler (dispatch.fcgi) no longer tries to explicitly flush $stdout (CgiProcess#out always calls flush)
+
+* Fixed rakefile actions against PostgreSQL when the password is all numeric #1462 [michael at schubert.cx]
+
+* ActionMailer::Base subclasses are reloaded with the other rails components #1262
+
+* Made the WEBrick adapter not use a mutex around action performance if ActionController::Base.allow_concurrency is true (default is false)
+
+* Fixed that mailer generator generated fixtures/plural while units expected fixtures/singular #1457 [Scott Barron]
+
+* Added a 'whiny nil' that's aim to ensure that when users pass nil to methods where that isn't appropriate, instead of NoMethodError? and the name of some method used by the framework users will see a message explaining what type of object was expected. Only active in test and development environments by default #1209 [Michael Koziarski]
+
+* Fixed the test_helper.rb to be safe for requiring controllers from multiple spots, like app/controllers/article_controller.rb and app/controllers/admin/article_controller.rb, without reloading the environment twice #1390 [Nicholas Seckar]
+
+* Fixed Webrick to escape + characters in URL's the same way that lighttpd and apache do #1397 [Nicholas Seckar]
+
+* Added -e/--environment option to script/runner #1408 [fbeausoleil at ftml.net]
+
+* Modernize the scaffold generator to use the simplified render and test methods and to change style from @params["id"] to params[:id].  #1367
+
+* Added graceful exit from pressing CTRL-C during the run of the rails command #1150 [Caleb Tennis]
+
+* Allow graceful exits for dispatch.fcgi processes by sending a SIGUSR1. If the process is currently handling a request, the request will be allowed to complete and then will terminate itself. If a request is not being handled, the process is terminated immediately (via #exit). This basically works like restart graceful on Apache. [Jamis Buck]
+
+* Made dispatch.fcgi more robust by catching fluke errors and retrying unless its a permanent condition. [Jamis Buck]
+
+* Added console --profile for profiling an IRB session #1154 [Jeremy Kemper]
+
+* Changed console_sandbox into console --sandbox #1154 [Jeremy Kemper]
+
+
+*0.12.1* (20th April, 2005)
+
+* Upgraded to Active Record 1.10.1, Action Pack 1.8.1, Action Mailer 0.9.1, Action Web Service 0.7.1
+
+
+*0.12.0* (19th April, 2005)
+
+* Fixed that purge_test_database would use database settings from the development environment when recreating the test database #1122 [rails at cogentdude.com]
+
+* Added script/benchmarker to easily benchmark one or more statement a number of times from within the environment. Examples:
+
+    # runs the one statement 10 times
+    script/benchmarker 10 'Person.expensive_method(10)'
+    
+    # pits the two statements against each other with 50 runs each
+    script/benchmarker 50 'Person.expensive_method(10)' 'Person.cheap_method(10)'
+
+* Added script/profiler to easily profile a single statement from within the environment. Examples:
+
+    script/profiler 'Person.expensive_method(10)'
+    script/profiler 'Person.expensive_method(10)' 10 # runs the statement 10 times
+
+* Added Rake target clear_logs that'll truncate all the *.log files in log/ to zero #1079 [Lucas Carlson]
+
+* Added lazy typing for generate, such that ./script/generate cn == ./script/generate controller and the likes #1051 [k at v2studio.com]
+
+* Fixed that ownership is brought over in pg_dump during tests for PostgreSQL #1060 [pburleson at gmail.com]
+
+* Upgraded to Active Record 1.10.0, Action Pack 1.8.0, Action Mailer 0.9.0, Action Web Service 0.7.0, Active Support 1.0.4
+
+
+*0.11.1* (27th March, 2005)
+
+* Fixed the dispatch.fcgi use of a logger
+
+* Upgraded to Active Record 1.9.1, Action Pack 1.7.0, Action Mailer 0.8.1, Action Web Service 0.6.2, Active Support 1.0.3
+
+
+*0.11.0* (22th March, 2005)
+
+* Removed SCRIPT_NAME from the WEBrick environment to prevent conflicts with PATH_INFO #896 [Nicholas Seckar]
+
+* Removed ?$1 from the dispatch.f/cgi redirect line to get rid of 'complete/path/from/request.html' => nil being in the @params now that the ENV["REQUEST_URI"] is used to determine the path #895 [dblack/Nicholas Seckar]
+
+* Added additional error handling to the FastCGI dispatcher to catch even errors taking down the entire process
+
+* Improved the generated scaffold code a lot to take advantage of recent Rails developments #882 [Tobias Luetke]
+
+* Combined the script/environment.rb used for gems and regular files version. If vendor/rails/* has all the frameworks, then files version is used, otherwise gems #878 [Nicholas Seckar]
+
+* Changed .htaccess to allow dispatch.* to be called from a sub-directory as part of the push with Action Pack to make Rails work on non-vhost setups #826 [Nicholas Seckar/Tobias Luetke]
+
+* Added script/runner which can be used to run code inside the environment by eval'ing the first parameter. Examples:
+    
+    ./script/runner 'ReminderService.deliver'
+    ./script/runner 'Mailer.receive(STDIN.read)'
+  
+  This makes it easier to do CRON and postfix scripts without actually making a script just to trigger 1 line of code.
+
+* Fixed webrick_server cookie handling to allow multiple cookes to be set at once #800, #813 [dave at cherryville.org]
+
+* Fixed the Rakefile's interaction with postgresql to:
+
+    1. Use PGPASSWORD and PGHOST in the environment to fix prompting for 
+       passwords when connecting to a remote db and local socket connections. 
+    2. Add a '-x' flag to pg_dump which stops it dumping privileges #807 [rasputnik]
+    3. Quote the user name and use template0 when dumping so the functions doesn't get dumped too #855 [pburleson]
+    4. Use the port if available #875 [madrobby]
+
+* Upgraded to Active Record 1.9.0, Action Pack 1.6.0, Action Mailer 0.8.0, Action Web Service 0.6.1, Active Support 1.0.2
+
+
+*0.10.1* (7th March, 2005)
+
+* Fixed rake stats to ignore editor backup files like model.rb~ #791 [skanthak]
+
+* Added exception shallowing if the DRb server can't be started (not worth making a fuss about to distract new users) #779 [Tobias Luetke]
+
+* Added an empty favicon.ico file to the public directory of new applications (so the logs are not spammed by its absence)
+
+* Fixed that scaffold generator new template should use local variable instead of instance variable #778 [Dan Peterson]
+
+* Allow unit tests to run on a remote server for PostgreSQL #781 [adamm at galacticasoftware.com]
+
+* Added web_service generator (run ./script/generate web_service for help) #776 [Leon Bredt]
+
+* Added app/apis and components to code statistics report #729 [Scott Barron]
+
+* Fixed WEBrick server to use ABSOLUTE_RAILS_ROOT instead of working_directory #687 [Nicholas Seckar]
+
+* Fixed rails_generator to be usable without RubyGems #686 [Cristi BALAN]
+
+* Fixed -h/--help for generate and destroy generators #331
+
+* Added begin/rescue around the FCGI dispatcher so no uncaught exceptions can bubble up to kill the process (logs to log/fastcgi.crash.log)
+
+* Fixed that association#count would produce invalid sql when called sequentialy #659 [kanis at comcard.de]
+
+* Fixed test/mocks/testing to the correct test/mocks/test #740
+
+* Added early failure if the Ruby version isn't 1.8.2 or above #735
+
+* Removed the obsolete -i/--index option from the WEBrick servlet #743
+
+* Upgraded to Active Record 1.8.0, Action Pack 1.5.1, Action Mailer 0.7.1, Action Web Service 0.6.0, Active Support 1.0.1
+
+
+*0.10.0* (24th February, 2005)
+
+* Changed default IP binding for WEBrick from 127.0.0.1 to 0.0.0.0 so that the server is accessible both locally and remotely #696 [Marcel]
+
+* Fixed that script/server -d was broken so daemon mode couldn't be used #687 [Nicholas Seckar]
+
+* Upgraded to breakpoint 92 which fixes:
+
+    * overload IRB.parse_opts(), fixes #443
+      => breakpoints in tests work even when running them via rake
+    * untaint handlers, might fix an issue discussed on the Rails ML
+    * added verbose mode to breakpoint_client
+    * less noise caused by breakpoint_client by default
+    * ignored TerminateLineInput exception in signal handler
+      => quiet exit on Ctrl-C
+
+* Added support for independent components residing in /components. Example:
+
+    Controller: components/list/items_controller.rb
+    (holds a List::ItemsController class with uses_component_template_root called)
+    
+    Model     : components/list/item.rb
+    (namespace is still shared, so an Item model in app/models will take precedence)
+    
+    Views     : components/list/items/show.rhtml
+
+
+* Added --sandbox option to script/console that'll roll back all changes made to the database when you quit #672 [Jeremy Kemper]
+
+* Added 'recent' as a rake target that'll run tests for files that changed in the last 10 minutes #612 [Jeremy Kemper]
+
+* Changed script/console to default to development environment and drop --no-inspect #650 [Jeremy Kemper]
+
+* Added that the 'fixture :posts' syntax can be used for has_and_belongs_to_many fixtures where a model doesn't exist #572 [Jeremy Kemper]
+
+* Added that running test_units and test_functional now performs the clone_structure_to_test as well #566 [rasputnik]
+
+* Added new generator framework that informs about its doings on generation and enables updating and destruction of generated artifacts. See the new script/destroy and script/update for more details #487 [Jeremy Kemper]
+
+* Added Action Web Service as a new add-on framework for Action Pack [Leon Bredt]
+
+* Added Active Support as an independent utility and standard library extension bundle
+
+* Upgraded to Active Record 1.7.0, Action Pack 1.5.0, Action Mailer 0.7.0
+
+
+*0.9.5* (January 25th, 2005)
+
+* Fixed dependency reloading by switching to a remove_const approach where all Active Records, Active Record Observers, and Action Controllers are reloading by undefining their classes. This enables you to remove methods in all three types and see the change reflected immediately and it fixes #539. This also means that only those three types of classes will benefit from the const_missing and reloading approach. If you want other classes (like some in lib/) to reload, you must use require [...]
+
+* Added Florian Gross' latest version of Breakpointer and friends that fixes a variaty of bugs #441 [Florian Gross]
+
+* Fixed skeleton Rakefile to work with sqlite3 out of the box #521 [rasputnik]
+
+* Fixed that script/breakpointer didn't get the Ruby path rewritten as the other scripts #523 [brandt at kurowski.net]
+
+* Fixed handling of syntax errors in models that had already been succesfully required once in the current interpreter
+
+* Fixed that models that weren't referenced in associations weren't being reloaded in the development mode by reinstating the reload
+
+* Fixed that generate scaffold would produce bad functional tests
+
+* Fixed that FCGI can also display SyntaxErrors
+
+* Upgraded to Active Record 1.6.0, Action Pack 1.4.0
+
+
+*0.9.4.1* (January 18th, 2005)
+
+* Added 5-second timeout to WordNet alternatives on creating reserved-word models #501 [Marcel Molina]
+
+* Fixed binding of caller #496 [Alexey]
+
+* Upgraded to Active Record 1.5.1, Action Pack 1.3.1, Action Mailer 0.6.1
+
+
+*0.9.4* (January 17th, 2005)
+
+* Added that ApplicationController will catch a ControllerNotFound exception if someone attempts to access a url pointing to an unexisting controller [Tobias Luetke]
+
+* Flipped code-to-test ratio around to be more readable #468 [Scott Baron]
+
+* Fixed log file permissions to be 666 instead of 777 (so they're not executable) #471 [Lucas Carlson]
+
+* Fixed that auto reloading would some times not work or would reload the models twice #475 [Tobias Luetke]
+
+* Added rewrite rules to deal with caching to public/.htaccess
+
+* Added the option to specify a controller name to "generate scaffold" and made the default controller name the plural form of the model.
+
+* Added that rake clone_structure_to_test, db_structure_dump, and purge_test_database tasks now pick up the source database to use from
+  RAILS_ENV instead of just forcing development #424 [Tobias Luetke]
+
+* Fixed script/console to work with Windows (that requires the use of irb.bat) #418 [octopod]
+
+* Fixed WEBrick servlet slowdown over time by restricting the load path reloading to mod_ruby
+
+* Removed Fancy Indexing as a default option on the WEBrick servlet as it made it harder to use various caching schemes
+
+* Upgraded to Active Record 1.5, Action Pack 1.3, Action Mailer 0.6
+
+
+*0.9.3* (January 4th, 2005)
+
+* Added support for SQLite in the auto-dumping/importing of schemas for development -> test #416
+
+* Added automated rewriting of the shebang lines on installs through the gem rails command #379 [Manfred Stienstra]
+
+* Added ActionMailer::Base.deliver_method = :test to the test environment so that mail objects are available in ActionMailer::Base.deliveries
+  for functional testing.
+
+* Added protection for creating a model through the generators with a name of an existing class, like Thread or Date.
+  It'll even offer you a synonym using wordnet.princeton.edu as a look-up. No, I'm not kidding :) [Florian Gross]
+
+* Fixed dependency management to happen in a unified fashion for Active Record and Action Pack using the new Dependencies module. This means that
+  the environment options needs to change from:
+  
+    Before in development.rb:
+      ActionController::Base.reload_dependencies = true  
+      ActiveRecord::Base.reload_associations     = true
+    
+    Now in development.rb:
+      Dependencies.mechanism = :load
+
+    Before in production.rb and test.rb:
+      ActionController::Base.reload_dependencies = false
+      ActiveRecord::Base.reload_associations     = false
+
+    Now in production.rb and test.rb:
+      Dependencies.mechanism = :require
+
+* Fixed problems with dependency caching and controller hierarchies on Ruby 1.8.2 in development mode #351
+
+* Fixed that generated action_mailers doesnt need to require the action_mailer since thats already done in the environment #382 [Lucas Carlson]
+
+* Upgraded to Action Pack 1.2.0 and Active Record 1.4.0
+
+
+*0.9.2*
+
+* Fixed CTRL-C exists from the Breakpointer to be a clean affair without error dumping [Kent Sibilev]
+
+* Fixed "rake stats" to work with sub-directories in models and controllers and to report the code to test ration [Scott Baron]
+
+* Added that Active Record associations are now reloaded instead of cleared to work with the new const_missing hook in Active Record.
+
+* Added graceful handling of an inaccessible log file by redirecting output to STDERR with a warning #330 [rainmkr]
+
+* Added support for a -h/--help parameter in the generator #331 [Ulysses]
+
+* Fixed that File.expand_path in config/environment.rb would fail when dealing with symlinked public directories [mjobin]
+
+* Upgraded to Action Pack 1.1.0 and Active Record 1.3.0
+
+
+*0.9.1*
+
+* Upgraded to Action Pack 1.0.1 for important bug fix
+
+* Updated gem dependencies
+
+
+*0.9.0*
+
+* Renamed public/dispatch.servlet to script/server -- it wasn't really dispatching anyway as its delegating calls to public/dispatch.rb
+
+* Renamed AbstractApplicationController and abstract_application.rb to ApplicationController and application.rb, so that it will be possible
+  for the framework to automatically pick up on app/views/layouts/application.rhtml and app/helpers/application.rb
+
+* Added script/console that makes it even easier to start an IRB session for interacting with the domain model. Run with no-args to
+  see help.
+
+* Added breakpoint support through the script/breakpointer client. This means that you can break out of execution at any point in
+  the code, investigate and change the model, AND then resume execution! Example:
+  
+    class WeblogController < ActionController::Base
+      def index
+        @posts = Post.find_all
+        breakpoint "Breaking out from the list"
+      end
+    end
+    
+  So the controller will accept the action, run the first line, then present you with a IRB prompt in the breakpointer window. 
+  Here you can do things like:
+  
+  Executing breakpoint "Breaking out from the list" at .../webrick_server.rb:16 in 'breakpoint'
+
+    >> @posts.inspect
+    => "[#<Post:0x14a6be8 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>, 
+         #<Post:0x14a6620 @attributes={\"title\"=>\"Rails you know!\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]"
+    >> @posts.first.title = "hello from a breakpoint"
+    => "hello from a breakpoint"
+
+  ...and even better is that you can examine how your runtime objects actually work:
+
+    >> f = @posts.first 
+    => #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
+    >> f.
+    Display all 152 possibilities? (y or n)
+  
+  Finally, when you're ready to resume execution, you press CTRL-D
+
+* Changed environments to be configurable through an environment variable. By default, the environment is "development", but you
+  can change that and set your own by configuring the Apache vhost with a string like (mod_env must be available on the server):
+  
+    SetEnv RAILS_ENV production
+  
+  ...if you're using WEBrick, you can pick the environment to use with the command-line parameters -e/--environment, like this:
+  
+    ruby public/dispatcher.servlet -e production
+
+* Added a new default environment called "development", which leaves the production environment to be tuned exclusively for that.
+
+* Added a start_server in the root of the Rails application to make it even easier to get started
+
+* Fixed public/.htaccess to use RewriteBase and share the same rewrite rules for all the dispatch methods
+
+* Fixed webrick_server to handle requests in a serialized manner (the Rails reloading infrastructure is not thread-safe)
+
+* Added support for controllers in directories. So you can have:
+
+    app/controllers/account_controller.rb        # URL: /account/
+    app/controllers/admin/account_controller.rb  # URL: /admin/account/
+  
+  NOTE: You need to update your public/.htaccess with the new rules to pick it up
+
+* Added reloading for associations and dependencies under cached environments like FastCGI and mod_ruby. This makes it possible to use 
+  those environments for development. This is turned on by default, but can be turned off with 
+  ActiveRecord::Base.reload_associations = false and ActionController::Base.reload_dependencies = false in production environments.
+
+* Added support for sub-directories in app/models. So now you can have something like Basecamp with:
+
+    app/models/accounting
+    app/models/project
+    app/models/participants
+    app/models/settings
+  
+  It's poor man's namespacing, but only for file-system organization. You still require files just like before.
+  Nothing changes inside the files themselves.
+
+
+* Fixed a few references in the tests generated by new_mailer [Jeremy Kemper]
+
+* Added support for mocks in testing with test/mocks
+
+* Cleaned up the environments a bit and added global constant RAILS_ROOT
+
+
+*0.8.5* (9)
+
+* Made dev-util available to all tests, so you can insert breakpoints in any test case to get an IRB prompt at that point [Jeremy Kemper]:
+
+    def test_complex_stuff
+      @david.projects << @new_project
+      breakpoint "Let's have a closer look at @david"
+    end
+  
+  You need to install dev-utils yourself for this to work ("gem install dev-util").
+
+* Added shared generator behavior so future upgrades should be possible without manually copying over files [Jeremy Kemper]
+
+* Added the new helper style to both controller and helper templates [Jeremy Kemper]
+
+* Added new_crud generator for creating a model and controller at the same time with explicit scaffolding [Jeremy Kemper]
+
+* Added configuration of Test::Unit::TestCase.fixture_path to test_helper to concide with the new AR fixtures style
+
+* Fixed that new_model was generating singular table/fixture names
+
+* Upgraded to Action Mailer 0.4.0
+
+* Upgraded to Action Pack 0.9.5
+
+* Upgraded to Active Record 1.1.0
+
+
+*0.8.0 (15)*
+
+* Removed custom_table_name option for new_model now that the Inflector is as powerful as it is
+
+* Changed the default rake action to just do testing and separate API generation and coding statistics into a "doc" task.
+
+* Fixed WEBrick dispatcher to handle missing slashes in the URLs gracefully [alexey]
+
+* Added user option for all postgresql tool calls in the rakefile [elvstone]
+
+* Fixed problem with running "ruby public/dispatch.servlet" instead of "cd public; ruby dispatch.servlet" [alexey]
+
+* Fixed WEBrick server so that it no longer hardcodes the ruby interpreter used to "ruby" but will get the one used based
+  on the Ruby runtime configuration. [Marcel Molina Jr.]
+
+* Fixed Dispatcher so it'll route requests to magic_beans to MagicBeansController/magic_beans_controller.rb [Caio Chassot]
+
+* "new_controller MagicBeans" and "new_model SubscriptionPayments" will now both behave properly as they use the new Inflector.
+
+* Fixed problem with MySQL foreign key constraint checks in Rake :clone_production_structure_to_test target [Andreas Schwarz]
+
+* Changed WEBrick server to by default be auto-reloading, which is slower but makes source changes instant.
+  Class compilation cache can be turned on with "-c" or "--cache-classes".
+
+* Added "-b/--binding" option to WEBrick dispatcher to bind the server to a specific IP address (default: 127.0.0.1) [Kevin Temp]
+
+* dispatch.fcgi now DOESN'T set FCGI_PURE_RUBY as it was slowing things down for now reason [Andreas Schwarz]
+
+* Added new_mailer generator to work with Action Mailer
+
+* Included new framework: Action Mailer 0.3
+
+* Upgraded to Action Pack 0.9.0
+
+* Upgraded to Active Record 1.0.0
+
+
+*0.7.0*
+
+* Added an optional second argument to the new_model script that allows the programmer to specify the table name, 
+  which will used to generate a custom table_name method in the model and will also be used in the creation of fixtures.
+  [Kevin Radloff]
+
+* script/new_model now turns AccountHolder into account_holder instead of accountholder [Kevin Radloff]
+
+* Fixed the faulty handleing of static files with WEBrick [Andreas Schwarz]
+
+* Unified function_test_helper and unit_test_helper into test_helper
+
+* Fixed bug with the automated production => test database dropping on PostgreSQL [dhawkins]
+
+* create_fixtures in both the functional and unit test helper now turns off the log during fixture generation
+  and can generate more than one fixture at a time. Which makes it possible for assignments like:
+  
+    @people, @projects, @project_access, @companies, @accounts = 
+      create_fixtures "people", "projects", "project_access", "companies", "accounts"
+
+* Upgraded to Action Pack 0.8.5 (locally-scoped variables, partials, advanced send_file)
+
+* Upgraded to Active Record 0.9.5 (better table_name guessing, cloning, find_all_in_collection)
+
+
+*0.6.5*
+
+* No longer specifies a template for rdoc, so it'll use whatever is default (you can change it in the rakefile)
+
+* The new_model generator will now use the same rules for plural wordings as Active Record 
+  (so Category will give categories, not categorys) [Kevin Radloff]
+
+* dispatch.fcgi now sets FCGI_PURE_RUBY to true to ensure that it's the Ruby version that's loaded [danp]
+
+* Made the GEM work with Windows
+
+* Fixed bug where mod_ruby would "forget" the load paths added when switching between controllers
+
+* PostgreSQL are now supported for the automated production => test database dropping [Kevin Radloff]
+
+* Errors thrown by the dispatcher are now properly handled in FCGI.
+
+* Upgraded to Action Pack 0.8.0 (lots and lots and lots of fixes)
+
+* Upgraded to Active Record 0.9.4 (a bunch of fixes)
+
+
+*0.6.0*
+
+* Added AbstractionApplicationController as a superclass for all controllers generated. This class can be used
+  to carry filters and methods that are to be shared by all. It has an accompanying ApplicationHelper that all
+  controllers will also automatically have available.
+
+* Added environments that can be included from any script to get the full Active Record and Action Controller
+  context running. This can be used by maintenance scripts or to interact with the model through IRB. Example:
+  
+    require 'config/environments/production'
+    
+    for account in Account.find_all
+      account.recalculate_interests
+    end
+  
+  A short migration script for an account model that had it's interest calculation strategy changed.
+
+* Accessing the index of a controller with "/weblog" will now redirect to "/weblog/" (only on Apache, not WEBrick)
+
+* Simplified the default Apache config so even remote requests are served off CGI as a default.
+  You'll now have to do something specific to activate mod_ruby and FCGI (like using the force urls).
+  This should make it easier for new comers that start on an external server.
+
+* Added more of the necessary Apache options to .htaccess to make it easier to setup
+
+* Upgraded to Action Pack 0.7.9 (lots of fixes)
+
+* Upgraded to Active Record 0.9.3 (lots of fixes)
+
+
+*0.5.7*
+
+* Fixed bug in the WEBrick dispatcher that prevented it from getting parameters from the URL
+  (through GET requests or otherwise)
+
+* Added lib in root as a place to store app specific libraries
+
+* Added lib and vendor to load_path, so anything store within can be loaded directly. 
+  Hence lib/redcloth.rb can be loaded with require "redcloth"
+
+* Upgraded to Action Pack 0.7.8 (lots of fixes)
+
+* Upgraded to Active Record 0.9.2 (minor upgrade)
+
+
+*0.5.6*
+
+* Upgraded to Action Pack 0.7.7 (multipart form fix)
+
+* Updated the generated template stubs to valid XHTML files
+
+* Ensure that controllers generated are capitalized, so "new_controller TodoLists" 
+  gives the same as "new_controller Todolists" and "new_controller todolists".
+
+
+*0.5.5*
+
+* Works on Windows out of the box! (Dropped symlinks)
+
+* Added webrick dispatcher: Try "ruby public/dispatch.servlet --help" [Florian Gross]
+
+* Report errors about initialization to browser (instead of attempting to use uninitialized logger)
+
+* Upgraded to Action Pack 0.7.6
+
+* Upgraded to Active Record 0.9.1
+
+* Added distinct 500.html instead of reusing 404.html
+
+* Added MIT license
+
+
+*0.5.0*
+
+* First public release
diff --git a/automation/admin/INSTALL b/automation/admin/INSTALL
new file mode 100644
index 0000000..b5a13b4
--- /dev/null
+++ b/automation/admin/INSTALL
@@ -0,0 +1,68 @@
+AutoSubmissions installation notes
+==================================
+
+These are installation notes for the MySQL/Ruby on Rails automated
+submissions system. There is a set of Perl modules installed as part
+of the main Tab2MAGE installation which are required for the CGI web
+submissions form and automated processing daemons; this document
+covers database initialization, and setup and invocation of the
+curation submissions tracking web interface.
+
+Prerequisites
+-------------
+
+First, install ruby, rubygems and rails. See http://rubyforge.org and
+http://www.rubyonrails.com for details on how to do this. You will
+also need to install MySQL (version 4.1 or 5.0 is recommended; see
+http://www.mysql.org).
+
+You will also most likely need to install the native MySQL interface
+for Ruby. This is most easily accomplished using the rubygems system:
+
+gem install -y mysql -v 2.7 -- --with-mysql-config /the/path/to/your/mysql-config/executable
+
+Database setup
+--------------
+
+Creation of the MySQL database and suitable login privileges is left
+to the user. To set up the database tables, switch to the db/
+directory and issue the following command:
+
+mysql your_database_name < database-mysql.schema
+
+This only creates the tables. Alternatively, you can create a database
+populated with some initial values for experiment annotation and
+default users:
+
+mysql your_database_name < database-mysql.dump
+
+Your database details must be entered in the config/database.yml file;
+See the file, database.yml-mysql, for an example.
+
+Webserver setup
+---------------
+
+To start the included WEBrick server, type:
+
+ruby scripts/server webrick
+
+Now navigate to http://localhost:3000/ to check that everything is
+working.
+
+If you have installed the LightTPD web server, you should be able to
+use it in full backgrounded daemon mode like this:
+
+ruby scripts/server -d
+
+In this case the webserver will be available at
+http://localhost:8087/, with configuration options set in the
+config/lighttpd.conf file.
+
+Once running, please use the user account 'admin' (password 'admin')
+to access all parts of the application. Low-privilege users may be
+added using the web interface; curator- and admin-level users must be
+created by modifying the roles_users table directly in the MySQL
+database. Examples are included in the database-mysql.dump file.
+
+---------------------------------------------------------------------
+Copyright Tim Rayner <rayner at ebi.ac.uk> ArrayExpress Team, EBI, 2007.
diff --git a/automation/admin/README b/automation/admin/README
new file mode 100644
index 0000000..cd9d0ff
--- /dev/null
+++ b/automation/admin/README
@@ -0,0 +1,153 @@
+== Welcome to Rails
+
+Rails is a web-application and persistence framework that includes everything
+needed to create database-backed web-applications according to the
+Model-View-Control pattern of separation. This pattern splits the view (also
+called the presentation) into "dumb" templates that are primarily responsible
+for inserting pre-built data in between HTML tags. The model contains the
+"smart" domain objects (such as Account, Product, Person, Post) that holds all
+the business logic and knows how to persist themselves to a database. The
+controller handles the incoming requests (such as Save New Account, Update
+Product, Show Post) by manipulating the model and directing data to the view.
+
+In Rails, the model is handled by what's called an object-relational mapping
+layer entitled Active Record. This layer allows you to present the data from
+database rows as objects and embellish these data objects with business logic
+methods. You can read more about Active Record in 
+link:files/vendor/rails/activerecord/README.html.
+
+The controller and view are handled by the Action Pack, which handles both
+layers by its two parts: Action View and Action Controller. These two layers
+are bundled in a single package due to their heavy interdependence. This is
+unlike the relationship between the Active Record and Action Pack that is much
+more separate. Each of these packages can be used independently outside of
+Rails.  You can read more about Action Pack in 
+link:files/vendor/rails/actionpack/README.html.
+
+
+== Getting started
+
+1. Run the WEBrick servlet: <tt>ruby script/server</tt> (run with --help for options)
+   ...or if you have lighttpd installed: <tt>ruby script/lighttpd</tt> (it's faster)
+2. Go to http://localhost:3000/ and get "Congratulations, you've put Ruby on Rails!"
+3. Follow the guidelines on the "Congratulations, you've put Ruby on Rails!" screen
+
+
+== Example for Apache conf
+
+  <VirtualHost *:80>
+    ServerName rails
+    DocumentRoot /path/application/public/
+    ErrorLog /path/application/log/server.log
+  
+    <Directory /path/application/public/>
+      Options ExecCGI FollowSymLinks
+      AllowOverride all
+      Allow from all
+      Order allow,deny
+    </Directory>
+  </VirtualHost>
+
+NOTE: Be sure that CGIs can be executed in that directory as well. So ExecCGI
+should be on and ".cgi" should respond. All requests from 127.0.0.1 go
+through CGI, so no Apache restart is necessary for changes. All other requests
+go through FCGI (or mod_ruby), which requires a restart to show changes.
+
+
+== Debugging Rails
+
+Have "tail -f" commands running on both the server.log, production.log, and
+test.log files. Rails will automatically display debugging and runtime
+information to these files. Debugging info will also be shown in the browser
+on requests from 127.0.0.1.
+
+
+== Breakpoints
+
+Breakpoint support is available through the script/breakpointer client. This
+means that you can break out of execution at any point in the code, investigate
+and change the model, AND then resume execution! Example:
+
+  class WeblogController < ActionController::Base
+    def index
+      @posts = Post.find_all
+      breakpoint "Breaking out from the list"
+    end
+  end
+  
+So the controller will accept the action, run the first line, then present you
+with a IRB prompt in the breakpointer window. Here you can do things like:
+
+Executing breakpoint "Breaking out from the list" at .../webrick_server.rb:16 in 'breakpoint'
+
+  >> @posts.inspect
+  => "[#<Post:0x14a6be8 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>, 
+       #<Post:0x14a6620 @attributes={\"title\"=>\"Rails you know!\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]"
+  >> @posts.first.title = "hello from a breakpoint"
+  => "hello from a breakpoint"
+
+...and even better is that you can examine how your runtime objects actually work:
+
+  >> f = @posts.first 
+  => #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
+  >> f.
+  Display all 152 possibilities? (y or n)
+
+Finally, when you're ready to resume execution, you press CTRL-D
+
+
+== Console
+
+You can interact with the domain model by starting the console through script/console. 
+Here you'll have all parts of the application configured, just like it is when the
+application is running. You can inspect domain models, change values, and save to the
+database. Starting the script without arguments will launch it in the development environment.
+Passing an argument will specify a different environment, like <tt>console production</tt>.
+
+
+== Description of contents
+
+app
+  Holds all the code that's specific to this particular application.
+
+app/controllers
+  Holds controllers that should be named like weblog_controller.rb for
+  automated URL mapping. All controllers should descend from
+  ActionController::Base.
+
+app/models
+  Holds models that should be named like post.rb.
+  Most models will descend from ActiveRecord::Base.
+  
+app/views
+  Holds the template files for the view that should be named like
+  weblog/index.rhtml for the WeblogController#index action. All views use eRuby
+  syntax. This directory can also be used to keep stylesheets, images, and so on
+  that can be symlinked to public.
+  
+app/helpers
+  Holds view helpers that should be named like weblog_helper.rb.
+
+config
+  Configuration files for the Rails environment, the routing map, the database, and other dependencies.
+
+components
+  Self-contained mini-applications that can bundle together controllers, models, and views.
+
+lib
+  Application specific libraries. Basically, any kind of custom code that doesn't
+  belong under controllers, models, or helpers. This directory is in the load path.
+    
+public
+  The directory available for the web server. Contains subdirectories for images, stylesheets,
+  and javascripts. Also contains the dispatchers and the default HTML files.
+
+script
+  Helper scripts for automation and generation.
+
+test
+  Unit and functional tests along with fixtures.
+
+vendor
+  External libraries that the application depends on. Also includes the plugins subdirectory.
+  This directory is in the load path.
diff --git a/automation/admin/README_LOGIN b/automation/admin/README_LOGIN
new file mode 100644
index 0000000..7cae805
--- /dev/null
+++ b/automation/admin/README_LOGIN
@@ -0,0 +1,96 @@
+== Installation
+
+Done generating the login system. but there are still a few things you have to
+do manually. First open your application.rb and add
+
+  require_dependency "login_system"
+
+to the top of the file and include the login system with
+
+  include LoginSystem 
+
+The beginning of your ApplicationController.
+It should look something like this : 
+
+  require_dependency "login_system"
+
+  class ApplicationController < ActionController::Base
+    include LoginSystem
+    model :user
+
+After you have done the modifications the the AbstractController you can import
+the user model into the database. This model is meant as an example and you
+should extend it. If you just want to get things up and running you can find
+some create table syntax in db/user_model.sql.
+
+The model :user is required when you are hitting problems to the degree of
+"Session could not be restored becuase not all items in it are known"
+
+== Requirements
+
+You need a database table corresponding to the User model. 
+
+mysql syntax:
+CREATE TABLE users (
+  id int(11) NOT NULL auto_increment,
+  login varchar(80) default NULL,
+  password varchar(40) default NULL,
+  PRIMARY KEY  (id)
+);
+ 
+postgres :
+CREATE TABLE "users" (
+ "id" SERIAL NOT NULL UNIQUE,
+ "login" VARCHAR(80),
+ "password" VARCHAR,
+ PRIMARY KEY("id")
+) WITH OIDS;
+
+
+sqlite:
+CREATE TABLE 'users' (
+  'id' INTEGER PRIMARY KEY NOT NULL,
+  'user' VARCHAR(80) DEFAULT NULL,
+  'password' VARCHAR(40) DEFAULT NULL
+);
+
+Of course your user model can have any amount of extra fields. This is just a
+starting point
+
+== How to use it 
+
+Now you can go around and happily add "before_filter :login_required" to the
+controllers which you would like to protect. 
+
+After integrating the login system with your rails application navigate to your
+new controller's signup method. There you can create a new account. After you
+are done you should have a look at your DB. Your freshly created user will be
+there but the password will be a sha1 hashed 40 digit mess. I find this should
+be the minimum of security which every page offering login&password should give
+its customers. Now you can move to one of those controllers which you protected
+with the before_filter :login_required snippet. You will automatically be re-
+directed to your freshly created login controller and you are asked for a
+password. After entering valid account data you will be taken back to the
+controller which you requested earlier. Simple huh?
+
+== Tips & Tricks
+
+How do I...
+
+  ... restrict access to only a few methods? 
+  
+  A: Use before_filters build in scoping. 
+     Example: 
+       before_filter :login_required :only => [:myaccount, :changepassword]
+       before_filter :login_required :except => [:index]
+     
+  ... check if a user is logged-in in my views?
+  
+  A: @session['user'] will tell you. Here is an example helper which you can use to make this more pretty:
+     Example: 
+       def user?
+         !@session['user'].nil?
+       end
+
+
+You can find more help at http://wiki.rubyonrails.com/rails/show/LoginGenerator
\ No newline at end of file
diff --git a/automation/admin/Rakefile b/automation/admin/Rakefile
new file mode 100644
index 0000000..cffd19f
--- /dev/null
+++ b/automation/admin/Rakefile
@@ -0,0 +1,10 @@
+# Add your own tasks in files placed in lib/tasks ending in .rake,
+# for example lib/tasks/switchtower.rake, and they will automatically be available to Rake.
+
+require(File.join(File.dirname(__FILE__), 'config', 'boot'))
+
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+
+require 'tasks/rails'
\ No newline at end of file
diff --git a/automation/admin/app/controllers/account_controller.rb b/automation/admin/app/controllers/account_controller.rb
new file mode 100644
index 0000000..bbde11d
--- /dev/null
+++ b/automation/admin/app/controllers/account_controller.rb
@@ -0,0 +1,52 @@
+class AccountController < ApplicationController
+
+  layout  'admin'
+
+  def login
+    case request.method
+      when :post
+        if session[:user] = User.authenticate(params[:user_login], params[:user_password])
+
+          flash[:notice]  = "Login successful"
+          redirect_back_or_default :action => "welcome"
+        else
+          @login    = params[:user_login]
+          @message  = "Login unsuccessful"
+      end
+    end
+  end
+  
+  def signup
+    case request.method
+      when :post
+
+      # Set the initial deletion state to zero
+      params[:user][:is_deleted] = 0
+        @user = User.new(params[:user])
+        
+        if @user.save      
+          session[:user] = User.authenticate(@user.login, params[:user][:password])
+          flash[:notice]  = "Signup successful"
+          redirect_back_or_default :action => "welcome"          
+        end
+      when :get
+        @user = User.new
+    end      
+  end  
+  
+  def delete
+    if params[:id]
+      @user = User.find(params[:id])
+      @user.destroy
+    end
+    redirect_back_or_default :action => "welcome"
+  end  
+    
+  def logout
+    session[:user] = nil
+  end
+    
+  def welcome
+  end
+  
+end
diff --git a/automation/admin/app/controllers/application.rb b/automation/admin/app/controllers/application.rb
new file mode 100644
index 0000000..85d8dfa
--- /dev/null
+++ b/automation/admin/app/controllers/application.rb
@@ -0,0 +1,7 @@
+# Filters added to this controller will be run for all controllers in the application.
+# Likewise, all the methods added will be available for all controllers.
+require_dependency 'acl_system'
+
+class ApplicationController < ActionController::Base
+  include ACLSystem
+end
diff --git a/automation/admin/app/controllers/array_designs_controller.rb b/automation/admin/app/controllers/array_designs_controller.rb
new file mode 100644
index 0000000..aa02122
--- /dev/null
+++ b/automation/admin/app/controllers/array_designs_controller.rb
@@ -0,0 +1,124 @@
+class ArrayDesignsController < ApplicationController
+
+  layout "admin"
+  before_filter :login_required
+
+  def index
+    list
+    render :action => 'list'
+  end
+
+  # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
+  verify :method => :post, :only => [ :destroy, :create, :update ],
+         :redirect_to => { :action => :list }
+
+  def list
+
+    sql_where_clause = "is_deleted=0 and miamexpress_subid is not null"
+
+    @search_term = ""
+
+    if params[:search_term]
+
+      # Strip single quotes, otherwise they will cause a crash.
+      @search_term = params[:search_term].gsub(/\'/, "")
+
+      # Silently allow asterisk wildcards
+      sql_search = @search_term.gsub(/\*/, "%").gsub(/\?/, "_")
+
+      sql_where_clause += " and (accession like '#{ sql_search }'" +
+	                  " or name like '%#{ sql_search }%'" +
+	                  " or comment like '%#{ sql_search }%'" +
+	                  " or miamexpress_login like '%#{ sql_search }%'" +
+	                  " or miamexpress_subid like '#{ sql_search }')"
+    end
+
+    params[:page] ||= 1
+    @array_designs = ArrayDesign.paginate :page => params[:page],
+      :per_page   => 40,
+      :conditions => sql_where_clause.to_s,
+      :order      => 'miamexpress_subid DESC'
+  end
+
+  def list_all
+
+    num_per_page = params[:list_all].to_i.zero? ? 30 : 1000000
+
+    sql_where_clause = "is_deleted=0 and (accession is not null and accession!='')"
+
+    @search_term = ""
+
+    # Don't search with an empty string.
+    if params[:search_term] && !params[:search_term].eql?("")
+
+      # Strip single quotes, otherwise they will cause a crash.
+      @search_term = params[:search_term].gsub(/\'/, "")
+
+      # Silently allow asterisk wildcards
+      sql_search = @search_term.gsub(/\*/, "%").gsub(/\?/, "_")
+
+      sql_where_clause += " and accession like '#{ sql_search }'" +
+	                  " or comment like '%#{ sql_search }%'"
+
+    end
+
+    params[:page] ||= 1
+    @array_designs = ArrayDesign.paginate :page => params[:page],
+      :per_page   => num_per_page,
+      :conditions => sql_where_clause.to_s,
+      :order      => 'accession'
+
+  end
+
+  def show
+    @array_design = ArrayDesign.find(params[:id])
+  end
+
+  def new
+    @array_design = ArrayDesign.new
+  end
+
+  def edit
+    @array_design = ArrayDesign.find(params[:id])
+  end
+
+  def create
+
+    # Set the initial deletion state to zero
+    params[:array_design][:is_deleted] = 0
+    @array_design = ArrayDesign.new(params[:array_design])
+
+    if @array_design.save
+      flash[:notice] = 'ArrayDesign was successfully created.'
+      redirect_to :action => 'list'
+    else
+      render :action => 'new'
+    end
+  end
+
+  def update
+    @array_design = ArrayDesign.find(params[:id])
+    if @array_design.update_attributes(params[:array_design])
+      flash[:notice] = 'ArrayDesign was successfully updated.'
+      redirect_to :action => 'list_all',
+	          :search_term     => params[:search_term],
+                  :page            => params[:page]
+    else
+      render :action => 'edit'
+    end
+  end
+
+  def deprecate
+    array_design = ArrayDesign.find(params[:id])
+    if array_design.experiments.any?
+      flash[:notice] = "Error: There are still experiments using #{ array_design.accession }."
+      redirect_to :action => 'list' 
+    elsif array_design.update_attribute(:is_deleted, 1)
+      flash[:notice] = 'Array design was successfully deleted'
+      redirect_to :action => 'list',
+	          :search_term     => params[:search_term],
+                  :page            => params[:page]
+    end
+  end
+
+end
diff --git a/automation/admin/app/controllers/category_controller.rb b/automation/admin/app/controllers/category_controller.rb
new file mode 100644
index 0000000..90dcc6e
--- /dev/null
+++ b/automation/admin/app/controllers/category_controller.rb
@@ -0,0 +1,70 @@
+class CategoryController < ApplicationController
+
+  layout "admin"
+  before_filter :login_required, :except => [ :show, :list ]
+
+  def index
+    list
+    render :action => 'list'
+  end
+
+  # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
+  verify :method => :post, :only => [ :destroy, :create, :update ],
+         :redirect_to => { :action => :list }
+
+  def new
+    @category = Category.new
+  end
+
+  def show
+    @category = Category.find(params[:id])
+  end
+
+  def edit
+    @category = Category.find(params[:id])
+  end
+
+  def list
+    params[:page] ||= 1
+    @categories = Category.paginate :page => params[:page], 
+      :conditions => 'is_deleted=0',
+      :per_page   => 20,
+      :order      => 'ontology_term'
+  end
+
+  def create
+
+    # Set the initial deletion state to zero
+    params[:category][:is_deleted] = 0
+    @category = Category.new(params[:category])
+
+    if @category.save
+      flash[:notice] = 'Category was successfully created'
+      redirect_to :action => 'list'
+    else
+      render :action => 'new'
+    end
+  end
+
+  def update
+    @category = Category.find(params[:id])
+    if @category.update_attributes(params[:category])
+      flash[:notice] = 'Category was successfully updated'
+      redirect_to :action => 'list'
+    else
+      render :action => 'edit'
+    end
+  end
+      
+  def deprecate
+    category = Category.find(params[:id])
+    if category.taxons.any? || category.designs.any? || category.materials.any?
+      flash[:notice] = "Error: There are still taxons, designs or materials using #{ category.ontology_term }."
+      redirect_to :action => 'list' 
+    elsif category.update_attribute(:is_deleted, 1)
+      flash[:notice] = 'Category was successfully deleted'
+      redirect_to :action => 'list'
+    end
+  end
+
+end
diff --git a/automation/admin/app/controllers/design_controller.rb b/automation/admin/app/controllers/design_controller.rb
new file mode 100644
index 0000000..af5df9d
--- /dev/null
+++ b/automation/admin/app/controllers/design_controller.rb
@@ -0,0 +1,66 @@
+class DesignController < ApplicationController
+
+  layout "admin"
+  before_filter :login_required, :except => [ :show, :list ]
+
+  def new
+    if Category.find(:all).any?
+      @design = Design.new
+    else
+      flash[:notice] = 'Please create at least one category:'
+      redirect_to :action => 'new', :controller => 'category'
+    end
+  end
+
+  def edit
+    @design = Design.find(params[:id])
+  end
+
+  def list
+    params[:page] ||= 1
+    @designs = Design.paginate :page => params[:page], 
+      :conditions => 'is_deleted=0',
+      :per_page   => 20,
+      :order      => 'display_label'
+  end
+
+  def create
+
+    # Set the initial deletion state to zero
+    params[:design][:is_deleted] = 0
+    @design = Design.new(params[:design])
+
+    if @design.save
+      flash[:notice] = 'Design was successfully created'
+      redirect_to :action => 'list'
+    else
+      render :action => 'new'
+    end
+  end
+
+  def update
+    @design = Design.find(params[:id])
+
+    # Fix for empty ids
+    params[:design][:category_ids] ||= []
+
+    if @design.update_attributes(params[:design])
+      flash[:notice] = 'Design was successfully updated'
+      redirect_to :action => 'list'
+    else
+      render :action => 'edit'
+    end
+  end
+      
+  def deprecate
+    design = Design.find(params[:id])
+    if design.experiments.any?
+      flash[:notice] = "Error: There are still experiments using the #{ design.ontology_value } design."
+      redirect_to :action => 'list' 
+    elsif design.update_attribute(:is_deleted, 1)
+      flash[:notice] = 'Design was successfully deleted'
+      redirect_to :action => 'list'
+    end
+  end
+
+end
diff --git a/automation/admin/app/controllers/event_controller.rb b/automation/admin/app/controllers/event_controller.rb
new file mode 100644
index 0000000..7bd174e
--- /dev/null
+++ b/automation/admin/app/controllers/event_controller.rb
@@ -0,0 +1,21 @@
+class EventController < ApplicationController
+
+  layout "admin"
+  before_filter :login_required
+
+  def edit
+    @event  = Event.find(params[:id])
+  end
+
+  def update
+    @event = Event.find(params[:id])
+    if @event.update_attributes(params[:event])
+      flash[:notice] = 'Event was successfully updated.'
+      redirect_to :action => 'edit',
+		  :id     => @event.id
+    else
+      render :action => 'edit'
+    end
+  end
+
+end
diff --git a/automation/admin/app/controllers/experiments_controller.rb b/automation/admin/app/controllers/experiments_controller.rb
new file mode 100644
index 0000000..c8383ee
--- /dev/null
+++ b/automation/admin/app/controllers/experiments_controller.rb
@@ -0,0 +1,148 @@
+class ExperimentsController < ApplicationController
+
+  layout "admin"
+  before_filter :login_required
+
+  def index
+    list
+    render :action => 'list'
+  end
+
+  # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
+  verify :method => :post, :only => [ :destroy, :create, :update ],
+         :redirect_to => { :action => :list }
+
+  def list
+
+    num_per_page = params[:list_all].to_i.zero? ? 30 : 1000000
+
+    sql_where_clause = "is_deleted=0 and (accession is not null and accession!='')"
+
+    @search_term = ""
+
+    # Don't search with an empty string.
+    if params[:search_term] && !params[:search_term].eql?("")
+
+      # Strip single quotes, otherwise they will cause a crash.
+      @search_term = params[:search_term].gsub(/\'/, "")
+
+      # Silently allow asterisk wildcards
+      sql_search = @search_term.gsub(/\*/, "%").gsub(/\?/, "_")
+
+      sql_where_clause += " and accession like '#{ sql_search }'" +
+	                  " or comment like '%#{ sql_search }%'"
+
+    end
+
+    params[:page] ||= 1
+    @experiments = Experiment.paginate :page => params[:page],
+      :per_page   => num_per_page,
+      :conditions => sql_where_clause.to_s,
+      :order      => 'accession'
+
+  end
+
+  def today_list
+    @time_now = Time.now;
+    date_today       = @time_now.strftime("%Y-%m-%d %H:%M:%S");
+    date_yesterday   = sprintf("%04d-%02d-%02d", @time_now.year, @time_now.month, @time_now.mday - 1);
+    sql_where_clause = sprintf("is_deleted=0 and date_last_processed between '%s' and '%s'", date_yesterday, date_today);
+
+    if params[:experiment_type]
+      sql_where_clause += " and experiment_type='#{ params[:experiment_type] }'"
+    end
+
+    params[:page] ||= 1
+    @experiments = Experiment.paginate :page => params[:page],
+      :per_page   => 30,
+      :conditions => sql_where_clause.to_s,
+      :order      => 'date_last_processed DESC'
+  end
+
+  def show
+    @experiment = Experiment.find(params[:id])
+  end
+
+  def new
+    @experiment = Experiment.new
+  end
+
+  def create
+
+    # Set the initial deletion state to zero
+    params[:experiment][:is_deleted] = 0
+    @experiment = Experiment.new(params[:experiment])
+
+    if @experiment.annotate(params[:annotation]) && @experiment.save
+      flash[:notice] = "#{params[:experiment][:experiment_type]} experiment was successfully created."
+      redirect_to :action          => 'list',
+	          :experiment_type => params[:experiment][:experiment_type]
+    else
+      render :action => 'new'
+    end
+  end
+
+  def edit
+
+    # Abstract superclass method dispatches edit calls to the relevant subclass.
+    @experiment  = Experiment.find(params[:id])
+    if @experiment.experiment_type == 'MIAMExpress'
+      redirect_to :controller      => 'miamexps',
+                  :action          => 'edit',
+                  :id              => @experiment.id,
+                  :experiment_type => @experiment.experiment_type,
+                  :search_term     => params[:search_term],
+                  :page            => params[:page]
+    else
+      redirect_to :controller      => 'tab2mages',
+                  :action          => 'edit',
+                  :id              => @experiment.id,
+                  :experiment_type => @experiment.experiment_type,
+                  :search_term     => params[:search_term],
+                  :page            => params[:page]
+    end
+  end
+
+  def update
+    @experiment = Experiment.find(params[:id])
+    if @experiment.annotate(params[:annotation]) && @experiment.update_attributes(params[:experiment])
+      flash[:notice] = 'Experiment was successfully updated.'
+      redirect_to :action => 'list',
+	          :experiment_type => params[:experiment_type],
+	          :search_term     => params[:search_term],
+                  :page            => params[:page]
+    else
+      render :action => 'edit'
+    end
+  end
+
+  def deprecate
+    experiment = Experiment.find(params[:id])
+    if experiment.spreadsheets.find(:all, :conditions => "is_deleted=0").any?
+      flash[:notice] = "Error: There are still spreadsheets attached to the experiment: #{ experiment.name }"
+      redirect_to :action          => 'list', 
+	          :experiment_type => params[:experiment_type],
+	          :search_term     => params[:search_term],
+                  :page            => params[:page]
+    elsif experiment.data_files.find(:all, :conditions => "is_deleted=0").any?
+      flash[:notice] = "Error: There are still data_files attached to the experiment: #{ experiment.name }"
+      redirect_to :action          => 'list', 
+	          :experiment_type => params[:experiment_type],
+	          :search_term     => params[:search_term],
+                  :page            => params[:page]
+    elsif experiment.accession?
+      flash[:notice] = "Error: This experiment has been assigned an accession number, and cannot be deleted: #{ experiment.name }"
+      redirect_to :action => 'list', 
+	          :experiment_type => params[:experiment_type],
+	          :search_term     => params[:search_term],
+                  :page            => params[:page]
+    elsif experiment.update_attribute(:is_deleted, 1)
+      flash[:notice] = 'Experiment was successfully deleted'
+      redirect_to :action => 'list',
+	          :experiment_type => params[:experiment_type],
+  	          :search_term     => params[:search_term],
+                  :page            => params[:page]
+    end
+  end
+
+end
diff --git a/automation/admin/app/controllers/factor_controller.rb b/automation/admin/app/controllers/factor_controller.rb
new file mode 100644
index 0000000..3da52bf
--- /dev/null
+++ b/automation/admin/app/controllers/factor_controller.rb
@@ -0,0 +1,6 @@
+class FactorController < ApplicationController
+
+  layout "admin"
+  before_filter :login_required
+
+end
diff --git a/automation/admin/app/controllers/material_controller.rb b/automation/admin/app/controllers/material_controller.rb
new file mode 100644
index 0000000..360a2f2
--- /dev/null
+++ b/automation/admin/app/controllers/material_controller.rb
@@ -0,0 +1,66 @@
+class MaterialController < ApplicationController
+
+  layout "admin"
+  before_filter :login_required, :except => [ :show, :list ]
+
+  def new
+    if Category.find(:all).any?
+      @material = Material.new
+    else
+      flash[:notice] = 'Please create at least one category:'
+      redirect_to :action => 'new', :controller => 'category'
+    end
+  end
+
+  def edit
+    @material = Material.find(params[:id])
+  end
+
+  def list
+    params[:page] ||= 1
+    @materials = Material.paginate :page => params[:page], 
+      :conditions => 'is_deleted=0',
+      :per_page   => 20,
+      :order      => 'display_label'
+  end
+
+  def create
+
+    # Set the initial deletion state to zero
+    params[:material][:is_deleted] = 0
+    @material = Material.new(params[:material])
+
+    if @material.save
+      flash[:notice] = 'Material was successfully created'
+      redirect_to :action => 'list'
+    else
+      render :action => 'new'
+    end
+  end
+
+  def update
+    @material = Material.find(params[:id])
+
+    # Fix for empty ids
+    params[:material][:category_ids] ||= []
+
+    if @material.update_attributes(params[:material])
+      flash[:notice] = 'Material was successfully updated'
+      redirect_to :action => 'list'
+    else
+      render :action => 'edit'
+    end
+  end
+      
+  def deprecate
+    material = Material.find(params[:id])
+    if material.experiments.any?
+      flash[:notice] = "Error: There are still experiments using the #{ material.ontology_value } material."
+      redirect_to :action => 'list' 
+    elsif material.update_attribute(:is_deleted, 1)
+      flash[:notice] = 'Material was successfully deleted'
+      redirect_to :action => 'list'
+    end
+  end
+
+end
diff --git a/automation/admin/app/controllers/miamexps_controller.rb b/automation/admin/app/controllers/miamexps_controller.rb
new file mode 100644
index 0000000..1d9eb78
--- /dev/null
+++ b/automation/admin/app/controllers/miamexps_controller.rb
@@ -0,0 +1,35 @@
+class MiamexpsController < ExperimentsController
+
+  def list
+    sql_where_clause = "is_deleted=0 and ( miamexpress_subid is not null || experiment_type='MIAMExpress' )"
+
+    @search_term = ""
+
+    if params[:search_term]
+
+      # Strip single quotes, otherwise they will cause a crash.
+      @search_term = params[:search_term].gsub(/\'/, "")
+
+      # Silently allow asterisk wildcards
+      sql_search = @search_term.gsub(/\*/, "%").gsub(/\?/, "_")
+
+      sql_where_clause += " and (accession like '#{ sql_search }'" +
+	                  " or name like '%#{ sql_search }%'" +
+	                  " or comment like '%#{ sql_search }%'" +
+	                  " or miamexpress_login like '%#{ sql_search }%'" +
+	                  " or miamexpress_subid like '#{ sql_search }')"
+    end
+
+    params[:page] ||= 1
+    @experiments = Experiment.paginate :page => params[:page],
+      :per_page   => 30,
+      :conditions => sql_where_clause.to_s,
+      :order      => 'miamexpress_subid DESC'
+
+  end
+
+  def edit
+    @experiment  = Experiment.find(params[:id])
+  end
+
+end
diff --git a/automation/admin/app/controllers/organism_controller.rb b/automation/admin/app/controllers/organism_controller.rb
new file mode 100644
index 0000000..897a583
--- /dev/null
+++ b/automation/admin/app/controllers/organism_controller.rb
@@ -0,0 +1,90 @@
+class OrganismController < ApplicationController
+
+  layout "admin"
+  before_filter :login_required, :except => [ :show, :list ]
+
+  def new
+    if Taxon.find(:all).any?
+      @organism = Organism.new
+    else
+      flash[:notice] = 'Please create at least one taxon:'
+      redirect_to :action => 'new', :controller => 'taxon'
+    end
+  end
+
+  def edit
+    @organism = Organism.find(params[:id])
+  end
+
+  def list
+    sql_where_clause = "is_deleted=0"
+
+    @search_term = ""
+
+    # Don't search with an empty string.
+    if params[:search_term] && !params[:search_term].eql?("")
+
+      # Strip single quotes, otherwise they will cause a crash.
+      @search_term = params[:search_term].gsub(/\'/, "")
+
+      # Silently allow asterisk wildcards
+      sql_search = @search_term.gsub(/\*/, "%").gsub(/\?/, "_")
+
+      sql_where_clause += " and scientific_name like '#{ sql_search }'"
+
+    end
+
+    params[:page] ||= 1
+    @organisms = Organism.paginate :page => params[:page], 
+      :conditions => sql_where_clause.to_s,
+      :per_page   => 20,
+      :order      => 'scientific_name'
+  end
+
+  def create
+
+    # Set the initial deletion state to zero
+    params[:organism][:is_deleted] = 0
+    @organism = Organism.new(params[:organism])
+
+    if @organism.save
+      flash[:notice] = 'Organism was successfully created'
+      redirect_to :action => 'list'
+    else
+      render :action => 'new'
+    end
+  end
+
+  def update
+    @organism = Organism.find(params[:id])
+    if @organism.update_attributes(params[:organism])
+      flash[:notice] = 'Organism was successfully updated'
+      redirect_to :action => 'list',
+	          :search_term     => params[:search_term],
+                  :page            => params[:page]
+    else
+      render :action => 'edit'
+    end
+  end
+      
+  def deprecate
+    organism = Organism.find(params[:id])
+    if organism.experiments.any?
+      flash[:notice] = "Error: There are still experiments using #{ organism.scientific_name }."
+      redirect_to :action => 'list',
+	          :search_term     => params[:search_term],
+                  :page            => params[:page]
+    elsif organism.array_designs.any?
+      flash[:notice] = "Error: There are still array designs using #{ organism.scientific_name }."
+      redirect_to :action => 'list',
+	          :search_term     => params[:search_term],
+                  :page            => params[:page]
+    elsif organism.update_attribute(:is_deleted, 1)
+      flash[:notice] = 'Organism was successfully deleted'
+      redirect_to :action => 'list',
+	          :search_term     => params[:search_term],
+                  :page            => params[:page]
+    end
+  end
+
+end
diff --git a/automation/admin/app/controllers/protocols_controller.rb b/automation/admin/app/controllers/protocols_controller.rb
new file mode 100644
index 0000000..c486108
--- /dev/null
+++ b/automation/admin/app/controllers/protocols_controller.rb
@@ -0,0 +1,90 @@
+class ProtocolsController < ApplicationController
+
+  layout "admin"
+  before_filter :login_required
+
+  def index
+    list
+    render :action => 'list'
+  end
+
+  # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
+  verify :method => :post, :only => [ :destroy, :create, :update ],
+         :redirect_to => { :action => :list }
+
+  def list
+
+    sql_where_clause = "is_deleted=0"
+
+    @search_term = ""
+
+    if params[:search_term]
+
+      # Strip single quotes, otherwise they will cause a crash.
+      @search_term = params[:search_term].gsub(/\'/, "")
+
+      # Silently allow asterisk wildcards
+      sql_search = @search_term.gsub(/\*/, "%").gsub(/\?/, "_")
+
+      sql_where_clause += " and (accession like '#{ sql_search }'" +
+	                  " or user_accession like '%#{ sql_search }%'" +
+	                  " or name like '%#{ sql_search }%'" +
+	                  " or comment like '%#{ sql_search }%'" +
+	                  " or expt_accession like '#{ sql_search }')"
+    end
+
+    params[:page] ||= 1
+    @protocols = Protocol.paginate :page => params[:page],
+      :per_page   => 40,
+      :conditions => sql_where_clause.to_s,
+      :order      => 'id DESC'
+
+  end
+
+  def show
+    @protocol = Protocol.find(params[:id])
+  end
+
+  def new
+    @protocol = Protocol.new
+  end
+
+  def edit
+    @protocol = Protocol.find(params[:id])
+  end
+
+  def create
+
+    # Set the initial deletion state to zero
+    params[:protocol][:is_deleted] = 0
+    @protocol = Protocol.new(params[:protocol])
+
+    if @protocol.save
+      flash[:notice] = 'Protocol was successfully created.'
+      redirect_to :action => 'list'
+    else
+      render :action => 'new'
+    end
+  end
+
+  def update
+    @protocol = Protocol.find(params[:id])
+    if @protocol.update_attributes(params[:protocol])
+      flash[:notice] = 'Protocol was successfully updated.'
+      redirect_to :action => 'list',
+	          :search_term     => params[:search_term],
+                  :page            => params[:page]
+    else
+      render :action => 'edit'
+    end
+  end
+
+  def deprecate
+    Protocol.find(params[:id]).update_attribute(:is_deleted, 1)
+      flash[:notice] = 'Protocol was successfully deleted.'
+    redirect_to :action => 'list',
+	        :search_term     => params[:search_term],
+                :page            => params[:page]
+  end
+
+end
diff --git a/automation/admin/app/controllers/quantitation_type_controller.rb b/automation/admin/app/controllers/quantitation_type_controller.rb
new file mode 100644
index 0000000..d2d25fa
--- /dev/null
+++ b/automation/admin/app/controllers/quantitation_type_controller.rb
@@ -0,0 +1,6 @@
+class QuantitationTypeController < ApplicationController
+
+  layout "admin"
+  before_filter :login_required
+
+end
diff --git a/automation/admin/app/controllers/tab2mages_controller.rb b/automation/admin/app/controllers/tab2mages_controller.rb
new file mode 100644
index 0000000..4f091ec
--- /dev/null
+++ b/automation/admin/app/controllers/tab2mages_controller.rb
@@ -0,0 +1,43 @@
+class Tab2magesController < ExperimentsController
+
+  def list
+
+    sql_where_clause = "is_deleted=0"
+
+    @search_term = ""
+
+    if params[:experiment_type]
+      sql_where_clause += " and experiment_type='#{ params[:experiment_type] }'"
+    end
+
+    if params[:search_term]
+
+      # Strip single quotes, otherwise they will cause a crash.
+      @search_term = params[:search_term].gsub(/\'/, "")
+
+      # Silently allow asterisk wildcards
+      sql_search = @search_term.gsub(/\*/, "%").gsub(/\?/, "_")
+
+      users = User.find(:all, :conditions => "login like '%#{ sql_search }%'")
+      user_clause = users.any? ? " or user_id in (#{ users.collect{|u| u.id}.join(',') })" : ""
+
+      sql_where_clause += " and (accession like '#{ sql_search }'" +
+	                  " or comment like '%#{ sql_search }%'" +
+	                  " or id like '#{ sql_search }'" +
+	                  " or miamexpress_login like '%#{ sql_search }%'" +
+	                  " or name like '%#{ sql_search }%'#{ user_clause })"
+    end
+    
+    params[:page] ||= 1
+    @experiments = Experiment.paginate :page => params[:page],
+      :per_page   => 30,
+      :conditions => sql_where_clause.to_s,
+      :order      => 'accession is null asc, accession="" asc, cast(substr(accession,8,10) as signed integer) desc'
+
+  end
+
+  def edit
+    @experiment  = Experiment.find(params[:id])
+  end
+
+end
diff --git a/automation/admin/app/controllers/taxon_controller.rb b/automation/admin/app/controllers/taxon_controller.rb
new file mode 100644
index 0000000..a88546a
--- /dev/null
+++ b/automation/admin/app/controllers/taxon_controller.rb
@@ -0,0 +1,71 @@
+class TaxonController < ApplicationController
+
+  layout "admin"
+  before_filter :login_required, :except => [ :show, :list ]
+
+  def new
+    if Category.find(:all).any?
+      @taxon = Taxon.new
+    else
+      flash[:notice] = 'Please create at least one category:'
+      redirect_to :action => 'new', :controller => 'category'
+    end
+  end
+
+  def edit
+    @taxon = Taxon.find(params[:id])
+  end
+
+  def list
+    params[:page] ||= 1
+    @taxons = Taxon.paginate :page => params[:page], 
+      :conditions => 'is_deleted=0',
+      :per_page   => 20,
+      :order      => 'scientific_name'
+  end
+
+  def category_table
+    @taxons     = Taxon.find(:all, :conditions => 'is_deleted=0')
+    @categories = Category.find(:all, :conditions => 'is_deleted=0')
+  end
+
+  def create
+
+    # Set the initial deletion state to zero
+    params[:taxon][:is_deleted] = 0
+    @taxon = Taxon.new(params[:taxon])
+
+    if @taxon.save
+      flash[:notice] = 'Taxon was successfully created'
+      redirect_to :action => 'list'
+    else
+      render :action => 'new'
+    end
+  end
+
+  def update
+    @taxon = Taxon.find(params[:id])
+    
+    # Fix for empty ids
+    params[:taxon][:category_ids] ||= []
+
+    if @taxon.update_attributes(params[:taxon])
+      flash[:notice] = 'Taxon was successfully updated'
+      redirect_to :action => 'list'
+    else
+      render :action => 'edit'
+    end
+  end
+      
+  def deprecate
+    taxon = Taxon.find(params[:id])
+    if taxon.organisms.any?
+      flash[:notice] = "Error: There are still organisms in the #{ taxon.common_name } taxon."
+      redirect_to :action => 'list' 
+    elsif taxon.update_attribute(:is_deleted, 1)
+      flash[:notice] = 'Taxon was successfully deleted'
+      redirect_to :action => 'list'
+    end
+  end
+
+end
diff --git a/automation/admin/app/controllers/users_controller.rb b/automation/admin/app/controllers/users_controller.rb
new file mode 100644
index 0000000..c04f0d5
--- /dev/null
+++ b/automation/admin/app/controllers/users_controller.rb
@@ -0,0 +1,89 @@
+class UsersController < ApplicationController
+
+  layout "admin"
+  before_filter :login_required
+
+  def list
+    sql_where_clause = "is_deleted=0"
+
+    @search_term = ""
+
+    if params[:search_term]
+
+      # Strip single quotes, otherwise they will cause a crash.
+      @search_term = params[:search_term].gsub(/\'/, "")
+
+      # Silently allow asterisk wildcards
+      sql_search = @search_term.gsub(/\*/, "%").gsub(/\?/, "_")
+
+      sql_where_clause += " and (login like '#{ sql_search }'" +
+	                  " or name like '%#{ sql_search }%'" +
+	                  " or email like '%#{ sql_search }%')"
+    end
+    
+    params[:page] ||= 1
+    @users = User.paginate :page => params[:page],
+      :conditions => sql_where_clause.to_s,
+      :order      => 'login',
+      :per_page   => 15
+  end
+
+  def show
+    @user = User.find(params[:id])
+  end
+
+  def new
+    redirect_to :controller => 'account', :action => 'signup'
+    @user = User.new
+  end
+
+  def create
+
+    # Set the initial deletion state to zero
+    params[:user][:is_deleted] = 0
+    @user = User.new(params[:user])
+
+    if @user.save
+      flash[:notice] = 'User was successfully created.'
+      redirect_to :action => 'list'
+    else
+      render :action => 'new'
+    end
+  end
+
+  def edit
+    @user = User.find(params[:id])
+  end
+
+  def update
+    @user = User.find(params[:id])
+
+    # FIXME the Time call below returns the correct string, but it is
+    # not stored correctly in the database.
+    if @user.update_attributes(params[:user]) \
+      && @user.update_attribute(:modified_at, Time.now.getutc.iso8601.to_s)
+      flash[:notice] = 'User was successfully updated.'
+      redirect_to :action          => 'list',
+	          :search_term     => params[:search_term],
+                  :page            => params[:page]
+    else
+      render :action => 'edit'
+    end
+  end
+
+  def deprecate
+    user = User.find(params[:id])
+    if user.experiments.any?
+      flash[:notice] = "Error: There are still experiments attached to user #{ user.login }."
+      redirect_to :action          => 'list',
+	          :search_term     => params[:search_term],
+                  :page            => params[:page]
+    elsif user.update_attribute(:is_deleted, 1)
+      flash[:notice] = 'User was successfully deleted'
+      redirect_to :action          => 'list',
+	          :search_term     => params[:search_term],
+                  :page            => params[:page]
+    end
+  end
+
+end
diff --git a/automation/admin/app/helpers/account_helper.rb b/automation/admin/app/helpers/account_helper.rb
new file mode 100644
index 0000000..6d3694a
--- /dev/null
+++ b/automation/admin/app/helpers/account_helper.rb
@@ -0,0 +1,2 @@
+module AccountHelper
+end
diff --git a/automation/admin/app/helpers/application_helper.rb b/automation/admin/app/helpers/application_helper.rb
new file mode 100644
index 0000000..22a7940
--- /dev/null
+++ b/automation/admin/app/helpers/application_helper.rb
@@ -0,0 +1,3 @@
+# Methods added to this helper will be available to all templates in the application.
+module ApplicationHelper
+end
diff --git a/automation/admin/app/helpers/array_designs_helper.rb b/automation/admin/app/helpers/array_designs_helper.rb
new file mode 100644
index 0000000..1b7a249
--- /dev/null
+++ b/automation/admin/app/helpers/array_designs_helper.rb
@@ -0,0 +1,2 @@
+module ArrayDesignsHelper
+end
diff --git a/automation/admin/app/helpers/category_helper.rb b/automation/admin/app/helpers/category_helper.rb
new file mode 100644
index 0000000..ac5cb5b
--- /dev/null
+++ b/automation/admin/app/helpers/category_helper.rb
@@ -0,0 +1,2 @@
+module CategoryHelper
+end
diff --git a/automation/admin/app/helpers/design_helper.rb b/automation/admin/app/helpers/design_helper.rb
new file mode 100644
index 0000000..6293f66
--- /dev/null
+++ b/automation/admin/app/helpers/design_helper.rb
@@ -0,0 +1,2 @@
+module DesignHelper
+end
diff --git a/automation/admin/app/helpers/event_helper.rb b/automation/admin/app/helpers/event_helper.rb
new file mode 100644
index 0000000..837363e
--- /dev/null
+++ b/automation/admin/app/helpers/event_helper.rb
@@ -0,0 +1,2 @@
+module EventHelper
+end
diff --git a/automation/admin/app/helpers/experiments_helper.rb b/automation/admin/app/helpers/experiments_helper.rb
new file mode 100644
index 0000000..694268c
--- /dev/null
+++ b/automation/admin/app/helpers/experiments_helper.rb
@@ -0,0 +1,2 @@
+module ExperimentsHelper
+end
diff --git a/automation/admin/app/helpers/factor_helper.rb b/automation/admin/app/helpers/factor_helper.rb
new file mode 100644
index 0000000..bda7ccb
--- /dev/null
+++ b/automation/admin/app/helpers/factor_helper.rb
@@ -0,0 +1,2 @@
+module FactorHelper
+end
diff --git a/automation/admin/app/helpers/material_helper.rb b/automation/admin/app/helpers/material_helper.rb
new file mode 100644
index 0000000..5b1e197
--- /dev/null
+++ b/automation/admin/app/helpers/material_helper.rb
@@ -0,0 +1,2 @@
+module MaterialHelper
+end
diff --git a/automation/admin/app/helpers/miamexps_helper.rb b/automation/admin/app/helpers/miamexps_helper.rb
new file mode 100644
index 0000000..2d84ff3
--- /dev/null
+++ b/automation/admin/app/helpers/miamexps_helper.rb
@@ -0,0 +1,2 @@
+module MiamexpsHelper
+end
diff --git a/automation/admin/app/helpers/organism_helper.rb b/automation/admin/app/helpers/organism_helper.rb
new file mode 100644
index 0000000..f3c3c92
--- /dev/null
+++ b/automation/admin/app/helpers/organism_helper.rb
@@ -0,0 +1,2 @@
+module OrganismHelper
+end
diff --git a/automation/admin/app/helpers/protocols_helper.rb b/automation/admin/app/helpers/protocols_helper.rb
new file mode 100644
index 0000000..ce23494
--- /dev/null
+++ b/automation/admin/app/helpers/protocols_helper.rb
@@ -0,0 +1,2 @@
+module ProtocolsHelper
+end
diff --git a/automation/admin/app/helpers/quantitation_type_helper.rb b/automation/admin/app/helpers/quantitation_type_helper.rb
new file mode 100644
index 0000000..3e6b317
--- /dev/null
+++ b/automation/admin/app/helpers/quantitation_type_helper.rb
@@ -0,0 +1,2 @@
+module QuantitationTypeHelper
+end
diff --git a/automation/admin/app/helpers/tab2mages_helper.rb b/automation/admin/app/helpers/tab2mages_helper.rb
new file mode 100644
index 0000000..9e8a0d0
--- /dev/null
+++ b/automation/admin/app/helpers/tab2mages_helper.rb
@@ -0,0 +1,2 @@
+module Tab2magesHelper
+end
diff --git a/automation/admin/app/helpers/taxon_helper.rb b/automation/admin/app/helpers/taxon_helper.rb
new file mode 100644
index 0000000..ba38184
--- /dev/null
+++ b/automation/admin/app/helpers/taxon_helper.rb
@@ -0,0 +1,2 @@
+module TaxonHelper
+end
diff --git a/automation/admin/app/helpers/users_helper.rb b/automation/admin/app/helpers/users_helper.rb
new file mode 100644
index 0000000..2310a24
--- /dev/null
+++ b/automation/admin/app/helpers/users_helper.rb
@@ -0,0 +1,2 @@
+module UsersHelper
+end
diff --git a/automation/admin/app/models/array_design.rb b/automation/admin/app/models/array_design.rb
new file mode 100644
index 0000000..6e76dc5
--- /dev/null
+++ b/automation/admin/app/models/array_design.rb
@@ -0,0 +1,12 @@
+class ArrayDesign < ActiveRecord::Base
+
+  has_and_belongs_to_many :organisms
+  has_and_belongs_to_many :experiments
+  has_many :events
+
+  validates_format_of :accession, :with => /^(A-[A-Z]{4}-\d+|unknown)?$/, :message => "is not the correct format"
+
+  # Allow nulls but no other duplicated values
+  validates_uniqueness_of :accession, :if => Proc.new{ |array| array.accession !~ /^(unknown)?$/ }
+
+end
diff --git a/automation/admin/app/models/category.rb b/automation/admin/app/models/category.rb
new file mode 100644
index 0000000..ac3cd97
--- /dev/null
+++ b/automation/admin/app/models/category.rb
@@ -0,0 +1,7 @@
+class Category < ActiveRecord::Base
+  has_and_belongs_to_many :taxons
+  has_and_belongs_to_many :designs
+  has_and_belongs_to_many :materials
+  validates_presence_of :ontology_term, :display_label
+  validates_uniqueness_of :display_label
+end
diff --git a/automation/admin/app/models/data_file.rb b/automation/admin/app/models/data_file.rb
new file mode 100644
index 0000000..034bcfc
--- /dev/null
+++ b/automation/admin/app/models/data_file.rb
@@ -0,0 +1,3 @@
+class DataFile < ActiveRecord::Base
+  has_one :experiment
+end
diff --git a/automation/admin/app/models/design.rb b/automation/admin/app/models/design.rb
new file mode 100644
index 0000000..ec725b0
--- /dev/null
+++ b/automation/admin/app/models/design.rb
@@ -0,0 +1,7 @@
+class Design < ActiveRecord::Base
+  has_and_belongs_to_many :categories
+  has_many :design_instances
+  has_many :experiments, :through => :design_instances
+  validates_presence_of :display_label, :ontology_category, :ontology_value
+  validates_uniqueness_of :display_label
+end
diff --git a/automation/admin/app/models/design_instance.rb b/automation/admin/app/models/design_instance.rb
new file mode 100644
index 0000000..6795f9e
--- /dev/null
+++ b/automation/admin/app/models/design_instance.rb
@@ -0,0 +1,4 @@
+class DesignInstance < ActiveRecord::Base
+  belongs_to :design
+  belongs_to :experiment
+end
diff --git a/automation/admin/app/models/event.rb b/automation/admin/app/models/event.rb
new file mode 100644
index 0000000..9bb9af4
--- /dev/null
+++ b/automation/admin/app/models/event.rb
@@ -0,0 +1,8 @@
+class Event < ActiveRecord::Base
+
+  belongs_to :experiment
+  belongs_to :array_design
+
+  validates_presence_of :event_type
+
+end
diff --git a/automation/admin/app/models/experiment.rb b/automation/admin/app/models/experiment.rb
new file mode 100644
index 0000000..ed6cd31
--- /dev/null
+++ b/automation/admin/app/models/experiment.rb
@@ -0,0 +1,43 @@
+require_dependency 'annotation'
+
+class Experiment < ActiveRecord::Base
+  has_many   :design_instances,   :dependent => :destroy
+  has_many   :designs,            :through   => :design_instances
+
+  has_many   :material_instances, :dependent => :destroy
+  has_many   :materials,          :through   => :material_instances
+
+  has_many   :organism_instances, :dependent => :destroy
+  has_many   :organisms,          :through   => :organism_instances
+
+  has_and_belongs_to_many :factors
+  has_and_belongs_to_many :quantitation_types
+  has_and_belongs_to_many :array_designs
+
+  has_many   :spreadsheets
+  has_many   :data_files
+
+  has_many   :events
+
+  belongs_to :user
+
+  validates_numericality_of :miamexpress_subid, :allow_nil => true, :only_integer => true, :message => "is not an integer"
+  validates_numericality_of :checker_score, :allow_nil => true, :only_integer => true, :message => "is not an integer"
+  validates_inclusion_of :is_affymetrix,
+                         :in_curation,
+                           :allow_nil => true,
+                           :in        => 0..1,
+                           :message   => "is not 0 or 1"
+  validates_inclusion_of :experiment_type,
+                           :in      => %w(Tab2MAGE MIAMExpress MUGEN GEO MAGE-TAB Unknown),
+                           :message => "is not recognized"
+  validates_format_of :accession, :with => /^(E-[A-Z]{4}-\d+|unknown)?$/, :message => "is not the correct format"
+
+  # Allow nulls but no other duplicated values
+  validates_uniqueness_of :accession, :if => Proc.new{ |expt| expt.accession !~ /^(unknown)?$/ }
+
+  validates_associated :organisms
+
+  # Annotation mixin
+  include Annotation
+end
diff --git a/automation/admin/app/models/factor.rb b/automation/admin/app/models/factor.rb
new file mode 100644
index 0000000..973081c
--- /dev/null
+++ b/automation/admin/app/models/factor.rb
@@ -0,0 +1,7 @@
+class Factor < ActiveRecord::Base
+
+  has_and_belongs_to_many :experiments
+
+  validates_presence_of :name
+
+end
diff --git a/automation/admin/app/models/material.rb b/automation/admin/app/models/material.rb
new file mode 100644
index 0000000..f53c1dc
--- /dev/null
+++ b/automation/admin/app/models/material.rb
@@ -0,0 +1,7 @@
+class Material < ActiveRecord::Base
+  has_and_belongs_to_many :categories
+  has_many :material_instances
+  has_many :experiments, :through => :material_instances
+  validates_presence_of :display_label, :ontology_category, :ontology_value
+  validates_uniqueness_of :display_label
+end
diff --git a/automation/admin/app/models/material_instance.rb b/automation/admin/app/models/material_instance.rb
new file mode 100644
index 0000000..f2e6a7c
--- /dev/null
+++ b/automation/admin/app/models/material_instance.rb
@@ -0,0 +1,4 @@
+class MaterialInstance < ActiveRecord::Base
+  belongs_to :material
+  belongs_to :experiment
+end
diff --git a/automation/admin/app/models/organism.rb b/automation/admin/app/models/organism.rb
new file mode 100644
index 0000000..541c0d7
--- /dev/null
+++ b/automation/admin/app/models/organism.rb
@@ -0,0 +1,10 @@
+class Organism < ActiveRecord::Base
+  belongs_to :taxon
+  has_many :organism_instances
+  has_many :experiments, :through => :organism_instances
+  has_and_belongs_to_many :array_designs
+  validates_presence_of :accession, :common_name, :scientific_name, :taxon
+  validates_uniqueness_of :accession, :common_name, :scientific_name
+  validates_numericality_of :accession, :only_integer => true, :message => "is not an integer"
+  validates_associated :taxon
+end
diff --git a/automation/admin/app/models/organism_instance.rb b/automation/admin/app/models/organism_instance.rb
new file mode 100644
index 0000000..0654b26
--- /dev/null
+++ b/automation/admin/app/models/organism_instance.rb
@@ -0,0 +1,4 @@
+class OrganismInstance < ActiveRecord::Base
+  belongs_to :organism
+  belongs_to :experiment
+end
diff --git a/automation/admin/app/models/permission.rb b/automation/admin/app/models/permission.rb
new file mode 100644
index 0000000..752ea78
--- /dev/null
+++ b/automation/admin/app/models/permission.rb
@@ -0,0 +1,6 @@
+# See <a href="http://wiki.rubyonrails.com/rails/show/AccessControlListExample">http://wiki.rubyonrails.com/rails/show/AccessControlListExample</a>
+# and <a href="http://wiki.rubyonrails.com/rails/show/LoginGeneratorAccessControlList">http://wiki.rubyonrails.com/rails/show/LoginGeneratorAccessControlList</a>
+
+class Permission < ActiveRecord::Base
+  has_and_belongs_to_many :roles
+end
diff --git a/automation/admin/app/models/protocol.rb b/automation/admin/app/models/protocol.rb
new file mode 100644
index 0000000..aeeea08
--- /dev/null
+++ b/automation/admin/app/models/protocol.rb
@@ -0,0 +1,4 @@
+class Protocol < ActiveRecord::Base
+  validates_presence_of :user_accession, :expt_accession
+  validates_uniqueness_of :accession
+end
diff --git a/automation/admin/app/models/quantitation_type.rb b/automation/admin/app/models/quantitation_type.rb
new file mode 100644
index 0000000..b75403c
--- /dev/null
+++ b/automation/admin/app/models/quantitation_type.rb
@@ -0,0 +1,7 @@
+class QuantitationType < ActiveRecord::Base
+
+  has_and_belongs_to_many :experiments
+
+  validates_presence_of :name
+
+end
diff --git a/automation/admin/app/models/role.rb b/automation/admin/app/models/role.rb
new file mode 100644
index 0000000..9035ba6
--- /dev/null
+++ b/automation/admin/app/models/role.rb
@@ -0,0 +1,7 @@
+# See <a href="http://wiki.rubyonrails.com/rails/show/AccessControlListExample">http://wiki.rubyonrails.com/rails/show/AccessControlListExample</a>
+# and <a href="http://wiki.rubyonrails.com/rails/show/LoginGeneratorAccessControlList">http://wiki.rubyonrails.com/rails/show/LoginGeneratorAccessControlList</a>
+
+class Role < ActiveRecord::Base
+  has_and_belongs_to_many :permissions
+  has_and_belongs_to_many :users
+end
diff --git a/automation/admin/app/models/spreadsheet.rb b/automation/admin/app/models/spreadsheet.rb
new file mode 100644
index 0000000..ece9581
--- /dev/null
+++ b/automation/admin/app/models/spreadsheet.rb
@@ -0,0 +1,3 @@
+class Spreadsheet < ActiveRecord::Base
+  has_one :experiment
+end
diff --git a/automation/admin/app/models/taxon.rb b/automation/admin/app/models/taxon.rb
new file mode 100644
index 0000000..427854b
--- /dev/null
+++ b/automation/admin/app/models/taxon.rb
@@ -0,0 +1,8 @@
+class Taxon < ActiveRecord::Base
+  has_many :organisms
+  has_and_belongs_to_many :categories
+  validates_presence_of :accession, :common_name, :scientific_name
+  validates_uniqueness_of :accession, :common_name, :scientific_name
+  validates_numericality_of :accession, :only_integer => true, :message => "is not an integer"
+  validates_associated :categories
+end
diff --git a/automation/admin/app/models/user.rb b/automation/admin/app/models/user.rb
new file mode 100644
index 0000000..3e4fa7c
--- /dev/null
+++ b/automation/admin/app/models/user.rb
@@ -0,0 +1,59 @@
+#require 'digest/md5'
+
+# this model expects a certain database layout and its based on the name/login pattern. 
+class User < ActiveRecord::Base
+
+  has_and_belongs_to_many :roles
+  has_many :experiments
+  validates_presence_of :login
+  validates_uniqueness_of :login
+
+  # Return true/false if User is authorized for resource.
+  def authorized?(resource)
+    match=false
+    permission_strings.each do |p|
+      r = Regexp.new(p)
+      match = match || ((r =~ resource) != nil)
+    end
+    return match
+  end
+
+  # Load permission strings 
+  def permission_strings
+    a = []
+    self.roles.each{|r| r.permissions.each{|p| a<< p.name }}
+    a
+  end
+
+  # Autogenerated code below
+
+  def self.authenticate(login, pass)
+ 
+    # Line below has been changed from md5(pass) to allow cleartext
+    # password storage (yes, I know this is bad, but sometimes curators
+    # want to be able to tell a user their password.
+    find( :first, :conditions => ["login = ? AND password = ?", login, pass])
+  end  
+
+  def change_password(pass)
+    update_attribute "password", pass # changed from self.class.md5(pass)
+  end
+    
+  protected
+
+#  def self.md5(pass)
+#    Digest::MD5.hexdigest("#{pass}")
+#  end
+    
+  before_create :crypt_password
+  
+  def crypt_password
+    write_attribute("password", password) # changed from self.class.md5(password)
+  end
+
+  validates_length_of :login, :within => 3..40
+  validates_length_of :password, :within => 5..40
+  validates_presence_of :login, :password, :password_confirmation
+  validates_uniqueness_of :login, :on => :save
+  validates_confirmation_of :password, :on => :save     
+end
diff --git a/automation/admin/app/views/account/login.rhtml b/automation/admin/app/views/account/login.rhtml
new file mode 100644
index 0000000..bcb4d97
--- /dev/null
+++ b/automation/admin/app/views/account/login.rhtml
@@ -0,0 +1,22 @@
+<% form_tag :action=> "login" do %>
+
+<div title="Account login" id="loginform" class="form">
+    <h3>Please login</h3>
+    
+    <% if @message %>
+      <div id="message"><%= @message %></div>
+    <% end %>
+
+    <label for="user_login">Login:</label><br/>
+    <input type="text" name="user_login" id="user_login" size="30" value=""/><br/>
+
+    <label for="user_password">Password:</label><br/>
+    <input type="password" name="user_password" id="user_password" size="30"/>
+
+    <br/>
+    <input type="submit" name="login" value="Login »" class="primary" />
+
+</div>
+
+<% end %>
+
diff --git a/automation/admin/app/views/account/logout.rhtml b/automation/admin/app/views/account/logout.rhtml
new file mode 100644
index 0000000..6a3d22c
--- /dev/null
+++ b/automation/admin/app/views/account/logout.rhtml
@@ -0,0 +1,10 @@
+
+<div class="memo">
+    <h3>Logoff</h3>
+
+    <p>You are now logged out of the system...</p>
+      
+    <%= link_to "« login", :action=>"login"%>  
+      
+</div>
+
diff --git a/automation/admin/app/views/account/signup.rhtml b/automation/admin/app/views/account/signup.rhtml
new file mode 100644
index 0000000..65cab57
--- /dev/null
+++ b/automation/admin/app/views/account/signup.rhtml
@@ -0,0 +1,18 @@
+<% form_tag :action=> "signup" do %>
+
+<div title="Account signup" id="signupform" class="form">
+  <h3>Signup</h3>
+  <%= error_messages_for 'user' %><br/>
+  
+  <label for="user_login">Desired login:</label><br/>
+  <%= text_field "user", "login", :size => 30 %><br/>
+  <label for="user_password">Choose password:</label><br/>
+  <%= password_field "user", "password", :size => 30 %><br/>
+  <label for="user_password_confirmation">Confirm password:</label><br/>
+  <%= password_field "user", "password_confirmation", :size => 30 %><br/>
+
+<input type="submit" value="Signup »" class="primary" />
+
+</div>
+<% end %>
+
diff --git a/automation/admin/app/views/account/welcome.rhtml b/automation/admin/app/views/account/welcome.rhtml
new file mode 100644
index 0000000..a612f10
--- /dev/null
+++ b/automation/admin/app/views/account/welcome.rhtml
@@ -0,0 +1,12 @@
+
+<div class="memo">
+    <h3>Welcome</h3>
+
+    <p>You are now logged into the system...</p>
+    <p>
+    Please select one of the links in the menu panel to the left.
+    </p>
+      
+    <%= link_to "« logout", :action=>"logout"%>  
+      
+</div>
diff --git a/automation/admin/app/views/array_designs/_aedwinfo.rhtml b/automation/admin/app/views/array_designs/_aedwinfo.rhtml
new file mode 100755
index 0000000..85ea2ed
--- /dev/null
+++ b/automation/admin/app/views/array_designs/_aedwinfo.rhtml
@@ -0,0 +1,18 @@
+
+  <p class="text">Explanation of AEDW scores:</p>
+  <ul>
+    <li>There must be a database entry (which matches the expected pattern) from an approved database for at least      
+50% of experimental features. At present the list of approved databases is:
+      <ul>
+        <li>swissprot</li>
+        <li>trembl</li>
+        <li>swall</li>
+        <li>flybase</li>
+        <li>genedb</li>
+        <li>mgd</li>
+        <li>sgd</li>
+        <li>rgd</li>
+        <li>wormbase</li>
+      </ul>
+    </li>
+  </ul>
diff --git a/automation/admin/app/views/array_designs/_form.rhtml b/automation/admin/app/views/array_designs/_form.rhtml
new file mode 100644
index 0000000..f8de120
--- /dev/null
+++ b/automation/admin/app/views/array_designs/_form.rhtml
@@ -0,0 +1,44 @@
+<%= error_messages_for 'array_design' %>
+
+<table>
+  <tr>
+    <%= content_tag("td", "Submission ID:") %>
+    <%= content_tag("td", @array_design.miamexpress_subid) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Accession:") %>
+    <%= content_tag("td", input("array_design", "accession")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Name:") %>
+    <%= content_tag("td", @array_design.name) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "User:") %>
+    <%= content_tag("td", @array_design.miamexpress_login) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Status:") %>
+    <%= content_tag("td", input("array_design", "status")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "DW ready:") %>
+    <%= content_tag("td", check_box("array_design", "data_warehouse_ready")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Loaded in DW:") %>
+    <%= content_tag("td", @array_design.in_data_warehouse? ? 'YES' : 'no') %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "MIAME score:") %>
+    <%= content_tag("td", @array_design.miame_score? ? 'Pass' : 'Fail') %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Date last processed:") %>
+    <%= content_tag("td", @array_design.date_last_processed) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Comment:") %>
+    <%= content_tag("td", input("array_design", "comment")) %>
+  </tr>
+</table>
diff --git a/automation/admin/app/views/array_designs/_miameinfo.rhtml b/automation/admin/app/views/array_designs/_miameinfo.rhtml
new file mode 100755
index 0000000..c03e501
--- /dev/null
+++ b/automation/admin/app/views/array_designs/_miameinfo.rhtml
@@ -0,0 +1,6 @@
+
+  <p class="text">Explanation of MIAME scores:</p>
+  <ul>
+    <li>Oligo arrays must have sequence for at least 70% of experimental features.</li>
+    <li>Non-oligo arrays must have sequence or any database entry for at least 70% of experimental features.</li>
+  </ul>
diff --git a/automation/admin/app/views/array_designs/_title.rhtml b/automation/admin/app/views/array_designs/_title.rhtml
new file mode 100644
index 0000000..fd953b9
--- /dev/null
+++ b/automation/admin/app/views/array_designs/_title.rhtml
@@ -0,0 +1,3 @@
+<%= content_tag("h1", "Array Designs", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
diff --git a/automation/admin/app/views/array_designs/edit.rhtml b/automation/admin/app/views/array_designs/edit.rhtml
new file mode 100644
index 0000000..2d110db
--- /dev/null
+++ b/automation/admin/app/views/array_designs/edit.rhtml
@@ -0,0 +1,10 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag :action => "update", :id => @array_design.id do %>
+    <%= render_partial "form" %>
+    <%= hidden_field_tag("page", params[:page]) %>
+    <%= hidden_field_tag("search_term", params[:search_term]) %>
+    <%= content_tag("p", submit_tag("Update")) %>
+  <% end %>
+</div>
diff --git a/automation/admin/app/views/array_designs/list.rhtml b/automation/admin/app/views/array_designs/list.rhtml
new file mode 100644
index 0000000..c99608c
--- /dev/null
+++ b/automation/admin/app/views/array_designs/list.rhtml
@@ -0,0 +1,70 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag do %>
+	<%= content_tag("span", "Search on SubId, Accession, Name or User (wildcards: *, ?): ") %>
+	<%= text_field_tag("search_term", @search_term) %>
+	<%= submit_tag("Search") %>
+  <% end %>
+</div>
+
+<div>
+  <table>
+    <tr>
+      <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+      <%= content_tag("th", "SubID") %>
+      <%= content_tag("th", "Accession") %>
+      <%= content_tag("th", "Name") %>
+      <%= content_tag("th", "User") %>
+      <%= content_tag("th", "Status") %>
+      <%= content_tag("th", "MIAME") %>
+      <%= content_tag("th", "DW status") %>
+      <%= content_tag("th", "Date last processed") %>
+      <%= content_tag("th", "Comment") %>
+      <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+    </tr>
+
+    <% @array_designs.each do |a| %>
+      <tr>
+	<td>
+	<%= link_to "Edit", :action      => "edit", 
+                            :id          => a.id,
+			    :page        => params[:page],
+	                    :search_term => @search_term %>
+        </td>
+	<%= content_tag("td", a.miamexpress_subid) %>
+	<%= content_tag("td", a.accession) %>
+	<%= content_tag("td", a.name) %>
+	<%= content_tag("td", a.miamexpress_login) %>
+	<%= content_tag("td", a.status) %>
+	<%= content_tag("td", a.miame_score.nil? ? '' :
+                              a.miame_score? ?
+                              '<font color="green">PASS</font>' :
+                              '<font color="red">fail</font>') %>
+	<%= content_tag("td", a.in_data_warehouse? ? '<font color="blue">Loaded</font>' :
+	                      a.data_warehouse_ready.nil? ? '' :
+                              a.data_warehouse_ready? ?
+                              '<font color="green">YES</font>' :
+                              '<font color="red">no</font>') %>
+	<%= content_tag("td", a.date_last_processed ?
+			      a.date_last_processed.strftime('%Y-%m-%d') :
+                              "") %>
+	<%= content_tag("td", a.comment) %>
+	<td>
+        <%= link_to "Delete", {:action      => "deprecate", 
+                               :id          => a.id,
+	                       :page        => params[:page],
+	                       :search_term => @search_term },
+                               :confirm     => "Really delete array design \"#{a.accession}\"?" %>
+        </td>
+      </tr> 
+    <% end %>
+  </table>
+
+  <%= will_paginate @array_designs, :class  => 'footer',
+	                            :params => { :search_term => params[:search_term] } %>
+
+  <%= render_partial "miameinfo" %>
+  <%= render_partial "aedwinfo" %>
+
+</div>
diff --git a/automation/admin/app/views/array_designs/list_all.rhtml b/automation/admin/app/views/array_designs/list_all.rhtml
new file mode 100644
index 0000000..2b27aa1
--- /dev/null
+++ b/automation/admin/app/views/array_designs/list_all.rhtml
@@ -0,0 +1,59 @@
+<%= content_tag("h1", "All Arrays", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
+
+<div>
+  <% form_tag do %>
+	<%= content_tag("span", "Search on Accession or Comment (wildcards: *, ?): ") %>
+	<%= text_field_tag("search_term", @search_term) %>
+	<%= submit_tag("Search") %>
+        <%= content_tag("span", "  List all results on one page:") %>
+        <%= check_box_tag("list_all", 1, params[:list_all] ) %>
+  <% end %>
+</div>
+
+<div>
+  <table>
+    <tr>
+      <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+      <%= content_tag("th", "Accession") %>
+      <%= content_tag("th", "Species") %>
+      <%= content_tag("th", "Annotation Source") %>
+      <%= content_tag("th", "Annotation Version") %>
+      <%= content_tag("th", "BioMart Table Name") %>
+      <%= content_tag("th", "Release Date") %>
+      <%= content_tag("th", "Is Released") %>
+      <%= content_tag("th", "Loaded in DW") %>
+      <%= content_tag("th", "Comment") %>
+    </tr>
+
+    <% @array_designs.each do |a| %>
+      <tr>
+	<td>
+	<%= link_to "Details", :action      => "show", 
+                               :id          => a.id %>
+        </td>
+	<%= content_tag("td", a.accession) %>
+	<%= content_tag("td", a.organisms.collect{ |o| o.scientific_name}.join(", ")) %>
+	<%= content_tag("td", a.annotation_source) %>
+	<%= content_tag("td", a.annotation_version) %>
+	<%= content_tag("td", a.biomart_table_name) %>
+	<%= content_tag("td", a.release_date ?
+			      a.release_date.strftime('%Y-%m-%d') :
+                              "") %>
+	<%= content_tag("td", a.is_released.nil? ? '' :
+                              a.is_released? ?
+                              '<font color="green">PUBLIC</font>' :
+                              '<font color="red">private</font>') %>
+	<%= content_tag("td", a.in_data_warehouse?        ?
+                              '<font color="green">YES</font>' :
+                              '<font color="red">no</font>') %>
+	<%= content_tag("td", truncate(a.comment, 30)) %>
+      </tr> 
+    <% end %>
+  </table>
+
+  <%= will_paginate @array_designs, :class  => 'footer',
+	                            :params => { :search_term => params[:search_term] } %>
+
+</div>
diff --git a/automation/admin/app/views/array_designs/new.rhtml b/automation/admin/app/views/array_designs/new.rhtml
new file mode 100644
index 0000000..c70be0f
--- /dev/null
+++ b/automation/admin/app/views/array_designs/new.rhtml
@@ -0,0 +1,8 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag :action => 'create' do %>
+    <%= render_partial "form" %>
+    <%= content_tag("p", submit_tag("Create")) %>
+  <% end %>
+</div>
diff --git a/automation/admin/app/views/array_designs/show.rhtml b/automation/admin/app/views/array_designs/show.rhtml
new file mode 100644
index 0000000..71f5009
--- /dev/null
+++ b/automation/admin/app/views/array_designs/show.rhtml
@@ -0,0 +1,84 @@
+<%= content_tag("h1", @array_design.accession, :class => "title") %>
+<%= content_tag("h4", "This array_design is currently " +
+	                 (@array_design.is_released.nil? ? '' :
+                              @array_design.is_released? ?
+                              '<font color="green">Public</font>' :
+                              '<font color="red">Private</font>') +
+			" (release date: " +
+			      (@array_design.release_date ?
+			      @array_design.release_date.strftime('%Y-%m-%d') :
+                              "") + ")" ) %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
+
+<table width="100%">
+<tr>
+  <%= content_tag("th", "Accession") %>
+  <%= content_tag("td", @array_design.accession) %>
+</tr>
+<tr>
+  <%= content_tag("th", "Species") %>
+  <%= content_tag("td", @array_design.organisms.collect{ |o| o.scientific_name }.join(", ")) %>
+</tr>
+<tr>
+  <%= content_tag("th", "Annotation Source") %>
+  <%= content_tag("td", @array_design.annotation_source) %>
+</tr>
+<tr>
+  <%= content_tag("th", "Annotation Version") %>
+  <%= content_tag("td", @array_design.annotation_version) %>
+</tr>
+<tr>
+  <%= content_tag("th", "BioMart Table Name") %>
+  <%= content_tag("td", @array_design.biomart_table_name) %>
+</tr>
+</table>
+
+<hr/>
+	
+<table width="100%">
+<tr>
+  <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+  <%= content_tag("th", "Event") %>
+  <%= content_tag("th", "Target DB") %>
+  <%= content_tag("th", "Operator") %>
+  <%= content_tag("th", "Start Time") %>
+  <%= content_tag("th", "End Time") %>
+  <%= content_tag("th", "Comment") %>
+</tr>
+<% for event in @array_design.events.sort_by{ |e| e.start_time || Time.local(0) } %>
+  <tr>
+    <td>
+    <%= link_to "Edit", :controller      => "event",
+	                :action          => "edit", 
+                        :id              => event.id %>
+    </td>
+    <%= content_tag("td", "<font color='#{ (event.was_successful.nil? ? 'black' :
+	  				    event.was_successful == 0 ? 'red' :
+					    'green') }'>" + event.event_type + '</font>') %>
+    <%= content_tag("td", event.target_db) %>
+    <%= content_tag("td", event.operator) %>
+    <%= content_tag("td", event.start_time) %>
+    <%= content_tag("td", event.end_time) %>
+    <%= content_tag("td", truncate(event.comment, 30)) %>
+  </tr>
+<% end %>
+</table>
+
+<p class="text"><font color="green">Green</font> events were
+successful, while <font color="red">Red</font> events failed. Black
+events are unfinished.</p>
+
+<div>
+  <% form_tag :action => "update", :id => @array_design.id do %>
+<table>
+  <tr>
+    <%= content_tag("td", "Comment:") %>
+    <%= content_tag("td", input("array_design", "comment")) %>
+  </tr>
+</table>
+    <%= content_tag("p", submit_tag("Update")) %>
+  <% end %>
+</div>
+
+<%= link_to 'Back', :action => 'list' %>
diff --git a/automation/admin/app/views/category/_form.rhtml b/automation/admin/app/views/category/_form.rhtml
new file mode 100644
index 0000000..934b7dd
--- /dev/null
+++ b/automation/admin/app/views/category/_form.rhtml
@@ -0,0 +1,34 @@
+<%= error_messages_for 'category' %>
+
+<table><tr>
+<td><a href="http://mged.sourceforge.net/ontologies/MOhtml/">MGED Ontology webpage</a></td>
+</tr><table>
+
+<table>
+  <tr>
+    <%= content_tag("td", "Ontology term:") %>
+    <%= content_tag("td", input("category", "ontology_term")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Display label:") %>
+    <%= content_tag("td", input("category", "display_label")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Can be a BioMaterialCharacteristic:") %>
+    <%= content_tag("td", check_box("category", "is_bmc")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Can be a FactorValue:") %>
+    <%= content_tag("td", check_box("category", "is_fv")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Is common to all spreadsheets:") %>
+    <%= content_tag("td", check_box("category", "is_common")) %>
+  </tr>
+</table>
+
+<p class="text">Use the check boxes to indicate whether a given
+category can be used as a BioMaterialCharacteristic or FactorValue
+(examples: Compound, Dose are FVs, but not BMCs). If a category should
+be asked of <em>all</em> submissions (e.g. Species,
+BioSourceProvider), check the "common" box.</p>
diff --git a/automation/admin/app/views/category/_title.rhtml b/automation/admin/app/views/category/_title.rhtml
new file mode 100644
index 0000000..66796f1
--- /dev/null
+++ b/automation/admin/app/views/category/_title.rhtml
@@ -0,0 +1,3 @@
+<%= content_tag("h1", "Characteristic Categories", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
diff --git a/automation/admin/app/views/category/edit.rhtml b/automation/admin/app/views/category/edit.rhtml
new file mode 100644
index 0000000..9ac789a
--- /dev/null
+++ b/automation/admin/app/views/category/edit.rhtml
@@ -0,0 +1,8 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag :action => "update", :id => @category.id do %>
+    <%= render_partial "form" %>
+    <%= submit_tag "Update" %>
+  <% end %>
+</div>
diff --git a/automation/admin/app/views/category/list.rhtml b/automation/admin/app/views/category/list.rhtml
new file mode 100644
index 0000000..97b6fa2
--- /dev/null
+++ b/automation/admin/app/views/category/list.rhtml
@@ -0,0 +1,42 @@
+<%= render_partial "title" %>
+
+<div>
+  <table>
+    <tr>
+      <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+      <%= content_tag("th", "Ontology term") %>
+      <%= content_tag("th", "Display label") %>
+      <%= content_tag("th", "Is common") %>
+      <%= content_tag("th", "Can be a BMC") %>
+      <%= content_tag("th", "Can be a FV") %>
+      <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+    </tr>
+
+    <% @categories.each do |cat| %>
+      <tr>
+	<td>
+	<%= link_to "Edit", :action => "edit", 
+                            :id     => cat.id %>
+        </td>
+	<%= content_tag("td", cat.ontology_term) %>
+	<%= content_tag("td", cat.display_label) %>
+	<%= content_tag("td", cat.is_common? ? 'Yes' : 'No') %>
+	<%= content_tag("td", cat.is_bmc?    ? 'Yes' : 'No') %>
+	<%= content_tag("td", cat.is_fv?     ? 'Yes' : 'No') %>
+	<td>
+        <%= link_to "Delete", {:action  => "deprecate", 
+                               :id      => cat.id},
+                               :confirm => "Really delete category \"#{cat.ontology_term}\"?" %>
+        </td>
+      </tr> 
+    <% end %>
+  </table>
+
+  <%= will_paginate @categories, :class  => 'footer',
+	                         :params => { :search_term => params[:search_term] }  %>
+
+  <%= content_tag("div", link_to("Add new category", 
+                        :controller => "category", 
+                        :action     => "new"), { :class => 'footer' } ) %>
+
+</div>
diff --git a/automation/admin/app/views/category/new.rhtml b/automation/admin/app/views/category/new.rhtml
new file mode 100644
index 0000000..d920995
--- /dev/null
+++ b/automation/admin/app/views/category/new.rhtml
@@ -0,0 +1,8 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag :action => 'create' do %>
+    <%= render_partial "form" %>
+    <%= submit_tag "Create" %>
+  <% end %>
+</div>
diff --git a/automation/admin/app/views/design/_form.rhtml b/automation/admin/app/views/design/_form.rhtml
new file mode 100644
index 0000000..d7d7aaa
--- /dev/null
+++ b/automation/admin/app/views/design/_form.rhtml
@@ -0,0 +1,43 @@
+<%= error_messages_for 'design' %>
+
+<table><tr>
+<td><a href="http://mged.sourceforge.net/ontologies/MOhtml/">MGED Ontology webpage</a></td>
+</tr><table>
+
+<table>
+  <tr>
+    <%= content_tag("td", "Display label:") %>
+    <%= content_tag("td", input("design", "display_label")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Ontology category:") %>
+    <%= content_tag("td", input("design", "ontology_category")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Ontology value:") %>
+    <%= content_tag("td", input("design", "ontology_value")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Design type:") %>
+    <%= content_tag("td", select(
+	"design",
+	"design_type",
+	['biological',
+         'methodological',
+	 'technological'
+	])) %>
+  </tr>
+</table>
+
+<table>
+  <% Category.find(:all, :conditions => "is_deleted=0").sort_by { |c| c.ontology_term }.each do |cat| %>
+    <tr>
+      <td>
+        <%= check_box_tag("design[category_ids][]",
+                           cat.id,
+                           @design.categories.include?(cat)) %>
+      </td>
+      <%= content_tag("td", cat.ontology_term) %>
+    </tr>
+  <% end %>
+</table>
diff --git a/automation/admin/app/views/design/_title.rhtml b/automation/admin/app/views/design/_title.rhtml
new file mode 100644
index 0000000..1bbd5b8
--- /dev/null
+++ b/automation/admin/app/views/design/_title.rhtml
@@ -0,0 +1,3 @@
+<%= content_tag("h1", "Designs", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
diff --git a/automation/admin/app/views/design/edit.rhtml b/automation/admin/app/views/design/edit.rhtml
new file mode 100644
index 0000000..5ce34ba
--- /dev/null
+++ b/automation/admin/app/views/design/edit.rhtml
@@ -0,0 +1,8 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag :action => "update", :id => @design.id do %>
+    <%= render_partial "form" %>
+    <%= submit_tag "Update" %>
+  <% end %>
+</div>
\ No newline at end of file
diff --git a/automation/admin/app/views/design/list.rhtml b/automation/admin/app/views/design/list.rhtml
new file mode 100644
index 0000000..c3cb44d
--- /dev/null
+++ b/automation/admin/app/views/design/list.rhtml
@@ -0,0 +1,40 @@
+<%= render_partial "title" %>
+
+<div>
+  <table>
+    <tr>
+      <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+      <%= content_tag("th", "Display label") %>
+      <%= content_tag("th", "Ontology category") %>
+      <%= content_tag("th", "Ontology value") %>
+      <%= content_tag("th", "Design type") %>
+      <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+    </tr>
+
+    <% @designs.each do |d| %>
+      <tr>
+	<td>
+	<%= link_to "Edit", :action => "edit", 
+                            :id     => d.id %>
+        </td>
+	<%= content_tag("td", d.display_label) %>
+	<%= content_tag("td", d.ontology_category) %>
+	<%= content_tag("td", d.ontology_value) %>
+	<%= content_tag("td", d.design_type) %>
+	<td>
+        <%= link_to "Delete", {:action  => "deprecate", 
+                               :id      => d.id},
+                               :confirm => "Really delete design \"#{d.ontology_value}\"?" %>
+        </td>
+      </tr> 
+    <% end %>
+  </table>
+
+  <%= will_paginate @designs, :class  => 'footer',
+                              :params => { :search_term => params[:search_term] }  %>
+
+  <%= content_tag("div", link_to("Add new design", 
+                        :controller => "design", 
+                        :action     => "new"), { :class => 'footer' } ) %>
+
+</div>
diff --git a/automation/admin/app/views/design/new.rhtml b/automation/admin/app/views/design/new.rhtml
new file mode 100644
index 0000000..c2334f3
--- /dev/null
+++ b/automation/admin/app/views/design/new.rhtml
@@ -0,0 +1,8 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag :action => 'create' do %>
+    <%= render_partial "form" %>
+    <%= submit_tag "Create" %>
+  <% end %>
+</div>
\ No newline at end of file
diff --git a/automation/admin/app/views/event/_form.rhtml b/automation/admin/app/views/event/_form.rhtml
new file mode 100644
index 0000000..1345a22
--- /dev/null
+++ b/automation/admin/app/views/event/_form.rhtml
@@ -0,0 +1,62 @@
+<%= error_messages_for 'event' %>
+
+<div>
+  <% form_tag :action => "update", :id => @event.id do %>
+
+<table>
+  <tr>
+    <%= content_tag("th", "Associated Array:") %>
+    <%= content_tag("td", ( @event.array_design.nil? ? '' :
+				link_to( @event.array_design.accession,
+  					 :controller => 'array_designs',
+					 :action     => 'show',
+					 :id         => @event.array_design.id) ) ) %>
+  </tr>
+  <tr>
+    <%= content_tag("th", "Associated Experiment:") %>
+    <%= content_tag("td", ( @event.experiment.nil? ? '' :
+				link_to( @event.experiment.accession,
+  					 :controller => 'experiments',
+					 :action     => 'show',
+					 :id         => @event.experiment.id) ) ) %>
+  </tr>
+  <tr>
+    <%= content_tag("th", "Event type:") %>
+    <%= content_tag("td", @event.event_type) %>
+  </tr>
+  <tr>
+    <%= content_tag("th", "Operator:") %>
+    <%= content_tag("td", @event.operator) %>
+  </tr>
+  <tr>
+    <%= content_tag("th", "Source Database:") %>
+    <%= content_tag("td", @event.source_db) %>
+  </tr>
+  <tr>
+    <%= content_tag("th", "Target Database:") %>
+    <%= content_tag("td", @event.target_db) %>
+  </tr>
+  <tr>
+    <%= content_tag("th", "Was Successful:") %>
+    <%= content_tag("td", @event.was_successful.nil? ? '' :
+                              @event.was_successful? ?
+                              '<font color="green">YES</font>' :
+                              '<font color="red">no</font>') %>
+  </tr>
+  <tr>
+    <%= content_tag("th", "Start Time:") %>
+    <%= content_tag("td", @event.start_time) %>
+  </tr>
+  <tr>
+    <%= content_tag("th", "End Time:") %>
+    <%= content_tag("td", @event.end_time) %>
+  </tr>
+  <tr>
+    <%= content_tag("th", "Comment:") %>
+    <%= content_tag("td", input("event", "comment")) %>
+  </tr>
+</table>
+
+    <%= content_tag("p", submit_tag("Update")) %>
+  <% end %>
+</div>
\ No newline at end of file
diff --git a/automation/admin/app/views/event/_title.rhtml b/automation/admin/app/views/event/_title.rhtml
new file mode 100644
index 0000000..e48f90d
--- /dev/null
+++ b/automation/admin/app/views/event/_title.rhtml
@@ -0,0 +1,3 @@
+<%= content_tag("h1", "Editing Event", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
diff --git a/automation/admin/app/views/event/edit.rhtml b/automation/admin/app/views/event/edit.rhtml
new file mode 100644
index 0000000..9a0b0ea
--- /dev/null
+++ b/automation/admin/app/views/event/edit.rhtml
@@ -0,0 +1,2 @@
+<%= render_partial "title" %>
+<%= render_partial "form" %>
diff --git a/automation/admin/app/views/experiments/_annotate.rhtml b/automation/admin/app/views/experiments/_annotate.rhtml
new file mode 100644
index 0000000..595caa0
--- /dev/null
+++ b/automation/admin/app/views/experiments/_annotate.rhtml
@@ -0,0 +1,52 @@
+<table>
+<tr>
+<th>Designs</th>
+<th>Materials</th>
+<th>Organisms</th>
+</tr>
+
+<tr>
+<td valign="top">
+<table>
+  <% Design.find(:all, :conditions => "is_deleted=0").sort_by { |d| d.ontology_value }.each do |design| %>
+    <tr>
+      <td>
+        <%= check_box_tag("annotation[design_ids][]",
+                           design.id,
+                           @experiment.designs.include?(design)) %>
+      </td>
+      <%= content_tag("td", design.display_label) %>
+    </tr>
+  <% end %>
+</table>
+</td>
+<td valign="top">
+<table>
+  <% Material.find(:all, :conditions => "is_deleted=0").sort_by { |d| d.ontology_value }.each do |material| %>
+    <tr>
+      <td>
+        <%= check_box_tag("annotation[material_ids][]",
+                           material.id,
+                           @experiment.materials.include?(material)) %>
+      </td>
+      <%= content_tag("td", material.display_label) %>
+    </tr>
+  <% end %>
+</table>
+</td>
+<td valign="top">
+<table>
+  <% Organism.find(:all, :conditions => "is_deleted=0 and taxon_id is not null").sort_by { |d| d.scientific_name }.each do |organism| %>
+    <tr>
+      <td>
+        <%= check_box_tag("annotation[organism_ids][]",
+                           organism.id,
+                           @experiment.organisms.include?(organism)) %>
+      </td>
+      <%= content_tag("td", organism.scientific_name) %>
+    </tr>
+  <% end %>
+</table>
+</td>
+</tr>
+</table>
\ No newline at end of file
diff --git a/automation/admin/app/views/experiments/_form.rhtml b/automation/admin/app/views/experiments/_form.rhtml
new file mode 100644
index 0000000..3aa1780
--- /dev/null
+++ b/automation/admin/app/views/experiments/_form.rhtml
@@ -0,0 +1,26 @@
+<%= error_messages_for 'experiment' %>
+
+<table>
+  <tr>
+    <%= content_tag("td", 'User login:') %>
+    <%= content_tag("td", select(
+	"experiment",
+	"user_id",
+	User.find(:all).collect { |t| [t.login, t.id] }.sort_by { |login, id| login },
+	{ :include_blank => true })) %>
+  </tr>
+  <% Experiment.content_columns.each do |c| %>
+    <tr>
+      <%= content_tag("td", c.name.humanize.concat(":")) %>
+      <%= content_tag("td", input("experiment", c.name)) %>
+    </tr>
+  <% end %>
+  <tr>
+    <%= content_tag("td", "Spreadsheet:") %>
+    <%= content_tag("td", @experiment.spreadsheets.collect {|s| s.name}) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Data files:") %>
+    <%= content_tag("td", @experiment.data_files.collect {|d| d.name}) %>
+  </tr>
+</table>
diff --git a/automation/admin/app/views/experiments/_title.rhtml b/automation/admin/app/views/experiments/_title.rhtml
new file mode 100644
index 0000000..7c3e18b
--- /dev/null
+++ b/automation/admin/app/views/experiments/_title.rhtml
@@ -0,0 +1,3 @@
+<%= content_tag("h1", "All Experiments", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
diff --git a/automation/admin/app/views/experiments/edit.rhtml b/automation/admin/app/views/experiments/edit.rhtml
new file mode 100644
index 0000000..b7f86e4
--- /dev/null
+++ b/automation/admin/app/views/experiments/edit.rhtml
@@ -0,0 +1,12 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag :action => "update", :id => @experiment.id do %>
+    <%= render_partial "form" %>
+    <%= hidden_field_tag("page", params[:page]) %>
+    <%= hidden_field_tag("search_term", params[:search_term]) %>
+    <%= content_tag("p", submit_tag("Update")) %>
+    <%= render_partial "annotate" %>
+    <%= content_tag("p", submit_tag("Update")) %>
+  <% end %>
+</div>
\ No newline at end of file
diff --git a/automation/admin/app/views/experiments/list.rhtml b/automation/admin/app/views/experiments/list.rhtml
new file mode 100644
index 0000000..9cc4cb7
--- /dev/null
+++ b/automation/admin/app/views/experiments/list.rhtml
@@ -0,0 +1,71 @@
+<%= render_partial "title" %>
+
+<div>
+	<% form_tag do %>
+	<%= content_tag("span", "Search on Accession or Comment (wildcards: *, ?): ") %>
+	<%= text_field_tag("search_term", @search_term) %>
+	<%= submit_tag("Search") %>
+        <%= content_tag("span", "  List all results on one page:") %>
+        <%= check_box_tag("list_all", 1, params[:list_all] ) %>
+	<% end %>
+</div>
+
+<div>
+  <table>
+    <tr>
+      <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+      <%= content_tag("th", "Accession") %>
+      <%= content_tag("th", "Curated Name") %>
+      <%= content_tag("th", "No. of Samples") %>
+      <%= content_tag("th", "No. of Hybs") %>
+      <%= content_tag("th", "Raw Data") %>
+      <%= content_tag("th", "Processed Data") %>
+      <%= content_tag("th", "AE MIAME Score") %>
+      <%= content_tag("th", "AE DW Score") %>
+      <%= content_tag("th", "Release Date") %>
+      <%= content_tag("th", "Is Released") %>
+      <%= content_tag("th", "Loaded in DW") %>
+      <%= content_tag("th", "Comment") %>
+    </tr>
+
+    <% @experiments.each do |e| %>
+      <tr>
+	<td>
+	<%= link_to "Details", :action          => "show", 
+                               :id              => e.id,
+			       :page            => params[:page],
+	                       :search_term     => @search_term %>
+        </td>
+	<%= content_tag("td", e.accession) %>
+	<%= content_tag("td", e.curated_name) %>
+        <%= content_tag("td", e.num_samples) %>
+	<%= content_tag("td", e.num_hybridizations) %>
+	<%= content_tag("td", e.has_raw_data.nil? ? '' :
+                              e.has_raw_data? ?
+                              '<font color="green">YES</font>' :
+                              '<font color="red">no</font>') %>
+	<%= content_tag("td", e.has_processed_data.nil? ? '' :
+                              e.has_processed_data? ?
+                              '<font color="green">YES</font>' :
+                              '<font color="red">no</font>') %>
+	<%= content_tag("td", e.ae_miame_score) %>
+	<%= content_tag("td", e.ae_data_warehouse_score) %>
+	<%= content_tag("td", e.release_date ?
+			      e.release_date.strftime('%Y-%m-%d') :
+                              "") %>
+	<%= content_tag("td", e.is_released.nil? ? '' :
+                              e.is_released? ?
+                              '<font color="green">PUBLIC</font>' :
+                              '<font color="red">private</font>') %>
+	<%= content_tag("td", e.in_data_warehouse?        ?
+                              '<font color="green">YES</font>' :
+                              '<font color="red">no</font>') %>
+	<%= content_tag("td", truncate(e.comment, 30)) %>
+      </tr> 
+    <% end %>
+  </table>
+
+  <%= will_paginate @experiments, :class  => 'footer',
+	                          :params => { :search_term => params[:search_term] }  %>
+
+</div>
diff --git a/automation/admin/app/views/experiments/new.rhtml b/automation/admin/app/views/experiments/new.rhtml
new file mode 100644
index 0000000..47f9848
--- /dev/null
+++ b/automation/admin/app/views/experiments/new.rhtml
@@ -0,0 +1,10 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag :action => 'create' do %>
+    <%= render_partial "form" %>
+    <%= content_tag("p", submit_tag("Create")) %>
+    <%= render_partial "annotate" %>
+    <%= content_tag("p", submit_tag("Create")) %>
+  <% end %>
+</div>
\ No newline at end of file
diff --git a/automation/admin/app/views/experiments/show.rhtml b/automation/admin/app/views/experiments/show.rhtml
new file mode 100644
index 0000000..d973e45
--- /dev/null
+++ b/automation/admin/app/views/experiments/show.rhtml
@@ -0,0 +1,124 @@
+<%= content_tag("h1", @experiment.accession, :class => "title") %>
+<%= content_tag("h4", "This experiment is currently " +
+	                 (@experiment.is_released.nil? ? '' :
+                              @experiment.is_released? ?
+                              '<font color="green">Public</font>' :
+                              '<font color="red">Private</font>') +
+			" (release date: " +
+			      (@experiment.release_date ?
+			      @experiment.release_date.strftime('%Y-%m-%d') :
+                              "") + ")" ) %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
+
+<table width="100%">
+<tr>
+  <%= content_tag("th", "Accession") %>
+  <%= content_tag("td", @experiment.accession) %>
+</tr>
+<tr>
+  <%= content_tag("th", "Curated Name") %>
+  <%= content_tag("td", @experiment.curated_name) %>
+</tr>
+<tr>
+  <%= content_tag("th", "Submitter's Description") %>
+  <%= content_tag("td", @experiment.submitter_description) %>
+</tr>
+</table>
+
+<hr/>
+
+<table width="100%">
+<tr>
+  <%= content_tag("th", "Species") %>
+  <%= content_tag("td", @experiment.organisms.collect{ |o| o.scientific_name}.join(", ")) %>
+</tr>
+<tr>
+  <%= content_tag("th", "Array Designs") %>
+  <%= content_tag("td", @experiment.array_designs.collect{ |a| a.accession}.join(", ")) %>
+</tr>
+<tr>
+  <%= content_tag("th", "Experimental Factors") %>
+  <%= content_tag("td", @experiment.factors.collect{ |f| f.name}.join(", ")) %>
+</tr>
+<tr>
+  <%= content_tag("th", "Quantitation Types") %>
+  <%= content_tag("td", @experiment.quantitation_types.collect{ |q| q.name}.join(", ")) %>
+</tr>
+</table>
+
+<hr/>
+
+<table width="100%">
+<tr>
+  <%= content_tag("th", "No. of Samples") %>
+  <%= content_tag("td", @experiment.num_samples) %>
+  <%= content_tag("th", "AE MIAME Score") %>
+  <%= content_tag("td", @experiment.ae_miame_score) %>
+  <%= content_tag("th", "Raw Data") %>
+  <%= content_tag("td", @experiment.has_raw_data.nil? ? '' :
+                              @experiment.has_raw_data? ?
+                              '<font color="green">YES</font>' :
+                              '<font color="red">no</font>') %>
+</tr>
+<tr>
+  <%= content_tag("th", "No. of Hybridizations") %>
+  <%= content_tag("td", @experiment.num_hybridizations) %>
+  <%= content_tag("th", "AE DW Score") %>
+  <%= content_tag("td", @experiment.ae_data_warehouse_score) %>
+  <%= content_tag("th", "Processed Data") %>
+  <%= content_tag("td", @experiment.has_processed_data.nil? ? '' :
+                              @experiment.has_processed_data? ?
+                              '<font color="green">YES</font>' :
+                              '<font color="red">no</font>') %>
+</tr>
+</table>
+
+<hr/>
+	
+<table width="100%">
+<tr>
+  <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+  <%= content_tag("th", "Event") %>
+  <%= content_tag("th", "Target DB") %>
+  <%= content_tag("th", "Operator") %>
+  <%= content_tag("th", "Start Time") %>
+  <%= content_tag("th", "End Time") %>
+  <%= content_tag("th", "Comment") %>
+</tr>
+<% for event in @experiment.events.sort_by{ |e| e.start_time || Time.local(0) } %>
+<tr>
+  <td>
+  <%= link_to "Edit", :controller      => "event",
+	              :action          => "edit", 
+                      :id              => event.id %>
+  </td>
+  <%= content_tag("td", "<font color='#{ (event.was_successful.nil? ? 'black' :
+					  event.was_successful == 0 ? 'red' :
+					  'green') }'>" + event.event_type + '</font>') %>
+  <%= content_tag("td", event.target_db) %>
+  <%= content_tag("td", event.operator) %>
+  <%= content_tag("td", event.start_time) %>
+  <%= content_tag("td", event.end_time) %>
+  <%= content_tag("td", truncate(event.comment, 30)) %>
+</tr>
+<% end %>
+</table>
+
+<p class="text"><font color="green">Green</font> events were
+successful, while <font color="red">Red</font> events failed. Black
+events are unfinished.</p>
+
+<div>
+  <% form_tag :action => "update", :id => @experiment.id do %>
+<table>
+  <tr>
+    <%= content_tag("td", "Comment:") %>
+    <%= content_tag("td", input("experiment", "comment")) %>
+  </tr>
+</table>
+    <%= content_tag("p", submit_tag("Update")) %>
+  <% end %>
+</div>
+
+<%= link_to 'Back', :action => 'list' %>
diff --git a/automation/admin/app/views/experiments/today_list.rhtml b/automation/admin/app/views/experiments/today_list.rhtml
new file mode 100644
index 0000000..df481e8
--- /dev/null
+++ b/automation/admin/app/views/experiments/today_list.rhtml
@@ -0,0 +1,61 @@
+<%= content_tag("h1", "Recently processed experiments (as of #{ @time_now.strftime("%Y-%m-%d") })", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
+
+<div>
+  <table>
+    <tr>
+      <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+      <%= content_tag("th", "Type") %>
+      <%= content_tag("th", "Accession") %>
+      <%= content_tag("th", "Directory") %>
+      <%= content_tag("th", "Name") %>
+      <%= content_tag("th", "Checker score") %>
+      <%= content_tag("th", "MIAME score") %>
+      <%= content_tag("th", "DW status") %>
+      <%= content_tag("th", "Status") %>
+      <%= content_tag("th", "Date last processed") %>
+      <%= content_tag("th", "Curator") %>
+      <%= content_tag("th", "Comment") %>
+    </tr>
+
+    <% @experiments.each do |e| %>
+      <tr>
+	<td>
+	<%= link_to "Edit", :action          => "edit", 
+                            :id              => e.id,
+			    :page            => params[:page],
+                            :experiment_type => params[:experiment_type],
+	                    :search_term     => @search_term %>
+        </td>
+        <%= content_tag("td", e.experiment_type.to_s) %>
+	<%= content_tag("td", e.accession) %>
+	<%= content_tag("td", e.experiment_type == 'MIAMExpress' ? '' : e.experiment_type + '_' + e.id.to_s) %>
+	<%= content_tag("td", e.name) %>
+	<%= content_tag("td", e.checker_score) %>
+	<%= content_tag("td", (e.miame_score.to_i.&(1).nonzero? ? 'R' : '') +
+                              (e.miame_score.to_i.&(2).nonzero? ? 'N' : '') +
+                              (e.miame_score.to_i.&(4).nonzero? ? 'F' : '') +
+                              (e.miame_score.to_i.&(8).nonzero? ? 'P' : '') +
+                              (e.miame_score.to_i.&(16).nonzero? ? 'A' : '')) %>
+	<%= content_tag("td", e.in_data_warehouse?        ? '<font color="blue">Loaded</font>' :
+                              e.data_warehouse_ready.nil? ? '' :
+                              e.data_warehouse_ready.to_i.eql?(31) ? '<font color="green">YES</font>' :
+			      e.data_warehouse_ready.to_i.eql?(30) ? '<font color="red">maybe</font>' :
+                              '<font color="red">no</font>') %>
+	<%= content_tag("td", e.status) %>
+	<%= content_tag("td", e.date_last_processed ?
+			e.date_last_processed.localtime.strftime('%Y-%m-%d %H:%M:%S') :
+                        "") %>
+	<%= content_tag("td", e.curator) %>
+	<%= content_tag("td", truncate(e.comment, 100)) %>
+      </tr> 
+    <% end %>
+  </table>
+
+  <%= content_tag("p", "Note that all times are GMT.") %>
+
+  <%= will_paginate @experiments, :class  => 'footer',
+	                          :params => { :search_term => params[:search_term] }  %>
+
+</div>
diff --git a/automation/admin/app/views/layouts/admin.rhtml b/automation/admin/app/views/layouts/admin.rhtml
new file mode 100644
index 0000000..6c24270
--- /dev/null
+++ b/automation/admin/app/views/layouts/admin.rhtml
@@ -0,0 +1,156 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+   <title>Submissions Tracking</title>
+   <%= stylesheet_link_tag "admin" %>
+ </head>
+ <body>
+
+  <div class="link-list" id="sidebar">
+   <div class="submenu">
+    <div class="submenutitle">
+      <%= content_tag("h4", "Submissions") %>
+    </div>
+
+    <div class="menu-item">
+    <%= link_to "Tab2MAGE experiments", 
+                :controller => "tab2mages", 
+                :action => "list",
+	        :experiment_type => "Tab2MAGE" %>
+    </div>
+
+    <div class="menu-item">
+    <%= link_to "MIAMExpress experiments", 
+                :controller => "miamexps", 
+                :action => "list",
+	        :experiment_type => "MIAMExpress" %>
+    </div>
+
+    <div class="menu-item">
+    <%= link_to "MIAMExpress arrays", 
+                :controller => "array_designs", 
+                :action => "list" %>
+    </div>
+
+    <div class="menu-item">
+    <%= link_to "MUGEN experiments", 
+                :controller => "tab2mages", 
+                :action => "list",
+	        :experiment_type => "MUGEN" %>
+    </div>
+
+    <div class="menu-item">
+    <%= link_to "GEO experiments", 
+                :controller => "tab2mages", 
+                :action => "list",
+	        :experiment_type => "GEO" %>
+    </div>
+
+    <div class="menu-item">
+    <%= link_to "MAGE-TAB experiments", 
+                :controller => "tab2mages", 
+                :action => "list",
+	        :experiment_type => "MAGE-TAB" %>
+    </div>
+
+    <div class="menu-item">
+    <%= link_to "Protocols", 
+                :controller => "protocols", 
+                :action => "list" %>
+    </div>
+
+    <div class="menu-item">
+    <%= link_to "Recent experiments", 
+                :controller => "experiments", 
+                :action => "today_list" %>
+    </div>
+   </div>
+
+   <div class="submenu">
+    <div class="submenutitle">
+      <%= content_tag("h4", "Tracking") %>
+    </div>
+
+    <div class="menu-item">
+    <%= link_to "All Experiments", 
+                :controller => "experiments", 
+                :action => "list" %>
+    </div>
+
+    <div class="menu-item">
+    <%= link_to "All Arrays", 
+                :controller => "array_designs", 
+                :action => "list_all" %>
+    </div>
+
+   </div>
+   <div class="submenu">
+    <div class="submenutitle">
+      <%= content_tag("h4", "Ontology") %>
+    </div>
+
+    <div class="menu-item">
+    <%= link_to "Categories", 
+                :controller => "category", 
+                :action => "list" %>
+    </div>
+
+    <div class="menu-item">
+    <%= link_to "Taxa", 
+                :controller => "taxon", 
+                :action => "list" %>
+    </div>
+     
+    <div class="menu-item">
+    <%= link_to "Organisms", 
+                :controller => "organism", 
+                :action => "list" %>
+    </div>
+     
+    <div class="menu-item">
+    <%= link_to "Designs", 
+                :controller => "design", 
+                :action => "list" %>
+    </div>
+
+    <div class="menu-item">
+    <%= link_to "Materials", 
+                :controller => "material", 
+                :action => "list" %>
+    </div>
+   </div>
+
+   <div class="submenu">
+    <div class="submenutitle">
+      <%= content_tag("h4", "Admin") %>
+    </div>
+
+    <div class="menu-item">
+    <%= link_to "User Accounts", 
+                :controller => "users", 
+                :action => "list" %>
+    </div>
+   </div>
+
+   <div class="submenu">
+    <% if !session[:user].nil? %>
+      <div class="submenutitle">
+        <%= content_tag("h4", "Account") %>
+      </div>
+
+    <div class="menu-item">
+      <%= link_to "Log out",
+                  :controller => "account",
+                  :action => "logout" %>
+    </div>
+    <% end %>
+   </div>
+
+  </div>
+  <div id="main">
+
+   <%= @content_for_layout %>
+
+  </div>
+ </body>
+</html>
\ No newline at end of file
diff --git a/automation/admin/app/views/material/_form.rhtml b/automation/admin/app/views/material/_form.rhtml
new file mode 100644
index 0000000..2bbfcdf
--- /dev/null
+++ b/automation/admin/app/views/material/_form.rhtml
@@ -0,0 +1,33 @@
+<%= error_messages_for 'material' %>
+
+<table><tr>
+<td><a href="http://mged.sourceforge.net/ontologies/MOhtml/">MGED Ontology webpage</a></td>
+</tr><table>
+
+<table>
+  <tr>
+    <%= content_tag("td", "Display label:") %>
+    <%= content_tag("td", input("material", "display_label")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Ontology category:") %>
+    <%= content_tag("td", input("material", "ontology_category")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Ontology value:") %>
+    <%= content_tag("td", input("material", "ontology_value")) %>
+  </tr>
+</table>
+
+<table>
+  <% Category.find(:all).sort_by { |c| c.ontology_term }.each do |cat| %>
+    <tr>
+      <td>
+        <%= check_box_tag("material[category_ids][]",
+                           cat.id,
+                           @material.categories.include?(cat)) %>
+      </td>
+      <%= content_tag("td", cat.ontology_term) %>
+    </tr>
+  <% end %>
+</table>
diff --git a/automation/admin/app/views/material/_title.rhtml b/automation/admin/app/views/material/_title.rhtml
new file mode 100644
index 0000000..54f390c
--- /dev/null
+++ b/automation/admin/app/views/material/_title.rhtml
@@ -0,0 +1,3 @@
+<%= content_tag("h1", "Material types", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
diff --git a/automation/admin/app/views/material/edit.rhtml b/automation/admin/app/views/material/edit.rhtml
new file mode 100644
index 0000000..317f304
--- /dev/null
+++ b/automation/admin/app/views/material/edit.rhtml
@@ -0,0 +1,8 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag :action => "update", :id => @material.id do %>
+    <%= render_partial "form" %>
+    <%= submit_tag "Update" %>
+  <% end %>
+</div>
\ No newline at end of file
diff --git a/automation/admin/app/views/material/list.rhtml b/automation/admin/app/views/material/list.rhtml
new file mode 100644
index 0000000..ae79c5b
--- /dev/null
+++ b/automation/admin/app/views/material/list.rhtml
@@ -0,0 +1,38 @@
+<%= render_partial "title" %>
+
+<div>
+  <table>
+    <tr>
+      <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+      <%= content_tag("th", "Display label") %>
+      <%= content_tag("th", "Ontology category") %>
+      <%= content_tag("th", "Ontology value") %>
+      <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+    </tr>
+
+    <% @materials.each do |m| %>
+      <tr>
+	<td>
+	<%= link_to "Edit", :action => "edit", 
+                            :id     => m.id %>
+        </td>
+	<%= content_tag("td", m.display_label) %>
+	<%= content_tag("td", m.ontology_category) %>
+	<%= content_tag("td", m.ontology_value) %>
+	<td>
+        <%= link_to "Delete", {:action  => "deprecate", 
+                               :id      => m.id},
+                               :confirm => "Really delete material \"#{m.ontology_value}\"?" %>
+        </td>
+      </tr> 
+    <% end %>
+  </table>
+
+  <%= will_paginate @materials, :class  => 'footer',
+	                        :params => { :search_term => params[:search_term] }  %>
+
+  <%= content_tag("div", link_to("Add new material", 
+                        :controller => "material", 
+                        :action     => "new"), { :class => 'footer' } ) %>
+
+</div>
diff --git a/automation/admin/app/views/material/new.rhtml b/automation/admin/app/views/material/new.rhtml
new file mode 100644
index 0000000..c2334f3
--- /dev/null
+++ b/automation/admin/app/views/material/new.rhtml
@@ -0,0 +1,8 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag :action => 'create' do %>
+    <%= render_partial "form" %>
+    <%= submit_tag "Create" %>
+  <% end %>
+</div>
\ No newline at end of file
diff --git a/automation/admin/app/views/miamexps/_aedwinfo.rhtml b/automation/admin/app/views/miamexps/_aedwinfo.rhtml
new file mode 100644
index 0000000..ebbb279
--- /dev/null
+++ b/automation/admin/app/views/miamexps/_aedwinfo.rhtml
@@ -0,0 +1,9 @@
+
+  <p class="text">Explanation of AEDW scores:</p>
+  <ul>
+    <li><b>T</b>: Experiment design type is appropriate.</li>
+    <li><b>H</b>: Number of hybridizations is large enough.</li>
+    <li><b>A</b>: Arrays are loaded into AEDW.</li>
+    <li><b>F</b>: FactorValues provided.</li>
+    <li><b>D</b>: Raw and normalized data provided.</li>
+  </ul>
diff --git a/automation/admin/app/views/miamexps/_form.rhtml b/automation/admin/app/views/miamexps/_form.rhtml
new file mode 100644
index 0000000..729a40e
--- /dev/null
+++ b/automation/admin/app/views/miamexps/_form.rhtml
@@ -0,0 +1,86 @@
+<%= error_messages_for 'experiment' %>
+
+<table>
+  <tr>
+    <%= content_tag("td", "Submission ID") %>
+    <%= content_tag("td", @experiment.miamexpress_subid) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Login") %>
+    <%= content_tag("td", @experiment.miamexpress_login) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Name") %>
+    <%= content_tag("td", @experiment.name) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Accession") %>
+    <%= content_tag("td", input("experiment", "accession")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Status") %>
+    <%= content_tag("td", select(
+	"experiment",
+	"status",
+	['Waiting',
+	 'Retrieving info from MX',
+    	 'Checking in progress',
+         '** CHECKER CRASH **',
+         'Checking failed',
+         'Checking passed',
+    	 'MAGE-ML export',
+	 'Export postponed',
+         'Export failed',
+         'Complete',
+	])) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Checker score") %>
+    <%= content_tag("td", @experiment.checker_score) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "MIAME score") %>
+    <%= content_tag("td", (@experiment.miame_score.to_i.&(1).nonzero?  ? 'R' : '') +
+                          (@experiment.miame_score.to_i.&(2).nonzero?  ? 'N' : '') +
+                          (@experiment.miame_score.to_i.&(4).nonzero?  ? 'F' : '') +
+                          (@experiment.miame_score.to_i.&(8).nonzero?  ? 'P' : '') +
+                          (@experiment.miame_score.to_i.&(16).nonzero? ? 'A' : '')) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "DW ready") %>
+    <%= content_tag("td", (@experiment.data_warehouse_ready.to_i.&(1).nonzero?  ? 'T' : '') +
+                          (@experiment.data_warehouse_ready.to_i.&(2).nonzero?  ? 'H' : '') +
+                          (@experiment.data_warehouse_ready.to_i.&(4).nonzero?  ? 'A' : '') +
+                          (@experiment.data_warehouse_ready.to_i.&(8).nonzero?  ? 'F' : '') +
+                          (@experiment.data_warehouse_ready.to_i.&(16).nonzero? ? 'D' : '')) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Loaded in DW:") %>
+    <%= content_tag("td", @experiment.in_data_warehouse? ? 'YES' : 'no') %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Software") %>
+    <%= content_tag("td", input("experiment", "software")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Batchloader used:") %>
+    <%= content_tag("td", check_box("experiment", "is_mx_batchloader")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Curator") %>
+    <%= content_tag("td", input("experiment", "curator")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Date submitted:") %>
+    <%= content_tag("td", @experiment.date_submitted) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Date last processed:") %>
+    <%= content_tag("td", @experiment.date_last_processed) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Comment") %>
+    <%= content_tag("td", input("experiment", "comment")) %>
+  </tr>
+</table>
+
diff --git a/automation/admin/app/views/miamexps/_miameinfo.rhtml b/automation/admin/app/views/miamexps/_miameinfo.rhtml
new file mode 100644
index 0000000..c12e465
--- /dev/null
+++ b/automation/admin/app/views/miamexps/_miameinfo.rhtml
@@ -0,0 +1,9 @@
+
+  <p class="text">Explanation of MIAME scores:</p>
+  <ul>
+    <li><b>R</b>: Raw data provided.</li>
+    <li><b>N</b>: Normalized data provided.</li>
+    <li><b>F</b>: FactorValues provided.</li>
+    <li><b>P</b>: Protocols provided (data transformation only at the moment).</li>
+    <li><b>A</b>: Arrays are all MIAME compliant.</li>
+  </ul>
diff --git a/automation/admin/app/views/miamexps/_title.rhtml b/automation/admin/app/views/miamexps/_title.rhtml
new file mode 100644
index 0000000..1554546
--- /dev/null
+++ b/automation/admin/app/views/miamexps/_title.rhtml
@@ -0,0 +1,3 @@
+<%= content_tag("h1", "#{params[:experiment_type]} experiments", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
diff --git a/automation/admin/app/views/miamexps/edit.rhtml b/automation/admin/app/views/miamexps/edit.rhtml
new file mode 100644
index 0000000..8c64832
--- /dev/null
+++ b/automation/admin/app/views/miamexps/edit.rhtml
@@ -0,0 +1,15 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag :action => "update", :id => @experiment.id do %>
+    <%= render_partial "form" %>
+    <%= hidden_field_tag("page", params[:page]) %>
+    <%= hidden_field_tag("search_term", params[:search_term]) %>
+    <%= hidden_field_tag("experiment_type", @experiment.experiment_type) %>
+    <%= content_tag("p", submit_tag("Update")) %>
+  <% end %>
+
+  <%= render_partial "miameinfo" %>
+  <%= render_partial "aedwinfo" %>
+
+</div>
\ No newline at end of file
diff --git a/automation/admin/app/views/miamexps/list.rhtml b/automation/admin/app/views/miamexps/list.rhtml
new file mode 100644
index 0000000..7d8342c
--- /dev/null
+++ b/automation/admin/app/views/miamexps/list.rhtml
@@ -0,0 +1,72 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag do %>
+	<%= content_tag("span", "Search on SubId, Login, Accession or Name (wildcards: *, ?): ") %>
+	<%= text_field_tag("search_term", @search_term) %>
+        <%= hidden_field_tag("experiment_type", params[:experiment_type]) %>
+	<%= submit_tag("Search") %>
+  <% end %>
+</div>
+
+<div>
+  <table>
+    <tr>
+      <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+      <%= content_tag("th", "SubId") %>
+      <%= content_tag("th", "Login") %>
+      <%= content_tag("th", "Accession") %>
+      <%= content_tag("th", "Directory") %>
+      <%= content_tag("th", "Name") %>
+      <%= content_tag("th", "Checker score") %>
+      <%= content_tag("th", "MIAME score") %>
+      <%= content_tag("th", "DW status") %>
+      <%= content_tag("th", "Status") %>
+      <%= content_tag("th", "Date last processed") %>
+      <%= content_tag("th", "Curator") %>
+      <%= content_tag("th", "Software") %>
+      <%= content_tag("th", "Comment") %>
+    </tr>
+
+    <% @experiments.each do |e| %>
+      <tr>
+	<td>
+	<%= link_to "Edit", :action          => "edit", 
+                            :id              => e.id,
+			    :page            => params[:page],
+                            :experiment_type => params[:experiment_type],
+	                    :search_term     => @search_term %>
+        </td>
+	<%= content_tag("td", e.miamexpress_subid) %>
+	<%= content_tag("td", e.miamexpress_login) %>
+	<%= content_tag("td", e.accession) %>
+	<%= content_tag("td", e.experiment_type.to_s.eql?('MIAMExpress') ? '' : e.experiment_type.to_s + "_" + e.id.to_s) %>
+	<%= content_tag("td", e.name) %>
+	<%= content_tag("td", e.checker_score) %>
+	<%= content_tag("td", (e.miame_score.to_i.&(1).nonzero? ? 'R' : '') +
+                              (e.miame_score.to_i.&(2).nonzero? ? 'N' : '') +
+                              (e.miame_score.to_i.&(4).nonzero? ? 'F' : '') +
+                              (e.miame_score.to_i.&(8).nonzero? ? 'P' : '') +
+                              (e.miame_score.to_i.&(16).nonzero? ? 'A' : '')) %>
+	<%= content_tag("td", e.in_data_warehouse?        ? '<font color="blue">Loaded</font>' :
+                              e.data_warehouse_ready.nil? ? '' :
+                              e.data_warehouse_ready.to_i.eql?(31) ? '<font color="green">YES</font>' :
+			      e.data_warehouse_ready.to_i.eql?(30) ? '<font color="red">maybe</font>' :
+                              '<font color="red">no</font>') %>
+	<%= content_tag("td", e.status) %>
+	<%= content_tag("td", e.date_last_processed ?
+			      e.date_last_processed.strftime('%Y-%m-%d') :
+                              "") %>
+	<%= content_tag("td", e.curator) %>
+	<%= content_tag("td", e.software) %>
+	<%= content_tag("td", truncate(e.comment, 30)) %>
+      </tr> 
+    <% end %>
+  </table>
+
+  <%= will_paginate @experiments, :class  => 'footer',
+	                          :params => { :search_term => params[:search_term] } %>
+
+  <%= render_partial "miameinfo" %>
+
+</div>
diff --git a/automation/admin/app/views/organism/_form.rhtml b/automation/admin/app/views/organism/_form.rhtml
new file mode 100644
index 0000000..dab395a
--- /dev/null
+++ b/automation/admin/app/views/organism/_form.rhtml
@@ -0,0 +1,25 @@
+<%= error_messages_for 'organism' %>
+
+<table><tr>
+<td><a href="http://mged.sourceforge.net/ontologies/MOhtml/">MGED Ontology webpage</a></td>
+<td><a href="http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=Taxonomy">NCBI taxonomy webpage</a></td>
+</tr><table>
+
+<table>
+  <tr>
+    <%= content_tag("td", "Scientific name:") %>
+    <%= content_tag("td", input("organism", "scientific_name")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Common name:") %>
+    <%= content_tag("td", input("organism", "common_name")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Accession:") %>
+    <%= content_tag("td", input("organism", "accession")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Taxon common name:") %>
+    <%= content_tag("td", select("organism", "taxon_id", Taxon.find(:all, :conditions => 'is_deleted=0').collect { |t| [t.common_name, t.id] }.sort_by { |name, id| name })) %>
+  </tr>
+</table>
diff --git a/automation/admin/app/views/organism/_title.rhtml b/automation/admin/app/views/organism/_title.rhtml
new file mode 100644
index 0000000..4022a5a
--- /dev/null
+++ b/automation/admin/app/views/organism/_title.rhtml
@@ -0,0 +1,3 @@
+<%= content_tag("h1", "Organisms", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
diff --git a/automation/admin/app/views/organism/edit.rhtml b/automation/admin/app/views/organism/edit.rhtml
new file mode 100644
index 0000000..78775e1
--- /dev/null
+++ b/automation/admin/app/views/organism/edit.rhtml
@@ -0,0 +1,10 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag :action => "update", :id => @organism.id do %>
+    <%= render_partial "form" %>
+    <%= hidden_field_tag("page", params[:page]) %>
+    <%= hidden_field_tag("search_term", params[:search_term]) %>
+    <%= submit_tag "Update" %>
+  <% end %>
+</div>
\ No newline at end of file
diff --git a/automation/admin/app/views/organism/list.rhtml b/automation/admin/app/views/organism/list.rhtml
new file mode 100644
index 0000000..7a72ad5
--- /dev/null
+++ b/automation/admin/app/views/organism/list.rhtml
@@ -0,0 +1,59 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag do %>
+	<%= content_tag("span", "Search on Scientific Name (wildcards: *, ?): ") %>
+	<%= text_field_tag("search_term", @search_term) %>
+	<%= submit_tag("Search") %>
+  <% end %>
+</div>
+
+<div>
+  <table>
+    <tr>
+      <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+      <%= content_tag("th", "Scientific name") %>
+      <%= content_tag("th", "Common name") %>
+      <%= content_tag("th", "Accession") %>
+      <%= content_tag("th", "Taxon") %>
+      <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+    </tr>
+
+    <% @organisms.each do |o| %>
+      <tr>
+	<td>
+	<%= link_to "Edit", :action      => "edit", 
+                            :id          => o.id,
+			    :page        => params[:page],
+	                    :search_term => @search_term %>
+        </td>
+        <%= content_tag("td", o.scientific_name) %>
+        <%= content_tag("td", o.common_name) %>
+        <%= content_tag("td", o.accession) %>
+        <%= content_tag("td", o.taxon.nil? ? '' : o.taxon.scientific_name) %>
+	<td>
+        <%= link_to "Delete", {:action      => "deprecate", 
+                               :id          => o.id,
+			       :page        => params[:page],
+	                       :search_term => @search_term },
+                               :confirm => "Really delete organism \"#{o.scientific_name}\"?" %>
+        </td>
+      </tr> 
+    <% end %>
+  </table>
+
+  <%= will_paginate @organisms, :class  => 'footer',
+	                        :params => { :search_term => params[:search_term] }  %>
+
+  <p class="text"><em>Note:</em> Organisms must be
+  assigned to taxa for them to be displayed in the public submissions web
+  interface. If you wish to enable one of the organisms listed above
+  for submissions you need to assign it to a taxon; preferably one
+  with the appropriate annotation categories selected, so that the
+  template spreadsheets contain the desired columns.</p>
+
+  <%= content_tag("div", link_to("Add new organism", 
+                        :controller => "organism", 
+                        :action     => "new"), { :class => 'footer' } ) %>
+
+</div>
diff --git a/automation/admin/app/views/organism/new.rhtml b/automation/admin/app/views/organism/new.rhtml
new file mode 100644
index 0000000..c2334f3
--- /dev/null
+++ b/automation/admin/app/views/organism/new.rhtml
@@ -0,0 +1,8 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag :action => 'create' do %>
+    <%= render_partial "form" %>
+    <%= submit_tag "Create" %>
+  <% end %>
+</div>
\ No newline at end of file
diff --git a/automation/admin/app/views/protocols/_form.rhtml b/automation/admin/app/views/protocols/_form.rhtml
new file mode 100644
index 0000000..3574d01
--- /dev/null
+++ b/automation/admin/app/views/protocols/_form.rhtml
@@ -0,0 +1,28 @@
+<%= error_messages_for 'protocol' %>
+
+<table>
+  <tr>
+    <%= content_tag("td", "Accession:") %>
+    <%= content_tag("td", input("protocol", "accession")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "User accession:") %>
+    <%= content_tag("td", input("protocol", "user_accession")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Expt accession:") %>
+    <%= content_tag("td", input("protocol", "expt_accession")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Name:") %>
+    <%= content_tag("td", input("protocol", "name")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Date last processed:") %>
+    <%= content_tag("td", @protocol.date_last_processed) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Comment:") %>
+    <%= content_tag("td", input("protocol", "comment")) %>
+  </tr>
+</table>
diff --git a/automation/admin/app/views/protocols/_title.rhtml b/automation/admin/app/views/protocols/_title.rhtml
new file mode 100644
index 0000000..37cb4c7
--- /dev/null
+++ b/automation/admin/app/views/protocols/_title.rhtml
@@ -0,0 +1,3 @@
+<%= content_tag("h1", "Tab2MAGE protocols", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
diff --git a/automation/admin/app/views/protocols/edit.rhtml b/automation/admin/app/views/protocols/edit.rhtml
new file mode 100644
index 0000000..2aa3ca2
--- /dev/null
+++ b/automation/admin/app/views/protocols/edit.rhtml
@@ -0,0 +1,10 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag :action => "update", :id => @protocol.id do %>
+    <%= render_partial "form" %>
+    <%= hidden_field_tag("page", params[:page]) %>
+    <%= hidden_field_tag("search_term", params[:search_term]) %>
+    <%= content_tag("p", submit_tag("Update")) %>
+  <% end %>
+</div>
diff --git a/automation/admin/app/views/protocols/list.rhtml b/automation/admin/app/views/protocols/list.rhtml
new file mode 100644
index 0000000..2e13003
--- /dev/null
+++ b/automation/admin/app/views/protocols/list.rhtml
@@ -0,0 +1,58 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag do %>
+	<%= content_tag("span", "Search on Accession, User Accession, Expt Accession or Name (wildcards: *, ?): ") %>
+	<%= text_field_tag("search_term", @search_term) %>
+	<%= submit_tag("Search") %>
+  <% end %>
+</div>
+
+<div>
+  <table>
+    <tr>
+      <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+      <%= content_tag("th", "Accession") %>
+      <%= content_tag("th", "User accession") %>
+      <%= content_tag("th", "Expt accession") %>
+      <%= content_tag("th", "Name") %>
+      <%= content_tag("th", "Date last processed") %>
+      <%= content_tag("th", "Comment") %>
+      <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+    </tr>
+
+    <% @protocols.each do |p| %>
+      <tr>
+	<td>
+	<%= link_to "Edit", :action      => "edit", 
+                            :id          => p.id,
+			    :page        => params[:page],
+	                    :search_term => @search_term %>
+        </td>
+	<%= content_tag("td", p.accession) %>
+	<%= content_tag("td", p.user_accession) %>
+	<%= content_tag("td", p.expt_accession) %>
+	<%= content_tag("td", p.name) %>
+	<%= content_tag("td", p.date_last_processed ?
+			      p.date_last_processed.strftime('%Y-%m-%d') :
+                              "") %>
+	<%= content_tag("td", p.comment) %>
+	<td>
+        <%= link_to "Delete", {:action      => "deprecate", 
+                               :id          => p.id,
+	                       :page        => params[:page],
+	                       :search_term => @search_term },
+                               :confirm     => "Really delete protocol \"#{p.accession}\"?" %>
+        </td>
+      </tr> 
+    <% end %>
+  </table>
+
+  <%= will_paginate @protocols, :class  => 'footer',
+	                        :params => { :search_term => params[:search_term] }  %>
+
+  <%= content_tag("div", link_to("Add new protocol", 
+                        :controller => "protocols", 
+                        :action     => "new"), { :class => 'footer' } ) %>
+
+</div>
diff --git a/automation/admin/app/views/protocols/new.rhtml b/automation/admin/app/views/protocols/new.rhtml
new file mode 100644
index 0000000..c70be0f
--- /dev/null
+++ b/automation/admin/app/views/protocols/new.rhtml
@@ -0,0 +1,8 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag :action => 'create' do %>
+    <%= render_partial "form" %>
+    <%= content_tag("p", submit_tag("Create")) %>
+  <% end %>
+</div>
diff --git a/automation/admin/app/views/protocols/show.rhtml b/automation/admin/app/views/protocols/show.rhtml
new file mode 100644
index 0000000..92c6dde
--- /dev/null
+++ b/automation/admin/app/views/protocols/show.rhtml
@@ -0,0 +1,8 @@
+<% for column in Protocol.content_columns %>
+<p>
+  <b><%= column.human_name %>:</b> <%=h @protocol.send(column.name) %>
+</p>
+<% end %>
+
+<%= link_to 'Edit', :action => 'edit', :id => @protocol %> |
+<%= link_to 'Back', :action => 'list' %>
diff --git a/automation/admin/app/views/tab2mages/_aedwinfo.rhtml b/automation/admin/app/views/tab2mages/_aedwinfo.rhtml
new file mode 100644
index 0000000..ebbb279
--- /dev/null
+++ b/automation/admin/app/views/tab2mages/_aedwinfo.rhtml
@@ -0,0 +1,9 @@
+
+  <p class="text">Explanation of AEDW scores:</p>
+  <ul>
+    <li><b>T</b>: Experiment design type is appropriate.</li>
+    <li><b>H</b>: Number of hybridizations is large enough.</li>
+    <li><b>A</b>: Arrays are loaded into AEDW.</li>
+    <li><b>F</b>: FactorValues provided.</li>
+    <li><b>D</b>: Raw and normalized data provided.</li>
+  </ul>
diff --git a/automation/admin/app/views/tab2mages/_annotate.rhtml b/automation/admin/app/views/tab2mages/_annotate.rhtml
new file mode 100644
index 0000000..595caa0
--- /dev/null
+++ b/automation/admin/app/views/tab2mages/_annotate.rhtml
@@ -0,0 +1,52 @@
+<table>
+<tr>
+<th>Designs</th>
+<th>Materials</th>
+<th>Organisms</th>
+</tr>
+
+<tr>
+<td valign="top">
+<table>
+  <% Design.find(:all, :conditions => "is_deleted=0").sort_by { |d| d.ontology_value }.each do |design| %>
+    <tr>
+      <td>
+        <%= check_box_tag("annotation[design_ids][]",
+                           design.id,
+                           @experiment.designs.include?(design)) %>
+      </td>
+      <%= content_tag("td", design.display_label) %>
+    </tr>
+  <% end %>
+</table>
+</td>
+<td valign="top">
+<table>
+  <% Material.find(:all, :conditions => "is_deleted=0").sort_by { |d| d.ontology_value }.each do |material| %>
+    <tr>
+      <td>
+        <%= check_box_tag("annotation[material_ids][]",
+                           material.id,
+                           @experiment.materials.include?(material)) %>
+      </td>
+      <%= content_tag("td", material.display_label) %>
+    </tr>
+  <% end %>
+</table>
+</td>
+<td valign="top">
+<table>
+  <% Organism.find(:all, :conditions => "is_deleted=0 and taxon_id is not null").sort_by { |d| d.scientific_name }.each do |organism| %>
+    <tr>
+      <td>
+        <%= check_box_tag("annotation[organism_ids][]",
+                           organism.id,
+                           @experiment.organisms.include?(organism)) %>
+      </td>
+      <%= content_tag("td", organism.scientific_name) %>
+    </tr>
+  <% end %>
+</table>
+</td>
+</tr>
+</table>
\ No newline at end of file
diff --git a/automation/admin/app/views/tab2mages/_form.rhtml b/automation/admin/app/views/tab2mages/_form.rhtml
new file mode 100644
index 0000000..fef9e9e
--- /dev/null
+++ b/automation/admin/app/views/tab2mages/_form.rhtml
@@ -0,0 +1,91 @@
+<%= error_messages_for 'experiment' %>
+
+  <tr>
+    <%= content_tag("td", "Accession:") %>
+    <%= content_tag("td", input("experiment", "accession")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Name:") %>
+    <%= content_tag("td", input("experiment", "name")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Status:") %>
+    <%= content_tag("td", select(
+	"experiment",
+	"status",
+	['Waiting',
+	 'Retrieving info from MX',
+    	 'Checking in progress',
+         '** CHECKER CRASH **',
+         'Checking failed',
+         'Checking passed',
+    	 'MAGE-ML export',
+	 'Export postponed',
+         'Export failed',
+         'Complete',
+	])) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Directory:") %>
+    <%= content_tag("td", @experiment.experiment_type.to_s + "_" + @experiment.id.to_s) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Spreadsheet:") %>
+    <%= content_tag("td", @experiment.spreadsheets.find(:all, :conditions => "is_deleted=0").collect{|s| s.name}.join("<br>")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Data files:") %>
+    <%= content_tag("td", @experiment.data_files.find(:all, :conditions => "is_deleted=0").collect{|d| d.name}.join("<br>")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Checker score:") %>
+    <%= content_tag("td", @experiment.checker_score) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "MIAME score") %>
+    <%= content_tag("td", (@experiment.miame_score.to_i.&(1).nonzero?  ? 'R' : '') +
+                          (@experiment.miame_score.to_i.&(2).nonzero?  ? 'N' : '') +
+                          (@experiment.miame_score.to_i.&(4).nonzero?  ? 'F' : '') +
+                          (@experiment.miame_score.to_i.&(8).nonzero?  ? 'P' : '') +
+                          (@experiment.miame_score.to_i.&(16).nonzero? ? 'A' : '')) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "DW ready:") %>
+    <%= content_tag("td", (@experiment.data_warehouse_ready.to_i.&(1).nonzero?  ? 'T' : '') +
+                          (@experiment.data_warehouse_ready.to_i.&(2).nonzero?  ? 'H' : '') +
+                          (@experiment.data_warehouse_ready.to_i.&(4).nonzero?  ? 'A' : '') +
+                          (@experiment.data_warehouse_ready.to_i.&(8).nonzero?  ? 'F' : '') +
+                          (@experiment.data_warehouse_ready.to_i.&(16).nonzero? ? 'D' : '')) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Loaded in DW:") %>
+    <%= content_tag("td", @experiment.in_data_warehouse? ? 'YES' : 'no') %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Affymetrix:") %>
+    <%= content_tag("td", check_box("experiment", "is_affymetrix")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Software:") %>
+    <%= content_tag("td", input("experiment", "software")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Curator:") %>
+    <%= content_tag("td", input("experiment", "curator")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Date last edited:") %>
+    <%= content_tag("td", @experiment.date_last_edited) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Date submitted:") %>
+    <%= content_tag("td", @experiment.date_submitted) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Date last processed:") %>
+    <%= content_tag("td", @experiment.date_last_processed) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Comment:") %>
+    <%= content_tag("td", input("experiment", "comment")) %>
+  </tr>
diff --git a/automation/admin/app/views/tab2mages/_miameinfo.rhtml b/automation/admin/app/views/tab2mages/_miameinfo.rhtml
new file mode 100644
index 0000000..c12e465
--- /dev/null
+++ b/automation/admin/app/views/tab2mages/_miameinfo.rhtml
@@ -0,0 +1,9 @@
+
+  <p class="text">Explanation of MIAME scores:</p>
+  <ul>
+    <li><b>R</b>: Raw data provided.</li>
+    <li><b>N</b>: Normalized data provided.</li>
+    <li><b>F</b>: FactorValues provided.</li>
+    <li><b>P</b>: Protocols provided (data transformation only at the moment).</li>
+    <li><b>A</b>: Arrays are all MIAME compliant.</li>
+  </ul>
diff --git a/automation/admin/app/views/tab2mages/_title.rhtml b/automation/admin/app/views/tab2mages/_title.rhtml
new file mode 100644
index 0000000..1554546
--- /dev/null
+++ b/automation/admin/app/views/tab2mages/_title.rhtml
@@ -0,0 +1,3 @@
+<%= content_tag("h1", "#{params[:experiment_type]} experiments", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
diff --git a/automation/admin/app/views/tab2mages/edit.rhtml b/automation/admin/app/views/tab2mages/edit.rhtml
new file mode 100644
index 0000000..22a3b2c
--- /dev/null
+++ b/automation/admin/app/views/tab2mages/edit.rhtml
@@ -0,0 +1,35 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag :action => "update", :id => @experiment.id do %>
+
+<table>
+  <tr>
+    <%= content_tag("td", 'User login:') %>
+    <%= content_tag("td", select(
+	"experiment",
+	"user_id",
+	User.find(:all).collect { |t| [t.login, t.id] }.sort_by { |login, id| login },
+	{ :include_blank => true })) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "In curation:") %>
+    <%= content_tag("td", check_box("experiment", "in_curation")) %>
+  </tr>
+
+    <%= render_partial "form" %>
+
+</table>
+
+    <%= hidden_field_tag("experiment_type", @experiment.experiment_type) %>
+    <%= hidden_field_tag("page", params[:page]) %>
+    <%= hidden_field_tag("search_term", params[:search_term]) %>
+    <%= content_tag("p", submit_tag("Update")) %>
+    <%= render_partial "annotate" %>
+    <%= content_tag("p", submit_tag("Update")) %>
+  <% end %>
+
+  <%= render_partial "miameinfo" %>
+  <%= render_partial "aedwinfo" %>
+
+</div>
diff --git a/automation/admin/app/views/tab2mages/list.rhtml b/automation/admin/app/views/tab2mages/list.rhtml
new file mode 100644
index 0000000..9a8bf4b
--- /dev/null
+++ b/automation/admin/app/views/tab2mages/list.rhtml
@@ -0,0 +1,97 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag do %>
+	<%= content_tag("span", "Search on Accession, User, Name or Comment (wildcards: *, ?): ") %>
+	<%= text_field_tag("search_term", @search_term) %>
+        <%= hidden_field_tag("experiment_type", params[:experiment_type]) %>
+	<%= submit_tag("Search") %>
+  <% end %>
+</div>
+
+<div>
+  <table>
+    <tr>
+      <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+      <%= content_tag("th", "Accession") %>
+      <%= content_tag("th", "User") %>
+      <%= content_tag("th", "Directory") %>
+      <%= content_tag("th", "Name") %>
+      <%= content_tag("th", "Checker score") %>
+      <%= content_tag("th", "MIAME score") %>
+      <%= content_tag("th", "DW status") %>
+      <%= content_tag("th", "In curation") %>
+      <%= content_tag("th", "Status") %>
+      <%= content_tag("th", "Software") %>
+      <%= content_tag("th", "Date last edited") %>
+      <%= content_tag("th", "Date submitted") %>
+      <%= content_tag("th", "Date last processed") %>
+      <%= content_tag("th", "Curator") %>
+      <%= content_tag("th", "Comment") %>
+      <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+    </tr>
+
+    <% @experiments.each do |e| %>
+      <tr>
+	<td>
+	<%= link_to "Edit", :action          => "edit", 
+                            :id              => e.id,
+			    :page            => params[:page],
+                            :experiment_type => params[:experiment_type],
+	                    :search_term     => @search_term %>
+        </td>
+	<%= content_tag("td", e.accession) %>
+	<%= content_tag("td", e.user ? e.user.login : nil) %>
+        <%= content_tag("td", e.experiment_type.to_s + "_" + e.id.to_s) %>
+	<%= content_tag("td", e.name) %>
+	<%= content_tag("td", e.checker_score) %>
+	<%= content_tag("td", (e.miame_score.to_i.&(1).nonzero? ? 'R' : '') +
+                              (e.miame_score.to_i.&(2).nonzero? ? 'N' : '') +
+                              (e.miame_score.to_i.&(4).nonzero? ? 'F' : '') +
+                              (e.miame_score.to_i.&(8).nonzero? ? 'P' : '') +
+                              (e.miame_score.to_i.&(16).nonzero? ? 'A' : '')) %>
+	<%= content_tag("td", e.in_data_warehouse?        ? '<font color="blue">Loaded</font>' :
+                              e.data_warehouse_ready.nil? ? '' :
+                              e.data_warehouse_ready.to_i.eql?(31) ? '<font color="green">YES</font>' :
+			      e.data_warehouse_ready.to_i.eql?(30) ? '<font color="red">maybe</font>' :
+                              '<font color="red">no</font>') %>
+	<%= content_tag("td", e.in_curation.nil? ? '' :
+                              e.in_curation? ?
+                              '<font color="green">YES</font>' :
+                              '<font color="red">no</font>') %>
+	<%= content_tag("td", e.status) %>
+	<%= content_tag("td", e.software) %>
+	<%= content_tag("td", e.date_last_edited ?
+			      e.date_last_edited.strftime('%Y-%m-%d') :
+                              "") %>
+	<%= content_tag("td", e.date_submitted ?
+			      e.date_submitted.strftime('%Y-%m-%d') :
+                              "") %>
+	<%= content_tag("td", e.date_last_processed ?
+			      e.date_last_processed.strftime('%Y-%m-%d') :
+                              "") %>
+	<%= content_tag("td", e.curator) %>
+	<%= content_tag("td", truncate(e.comment, 30)) %>
+	<td>
+        <%= link_to "Delete", {:action          => "deprecate", 
+                               :id              => e.id,
+	                       :page            => params[:page],
+                               :experiment_type => params[:experiment_type],
+	                       :search_term     => @search_term },
+                               :confirm         => "Really delete experiment \"#{e.id}\"?" %>
+        </td>
+      </tr> 
+    <% end %>
+  </table>
+
+  <%= will_paginate @experiments, :class  => 'footer',
+	                          :params => { :search_term => params[:search_term] }  %>
+
+  <%= content_tag("div", link_to("Add new #{params[:experiment_type]} experiment", 
+                        :controller      => "tab2mages",
+                        :experiment_type => params[:experiment_type],
+                        :action          => "new"), { :class => 'footer' } ) %>
+
+  <%= render_partial "miameinfo" %>
+
+</div>
diff --git a/automation/admin/app/views/tab2mages/new.rhtml b/automation/admin/app/views/tab2mages/new.rhtml
new file mode 100644
index 0000000..599e318
--- /dev/null
+++ b/automation/admin/app/views/tab2mages/new.rhtml
@@ -0,0 +1,13 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag :action => 'create' do %>
+<table>
+    <%= render_partial "form" %>
+</table>
+    <%= hidden_field_tag("experiment[experiment_type]", params[:experiment_type]) %>
+    <%= content_tag("p", submit_tag("Create")) %>
+    <%= render_partial "annotate" %>
+    <%= content_tag("p", submit_tag("Create")) %>
+  <% end %>
+</div>
diff --git a/automation/admin/app/views/taxon/_form.rhtml b/automation/admin/app/views/taxon/_form.rhtml
new file mode 100644
index 0000000..9a933ad
--- /dev/null
+++ b/automation/admin/app/views/taxon/_form.rhtml
@@ -0,0 +1,34 @@
+<%= error_messages_for 'taxon' %>
+
+<table><tr>
+<td><a href="http://mged.sourceforge.net/ontologies/MOhtml/">MGED Ontology webpage</a></td>
+<td><a href="http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=Taxonomy">NCBI taxonomy webpage</a></td>
+</tr><table>
+
+<table>
+  <tr>
+    <%= content_tag("td", "Scientific name:") %>
+    <%= content_tag("td", input("taxon", "scientific_name")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Common name:") %>
+    <%= content_tag("td", input("taxon", "common_name")) %>
+  </tr>
+  <tr>
+    <%= content_tag("td", "Accession:") %>
+    <%= content_tag("td", input("taxon", "accession")) %>
+  </tr>
+</table>
+
+<table>
+  <% Category.find(:all).sort_by { |c| c.ontology_term }.each do |cat| %>
+    <tr>
+      <td>
+        <%= check_box_tag("taxon[category_ids][]",
+                           cat.id,
+                           @taxon.categories.include?(cat)) %>
+      </td>
+      <%= content_tag("td", cat.ontology_term) %>
+    </tr>
+  <% end %>
+</table>
diff --git a/automation/admin/app/views/taxon/_title.rhtml b/automation/admin/app/views/taxon/_title.rhtml
new file mode 100644
index 0000000..712ed3a
--- /dev/null
+++ b/automation/admin/app/views/taxon/_title.rhtml
@@ -0,0 +1,3 @@
+<%= content_tag("h1", "Taxa", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
diff --git a/automation/admin/app/views/taxon/category_table.rhtml b/automation/admin/app/views/taxon/category_table.rhtml
new file mode 100644
index 0000000..752e2c3
--- /dev/null
+++ b/automation/admin/app/views/taxon/category_table.rhtml
@@ -0,0 +1,21 @@
+<%= render_partial "title" %>
+
+<%= content_tag("p", link_to("Taxa list", 
+                        :controller => "taxon", 
+                        :action     => "list"), { :class => 'footer' } ) %>
+
+<table>
+  <tr>
+    <%= content_tag("th", "Categories") %>
+    <% @taxons.each do |t| %>
+      <%= content_tag("th", t.common_name) %>
+    <% end %>
+  <% @categories.each do |c| %>
+    <tr>
+      <%= content_tag("th", c.ontology_term) %>
+      <% @taxons.each do |t| %>
+	<%= content_tag("td", (c.taxons.include?(t) ? "X" : ""), "align" => "center" ) %>
+      <% end %>
+    </tr>
+  <% end %>
+</table>
\ No newline at end of file
diff --git a/automation/admin/app/views/taxon/edit.rhtml b/automation/admin/app/views/taxon/edit.rhtml
new file mode 100644
index 0000000..9fc5569
--- /dev/null
+++ b/automation/admin/app/views/taxon/edit.rhtml
@@ -0,0 +1,8 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag :action => "update", :id => @taxon.id do %>
+    <%= render_partial "form" %>
+    <%= submit_tag "Update" %>
+  <% end %>
+</div>
\ No newline at end of file
diff --git a/automation/admin/app/views/taxon/list.rhtml b/automation/admin/app/views/taxon/list.rhtml
new file mode 100644
index 0000000..e60568f
--- /dev/null
+++ b/automation/admin/app/views/taxon/list.rhtml
@@ -0,0 +1,43 @@
+<%= render_partial "title" %>
+
+<%= content_tag("p", link_to("Taxa categories table", 
+                        :controller => "taxon", 
+                        :action     => "category_table"), { :class => 'footer' } ) %>
+
+<div>
+  <table>
+    <tr>
+      <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+      <%= content_tag("th", "Scientific name") %>
+      <%= content_tag("th", "Common name") %>
+      <%= content_tag("th", "Accession") %>
+      <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+    </tr>
+
+    <% @taxons.each do |t| %>
+
+      <tr>
+	<td>
+	<%= link_to "Edit", :action => "edit", 
+                            :id     => t.id %>
+        </td>
+        <%= content_tag("td", t.scientific_name) %>
+        <%= content_tag("td", t.common_name) %>
+        <%= content_tag("td", t.accession) %>
+	<td>
+        <%= link_to "Delete", {:action  => "deprecate", 
+                               :id      => t.id},
+                               :confirm => "Really delete taxon \"#{t.scientific_name}\"?" %>
+        </td>
+      </tr> 
+    <% end %>
+  </table>
+
+  <%= will_paginate @taxons, :class  => 'footer',
+	                     :params => { :search_term => params[:search_term] }  %>
+
+  <%= content_tag("div", link_to("Add new taxon", 
+                        :controller => "taxon", 
+                        :action     => "new"), { :class => 'footer' } ) %>
+
+</div>
diff --git a/automation/admin/app/views/taxon/new.rhtml b/automation/admin/app/views/taxon/new.rhtml
new file mode 100644
index 0000000..c2334f3
--- /dev/null
+++ b/automation/admin/app/views/taxon/new.rhtml
@@ -0,0 +1,8 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag :action => 'create' do %>
+    <%= render_partial "form" %>
+    <%= submit_tag "Create" %>
+  <% end %>
+</div>
\ No newline at end of file
diff --git a/automation/admin/app/views/users/_form.rhtml b/automation/admin/app/views/users/_form.rhtml
new file mode 100644
index 0000000..dff793b
--- /dev/null
+++ b/automation/admin/app/views/users/_form.rhtml
@@ -0,0 +1,31 @@
+<%= error_messages_for 'user' %>
+
+<table>
+    <tr>
+      <%= content_tag("td", "Login:") %>
+      <%= content_tag("td", input("user",
+                                  :login)) %>
+    </tr>
+    <tr>
+      <%= content_tag("td", "Name:") %>
+      <%= content_tag("td", input("user",
+                                  :name)) %>
+    </tr>
+    <tr>
+      <%= content_tag("td", "Password:") %>
+      <%= content_tag("td", input("user",
+                                  :password)) %>
+    </tr>
+    <tr>
+      <%= content_tag("td", "Confirm password:") %>
+      <%= content_tag("td", input("user",
+                                  :password,
+                                  "id"   => "user_password_confirmation",
+                                  "name" => "user[password_confirmation]")) %>
+    </tr>
+    <tr>
+      <%= content_tag("td", "Email address:") %>
+      <%= content_tag("td", input("user",
+                                  :email)) %>
+    </tr>
+</table>
diff --git a/automation/admin/app/views/users/_title.rhtml b/automation/admin/app/views/users/_title.rhtml
new file mode 100644
index 0000000..6af30bf
--- /dev/null
+++ b/automation/admin/app/views/users/_title.rhtml
@@ -0,0 +1,3 @@
+<%= content_tag("h1", "User Administration", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
diff --git a/automation/admin/app/views/users/edit.rhtml b/automation/admin/app/views/users/edit.rhtml
new file mode 100644
index 0000000..7581e6e
--- /dev/null
+++ b/automation/admin/app/views/users/edit.rhtml
@@ -0,0 +1,10 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag :action => "update", :id => @user.id do %>
+    <%= render_partial "form" %>
+    <%= hidden_field_tag("page", params[:page]) %>
+    <%= hidden_field_tag("search_term", params[:search_term]) %>
+    <%= submit_tag "Update" %>
+  <% end %>
+</div>
diff --git a/automation/admin/app/views/users/list.rhtml b/automation/admin/app/views/users/list.rhtml
new file mode 100644
index 0000000..7df5675
--- /dev/null
+++ b/automation/admin/app/views/users/list.rhtml
@@ -0,0 +1,54 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag do %>
+	<%= content_tag("span", "Search on Login, Name or Email (wildcards: *, ?): ") %>
+	<%= text_field_tag("search_term", @search_term) %>
+	<%= submit_tag("Search") %>
+  <% end %>
+</div>
+
+<div>
+  <table>
+    <tr>
+      <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+      <%= content_tag("th", "Login") %>
+      <%= content_tag("th", "Name") %>
+      <%= content_tag("th", "Password") %>
+      <%= content_tag("th", "Email") %>
+      <%= content_tag("th", "Last Accessed") %>
+      <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+    </tr>
+
+    <% @users.each do |u| %>
+      <tr>
+	<td>
+	<%= link_to "Edit", :action      => "edit", 
+                            :id          => u.id,
+			    :page        => params[:page],
+	                    :search_term => @search_term %>
+        </td>
+	<%= content_tag("td", u.login) %>
+	<%= content_tag("td", u.name) %>
+	<%= content_tag("td", u.password) %>
+	<%= content_tag("td", u.email) %>
+	<%= content_tag("td", u.access) %>
+	<td>
+        <%= link_to "Delete", {:action      => "deprecate", 
+                               :id          => u.id,
+			       :page        => params[:page],
+	                       :search_term => @search_term },
+                               :confirm => "Really delete user \"#{u.login}\"?" %>
+        </td>
+      </tr> 
+    <% end %>
+  </table>
+
+  <%= will_paginate @users, :class  => 'footer',
+	                    :params => { :search_term => params[:search_term] }  %>
+
+  <%= content_tag("div", link_to("Add new user", 
+                        :controller => "users", 
+                        :action     => "new"), { :class => 'footer' } ) %>
+
+</div>
diff --git a/automation/admin/app/views/users/new.rhtml b/automation/admin/app/views/users/new.rhtml
new file mode 100644
index 0000000..d920995
--- /dev/null
+++ b/automation/admin/app/views/users/new.rhtml
@@ -0,0 +1,8 @@
+<%= render_partial "title" %>
+
+<div>
+  <% form_tag :action => 'create' do %>
+    <%= render_partial "form" %>
+    <%= submit_tag "Create" %>
+  <% end %>
+</div>
diff --git a/automation/admin/app/views/users/show.rhtml b/automation/admin/app/views/users/show.rhtml
new file mode 100644
index 0000000..562ade0
--- /dev/null
+++ b/automation/admin/app/views/users/show.rhtml
@@ -0,0 +1,8 @@
+<% for column in User.content_columns %>
+<p>
+  <b><%= column.human_name %>:</b> <%=h @user.send(column.name) %>
+</p>
+<% end %>
+
+<%= link_to 'Edit', :action => 'edit', :id => @user %> |
+<%= link_to 'Back', :action => 'list' %>
diff --git a/automation/admin/config/README b/automation/admin/config/README
new file mode 100644
index 0000000..09c3649
--- /dev/null
+++ b/automation/admin/config/README
@@ -0,0 +1,45 @@
+*** NOTE ***
+
+As of Tab2MAGE version 1.9.9 these notes are obsolete, since we have
+migrated to using MySQL as the back-end database. These instructions
+are included in case future users wish to revert to SQLite
+
+*** END NOTE ***
+
+
+The standard SQLite interface included with ActiveRecord apparently
+has a default timeout of zero when connecting to the database. This
+can cause problems ("SQLite3::BusyException"), especially when the
+SQLite db file is on an NFS mount (which strictly speaking should be
+avoided anyway...). The ticket here:
+
+http://dev.rubyonrails.org/ticket/6126
+
+gives a possible solution, which involves applying the patch below,
+and adding a :timeout value to the database.yml config entry in this
+directory. We are currently evaluating this.
+
+Index: test/connections/native_sqlite3/connection.rb
+===================================================================
+--- test/connections/native_sqlite3/connection.rb	(revision 5072)
++++ test/connections/native_sqlite3/connection.rb	(working copy)
+@@ -11,7 +11,7 @@
+ sqlite_test_db2 = "#{BASE_DIR}/fixture_database_2.sqlite3"
+
+ def make_connection(clazz, db_file, db_definitions_file)
+-  ActiveRecord::Base.configurations = { clazz.name => { :adapter => 'sqlite3', :database => db_file } }
++  ActiveRecord::Base.configurations = { clazz.name => { :adapter => 'sqlite3', :database => db_file, :timeout => 5000 } }
+   unless File.exist?(db_file)
+     puts "SQLite3 database not found at #{db_file}. Rebuilding it."
+     sqlite_command = %Q{sqlite3 #{db_file} "create table a (a integer); drop table a;"}
+Index: lib/active_record/connection_adapters/sqlite_adapter.rb
+===================================================================
+--- lib/active_record/connection_adapters/sqlite_adapter.rb	(revision 5072)
++++ lib/active_record/connection_adapters/sqlite_adapter.rb	(working copy)
+@@ -19,6 +19,7 @@
+           :results_as_hash => true,
+           :type_translation => false
+         )
++        db.busy_timeout(config[:timeout]) unless config[:timeout].nil?
+         ConnectionAdapters::SQLiteAdapter.new(db, logger)
+       end
diff --git a/automation/admin/config/boot.rb b/automation/admin/config/boot.rb
new file mode 100644
index 0000000..9fcd50f
--- /dev/null
+++ b/automation/admin/config/boot.rb
@@ -0,0 +1,19 @@
+# Don't change this file. Configuration is done in config/environment.rb and config/environments/*.rb
+
+unless defined?(RAILS_ROOT)
+  root_path = File.join(File.dirname(__FILE__), '..')
+  unless RUBY_PLATFORM =~ /mswin32/
+    require 'pathname'
+    root_path = Pathname.new(root_path).cleanpath(true).to_s
+  end
+  RAILS_ROOT = root_path
+end
+
+if File.directory?("#{RAILS_ROOT}/vendor/rails")
+  require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
+else
+  require 'rubygems'
+  require 'initializer'
+end
+
+Rails::Initializer.run(:set_load_path)
diff --git a/automation/admin/config/database.yml-mysql b/automation/admin/config/database.yml-mysql
new file mode 100644
index 0000000..7feac9f
--- /dev/null
+++ b/automation/admin/config/database.yml-mysql
@@ -0,0 +1,20 @@
+# Example config file for a simple MySQL database backend.
+#
+development:
+  adapter: mysql
+  database: test
+  host: localhost
+
+production:
+  adapter: mysql
+  database: test
+  host: localhost
+
+# Warning: The database defined as 'test' will be erased and
+# re-generated from your development database when you run 'rake'.
+# Do not set this db to the same as development or production.
+test:
+  adapter: mysql
+  database: test_test
+  host: localhost
+
diff --git a/automation/admin/config/database.yml-sqlite b/automation/admin/config/database.yml-sqlite
new file mode 100644
index 0000000..378d6d9
--- /dev/null
+++ b/automation/admin/config/database.yml-sqlite
@@ -0,0 +1,29 @@
+# Example config file for a simple SQLite database backend.
+#
+# NOTE that currently the controllers assume a MySQL backend, and they
+# will probably need some tweaking of embedded SQL to get them to work
+# correctly (apologies for this blatant violation of MVC!).
+#
+# The timeout value below relates to this rails ticket,
+# which is not currently part of the main rails distribution:
+# http://dev.rubyonrails.org/ticket/6126
+# See also the README in this directory for more information.
+#
+development:
+  adapter: sqlite3
+  dbfile: db/autosubs.db
+  timeout: 10000
+
+production:
+  adapter: sqlite3
+  dbfile: db/autosubs.db
+  timeout: 10000
+
+# Warning: The database defined as 'test' will be erased and
+# re-generated from your development database when you run 'rake'.
+# Do not set this db to the same as development or production.
+test:
+  adapter: sqlite3
+  dbfile: db/autosubs_test.db
+  timeout: 10000
+
diff --git a/automation/admin/config/environment.rb b/automation/admin/config/environment.rb
new file mode 100644
index 0000000..e33cf58
--- /dev/null
+++ b/automation/admin/config/environment.rb
@@ -0,0 +1,66 @@
+# Be sure to restart your web server when you modify this file.
+
+# Uncomment below to force Rails into production mode when 
+# you don't control web/app server and can't set it the proper way
+# ENV['RAILS_ENV'] ||= 'production'
+
+# Bootstrap the Rails environment, frameworks, and default configuration
+require File.join(File.dirname(__FILE__), 'boot')
+
+Rails::Initializer.run do |config|
+  # Settings in config/environments/* take precedence those specified here
+  
+  # Skip frameworks you're not going to use
+  # config.frameworks -= [ :action_web_service, :action_mailer ]
+
+  # Add additional load paths for your own custom dirs
+  # config.load_paths += %W( #{RAILS_ROOT}/extras )
+
+  # Force all environments to use the same logger level 
+  # (by default production uses :info, the others :debug)
+  # config.log_level = :debug
+
+  # Use the database for sessions instead of the file system
+  # (create the session table with 'rake create_sessions_table')
+  # config.action_controller.session_store = :active_record_store
+
+  # Enable page/fragment caching by setting a file-based store
+  # (remember to create the caching directory and make it readable to the application)
+  # config.action_controller.fragment_cache_store = :file_store, "#{RAILS_ROOT}/cache"
+
+  # Activate observers that should always be running
+  # config.active_record.observers = :cacher, :garbage_collector
+
+  # Make Active Record use UTC-base instead of local time
+  # config.active_record.default_timezone = :utc
+  
+  # Use Active Record's schema dumper instead of SQL when creating the test database
+  # (enables use of different database adapters for development and test environments)
+  # config.active_record.schema_format = :ruby
+
+  # See Rails::Configuration for more options
+
+  # :secret, below, is a >30 character secret phrase used is generating session cookies.
+  config.action_controller.session = { :session_key => "_myapp_session",
+    :secret => "Lyta had a little Vorlon/her skin was pale as snow./" +
+               "Everywhere that Lyta went/the Vorlon was sure to go." }
+
+end
+
+# Add new inflection rules using the following format 
+# (all these examples are active by default):
+# Inflector.inflections do |inflect|
+#   inflect.plural /^(ox)$/i, '\1en'
+#   inflect.singular /^(ox)en/i, '\1'
+#   inflect.irregular 'person', 'people'
+#   inflect.uncountable %w( fish sheep )
+# end
+
+# Include your application configuration below
+
+# All our database timestamps are GMT.
+ActiveRecord::Base.default_timezone = :utc
+
+# Pagination now uses an external gem. Apparently
+# this is much better than the classic pagination.
+require 'will_paginate'
diff --git a/automation/admin/config/environments/development.rb b/automation/admin/config/environments/development.rb
new file mode 100644
index 0000000..831d73f
--- /dev/null
+++ b/automation/admin/config/environments/development.rb
@@ -0,0 +1,20 @@
+# Settings specified here will take precedence over those in config/environment.rb
+
+# In the development environment your application's code is reloaded on
+# every request.  This slows down response time but is perfect for development
+# since you don't have to restart the webserver when you make code changes.
+config.cache_classes     = false
+
+# Log error messages when you accidentally call methods on nil.
+config.whiny_nils        = true
+
+# Enable the breakpoint server that script/breakpointer connects to
+# DEPRECATED in rails 2.0.0
+#config.breakpoint_server = true
+
+# Show full error reports and disable caching
+config.action_controller.consider_all_requests_local = true
+config.action_controller.perform_caching             = false
+
+# Don't care if the mailer can't send
+config.action_mailer.raise_delivery_errors = false
diff --git a/automation/admin/config/environments/production.rb b/automation/admin/config/environments/production.rb
new file mode 100644
index 0000000..c9a4396
--- /dev/null
+++ b/automation/admin/config/environments/production.rb
@@ -0,0 +1,19 @@
+# Settings specified here will take precedence over those in config/environment.rb
+
+# The production environment is meant for finished, "live" apps.
+# Code is not reloaded between requests
+config.cache_classes = true
+
+# Use a different logger for distributed setups
+# config.logger        = SyslogLogger.new
+
+
+# Full error reports are disabled and caching is turned on
+config.action_controller.consider_all_requests_local = false
+config.action_controller.perform_caching             = true
+
+# Enable serving of images, stylesheets, and javascripts from an asset server
+# config.action_controller.asset_host                  = "http://assets.example.com"
+
+# Disable delivery errors if you bad email addresses should just be ignored
+# config.action_mailer.raise_delivery_errors = false
diff --git a/automation/admin/config/environments/test.rb b/automation/admin/config/environments/test.rb
new file mode 100644
index 0000000..6a4cddb
--- /dev/null
+++ b/automation/admin/config/environments/test.rb
@@ -0,0 +1,19 @@
+# Settings specified here will take precedence over those in config/environment.rb
+
+# The test environment is used exclusively to run your application's
+# test suite.  You never need to work with it otherwise.  Remember that
+# your test database is "scratch space" for the test suite and is wiped
+# and recreated between test runs.  Don't rely on the data there!
+config.cache_classes = true
+
+# Log error messages when you accidentally call methods on nil.
+config.whiny_nils    = true
+
+# Show full error reports and disable caching
+config.action_controller.consider_all_requests_local = true
+config.action_controller.perform_caching             = false
+
+# Tell ActionMailer not to deliver emails to the real world.
+# The :test delivery method accumulates sent emails in the
+# ActionMailer::Base.deliveries array.
+config.action_mailer.delivery_method = :test
\ No newline at end of file
diff --git a/automation/admin/config/lighttpd.conf b/automation/admin/config/lighttpd.conf
new file mode 100644
index 0000000..5f2c087
--- /dev/null
+++ b/automation/admin/config/lighttpd.conf
@@ -0,0 +1,53 @@
+# Default configuration file for the lighttpd web server
+# Start using ./script/server lighttpd
+
+server.bind = "0.0.0.0"
+server.port = 8087
+
+server.modules           = ( "mod_rewrite", "mod_accesslog", "mod_fastcgi", "mod_compress", "mod_expire" )
+
+server.error-handler-404 = "/dispatch.fcgi"
+server.document-root     = CWD + "/public/"
+
+server.errorlog          = CWD + "/log/lighttpd.error.log"
+accesslog.filename       = CWD + "/log/lighttpd.access.log"
+
+url.rewrite              = ( "^/$" => "index.html", "^([^.]+)$" => "$1.html" )
+
+compress.filetype        = ( "text/plain", "text/html", "text/css", "text/javascript" )
+compress.cache-dir       = CWD + "/tmp/cache"
+
+expire.url               = ( "/favicon.ico"  => "access 3 days", 
+                             "/images/"      => "access 3 days", 
+                             "/stylesheets/" => "access 3 days",
+                             "/javascripts/" => "access 3 days" )
+
+
+# Change *-procs to 2 if you need to use Upload Progress or other tasks that
+# *need* to execute a second request while the first is still pending.
+fastcgi.server      = ( ".fcgi" => ( "localhost" => (
+  "min-procs"       => 1, 
+  "max-procs"       => 1,
+  "socket"          => CWD + "/tmp/sockets/fcgi.socket",
+  "bin-path"        => CWD + "/public/dispatch.fcgi",
+  "bin-environment" => ( "RAILS_ENV" => "development" )
+) ) )
+
+mimetype.assign = (  
+  ".css"        =>  "text/css",
+  ".gif"        =>  "image/gif",
+  ".htm"        =>  "text/html",
+  ".html"       =>  "text/html",
+  ".jpeg"       =>  "image/jpeg",
+  ".jpg"        =>  "image/jpeg",
+  ".js"         =>  "text/javascript",
+  ".png"        =>  "image/png",
+  ".swf"        =>  "application/x-shockwave-flash",
+  ".txt"        =>  "text/plain"
+)
+
+# Making sure file uploads above 64k always work when using IE or Safari
+# For more information, see http://trac.lighttpd.net/trac/ticket/360
+$HTTP["useragent"] =~ "^(.*MSIE.*)|(.*AppleWebKit.*)$" {
+  server.max-keep-alive-requests = 0
+}
diff --git a/automation/admin/config/routes.rb b/automation/admin/config/routes.rb
new file mode 100644
index 0000000..6f98402
--- /dev/null
+++ b/automation/admin/config/routes.rb
@@ -0,0 +1,19 @@
+ActionController::Routing::Routes.draw do |map|
+  # Add your own custom routes here.
+  # The priority is based upon order of creation: first created -> highest priority.
+  
+  # Here's a sample route:
+  # map.connect 'products/:id', :controller => 'catalog', :action => 'view'
+  # Keep in mind you can assign values other than :controller and :action
+
+  # You can have the root of your site routed by hooking up '' 
+  # -- just remember to delete public/index.html.
+#  map.connect '', :controller => 'tab2mages', :action => 'list'
+
+  # Allow downloading Web Service WSDL as a file with an extension
+  # instead of a file named 'wsdl'
+  map.connect ':controller/service.wsdl', :action => 'wsdl'
+
+  # Install the default route as the lowest priority.
+  map.connect ':controller/:action/:id'
+end
diff --git a/automation/admin/db/database-mysql.dump b/automation/admin/db/database-mysql.dump
new file mode 100644
index 0000000..928de6c
--- /dev/null
+++ b/automation/admin/db/database-mysql.dump
@@ -0,0 +1,932 @@
+-- MySQL dump 10.10
+--
+-- Host: localhost    Database: autosubs_test
+-- ------------------------------------------------------
+-- Server version	5.0.27
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+--
+-- Table structure for table `array_designs`
+--
+
+DROP TABLE IF EXISTS `array_designs`;
+CREATE TABLE `array_designs` (
+  `id` int(11) NOT NULL auto_increment,
+  `miamexpress_subid` int(11) default NULL,
+  `accession` varchar(255) default NULL,
+  `name` varchar(255) default NULL,
+  `miamexpress_login` varchar(50) default NULL,
+  `status` varchar(50) default NULL,
+  `data_warehouse_ready` char(15) default NULL,
+  `date_last_processed` datetime default NULL,
+  `comment` text,
+  `is_deleted` int(11) NOT NULL,
+  `miame_score` int(11) default NULL,
+  `in_data_warehouse` int(11) default NULL,
+  `annotation_source` varchar(50) default NULL,
+  `annotation_version` varchar(50) default NULL,
+  `biomart_table_name` varchar(50) default NULL,
+  `release_date` datetime default NULL,
+  `is_released` int(11) default NULL,
+  PRIMARY KEY  (`id`),
+  UNIQUE KEY `miamexpress_subid` (`miamexpress_subid`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `array_designs`
+--
+
+LOCK TABLES `array_designs` WRITE;
+/*!40000 ALTER TABLE `array_designs` DISABLE KEYS */;
+/*!40000 ALTER TABLE `array_designs` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `array_designs_experiments`
+--
+
+DROP TABLE IF EXISTS `array_designs_experiments`;
+CREATE TABLE `array_designs_experiments` (
+  `id` int(11) NOT NULL auto_increment,
+  `array_design_id` int(11) NOT NULL,
+  `experiment_id` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `array_design_id` (`array_design_id`),
+  KEY `experiment_id` (`experiment_id`),
+  CONSTRAINT `array_designs_experiments_ibfk_1` FOREIGN KEY (`array_design_id`) REFERENCES `array_designs` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `array_designs_experiments_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `array_designs_experiments`
+--
+
+LOCK TABLES `array_designs_experiments` WRITE;
+/*!40000 ALTER TABLE `array_designs_experiments` DISABLE KEYS */;
+/*!40000 ALTER TABLE `array_designs_experiments` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `array_designs_organisms`
+--
+
+DROP TABLE IF EXISTS `array_designs_organisms`;
+CREATE TABLE `array_designs_organisms` (
+  `id` int(11) NOT NULL auto_increment,
+  `organism_id` int(11) NOT NULL,
+  `array_design_id` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `organism_id` (`organism_id`),
+  KEY `array_design_id` (`array_design_id`),
+  CONSTRAINT `array_designs_organisms_ibfk_1` FOREIGN KEY (`organism_id`) REFERENCES `organisms` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `array_designs_organisms_ibfk_2` FOREIGN KEY (`array_design_id`) REFERENCES `array_designs` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `array_designs_organisms`
+--
+
+LOCK TABLES `array_designs_organisms` WRITE;
+/*!40000 ALTER TABLE `array_designs_organisms` DISABLE KEYS */;
+/*!40000 ALTER TABLE `array_designs_organisms` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `categories`
+--
+
+DROP TABLE IF EXISTS `categories`;
+CREATE TABLE `categories` (
+  `id` int(11) NOT NULL auto_increment,
+  `ontology_term` varchar(50) default NULL,
+  `display_label` varchar(50) default NULL,
+  `is_common` int(11) default NULL,
+  `is_bmc` int(11) default NULL,
+  `is_fv` int(11) default NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `categories`
+--
+
+LOCK TABLES `categories` WRITE;
+/*!40000 ALTER TABLE `categories` DISABLE KEYS */;
+INSERT INTO `categories` VALUES (1,'Age','Age',NULL,1,1,0),(2,'OrganismPart','Organism part',NULL,1,1,0),(3,'DiseaseState','Disease state',NULL,1,1,0),(4,'CellLine','Cell line',NULL,1,1,0),(5,'CellType','Cell type',NULL,1,1,0),(6,'DevelopmentalStage','Developmental stage',NULL,1,1,0),(7,'DiseaseStaging','Disease staging',NULL,1,1,0),(8,'EnvironmentalHistory','Environmental history',NULL,1,1,0),(9,'GeneticModification','Genetic modification',NULL,1,1,0),(10,'Histology','Histology',NULL,1, [...]
+/*!40000 ALTER TABLE `categories` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `categories_designs`
+--
+
+DROP TABLE IF EXISTS `categories_designs`;
+CREATE TABLE `categories_designs` (
+  `category_id` int(11) NOT NULL,
+  `design_id` int(11) NOT NULL,
+  KEY `category_id` (`category_id`),
+  KEY `design_id` (`design_id`),
+  CONSTRAINT `categories_designs_ibfk_1` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `categories_designs_ibfk_2` FOREIGN KEY (`design_id`) REFERENCES `designs` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `categories_designs`
+--
+
+LOCK TABLES `categories_designs` WRITE;
+/*!40000 ALTER TABLE `categories_designs` DISABLE KEYS */;
+INSERT INTO `categories_designs` VALUES (3,1),(8,3),(12,3),(16,2),(5,4),(6,5),(12,6),(9,7),(12,7),(2,8),(15,9),(16,10),(20,11),(22,13),(23,13),(21,12),(23,18),(3,2),(24,2),(12,21),(24,22),(1,5),(25,12),(29,33);
+/*!40000 ALTER TABLE `categories_designs` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `categories_materials`
+--
+
+DROP TABLE IF EXISTS `categories_materials`;
+CREATE TABLE `categories_materials` (
+  `category_id` int(11) NOT NULL,
+  `material_id` int(11) NOT NULL,
+  KEY `category_id` (`category_id`),
+  KEY `material_id` (`material_id`),
+  CONSTRAINT `categories_materials_ibfk_1` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `categories_materials_ibfk_2` FOREIGN KEY (`material_id`) REFERENCES `materials` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `categories_materials`
+--
+
+LOCK TABLES `categories_materials` WRITE;
+/*!40000 ALTER TABLE `categories_materials` DISABLE KEYS */;
+INSERT INTO `categories_materials` VALUES (2,1),(4,2),(5,2),(1,3),(11,3),(15,3);
+/*!40000 ALTER TABLE `categories_materials` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `categories_taxons`
+--
+
+DROP TABLE IF EXISTS `categories_taxons`;
+CREATE TABLE `categories_taxons` (
+  `category_id` int(11) NOT NULL,
+  `taxon_id` int(11) NOT NULL,
+  KEY `category_id` (`category_id`),
+  KEY `taxon_id` (`taxon_id`),
+  CONSTRAINT `categories_taxons_ibfk_1` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `categories_taxons_ibfk_2` FOREIGN KEY (`taxon_id`) REFERENCES `taxons` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `categories_taxons`
+--
+
+LOCK TABLES `categories_taxons` WRITE;
+/*!40000 ALTER TABLE `categories_taxons` DISABLE KEYS */;
+INSERT INTO `categories_taxons` VALUES (1,1),(15,1),(12,2),(15,2),(16,2),(12,6),(16,6),(6,5),(12,5),(16,5),(16,3),(6,4),(2,4),(7,5),(25,1),(26,1),(6,7),(12,7),(16,7),(1,8),(6,8),(14,8),(15,8),(16,8),(27,8),(28,8),(1,9),(2,9),(6,9),(8,9),(15,9),(16,9),(24,9),(12,8);
+/*!40000 ALTER TABLE `categories_taxons` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `data_files`
+--
+
+DROP TABLE IF EXISTS `data_files`;
+CREATE TABLE `data_files` (
+  `id` int(11) NOT NULL auto_increment,
+  `experiment_id` int(11) NOT NULL,
+  `name` varchar(255) default NULL,
+  `is_unpacked` int(11) default NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `experiment_id` (`experiment_id`),
+  CONSTRAINT `data_files_ibfk_1` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `data_files`
+--
+
+LOCK TABLES `data_files` WRITE;
+/*!40000 ALTER TABLE `data_files` DISABLE KEYS */;
+/*!40000 ALTER TABLE `data_files` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `data_formats`
+--
+
+DROP TABLE IF EXISTS `data_formats`;
+CREATE TABLE `data_formats` (
+  `id` int(11) NOT NULL auto_increment,
+  `name` varchar(50) character set latin1 collate latin1_general_cs NOT NULL,
+  PRIMARY KEY  (`id`),
+  UNIQUE KEY `name` (`name`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `data_formats`
+--
+
+LOCK TABLES `data_formats` WRITE;
+/*!40000 ALTER TABLE `data_formats` DISABLE KEYS */;
+/*!40000 ALTER TABLE `data_formats` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `design_instances`
+--
+
+DROP TABLE IF EXISTS `design_instances`;
+CREATE TABLE `design_instances` (
+  `id` int(11) NOT NULL auto_increment,
+  `design_id` int(11) NOT NULL,
+  `experiment_id` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `design_id` (`design_id`),
+  KEY `experiment_id` (`experiment_id`),
+  CONSTRAINT `design_instances_ibfk_1` FOREIGN KEY (`design_id`) REFERENCES `designs` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `design_instances_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `design_instances`
+--
+
+LOCK TABLES `design_instances` WRITE;
+/*!40000 ALTER TABLE `design_instances` DISABLE KEYS */;
+/*!40000 ALTER TABLE `design_instances` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `designs`
+--
+
+DROP TABLE IF EXISTS `designs`;
+CREATE TABLE `designs` (
+  `id` int(11) NOT NULL auto_increment,
+  `display_label` varchar(50) default NULL,
+  `ontology_category` varchar(50) default NULL,
+  `ontology_value` varchar(50) default NULL,
+  `design_type` char(15) NOT NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `designs`
+--
+
+LOCK TABLES `designs` WRITE;
+/*!40000 ALTER TABLE `designs` DISABLE KEYS */;
+INSERT INTO `designs` VALUES (1,'Disease state','ExperimentalDesignType','disease_state_design','biological',0),(2,'CGH','ExperimentalDesignType','comparative_genome_hybridization_design','technological',0),(3,'Behavior','ExperimentalDesignType','innate_behavior_design','biological',0),(4,'Cell type comparison','ExperimentalDesignType','cell_type_comparison_design','biological',0),(5,'Development or differentiation','ExperimentalDesignType','development_or_differentiation_design','biolog [...]
+/*!40000 ALTER TABLE `designs` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `events`
+--
+
+DROP TABLE IF EXISTS `events`;
+CREATE TABLE `events` (
+  `id` int(11) NOT NULL auto_increment,
+  `array_design_id` int(11) default NULL,
+  `experiment_id` int(11) default NULL,
+  `event_type` varchar(50) NOT NULL,
+  `was_successful` int(11) default NULL,
+  `source_db` varchar(30) default NULL,
+  `target_db` varchar(30) default NULL,
+  `start_time` datetime default NULL,
+  `end_time` datetime default NULL,
+  `machine` varchar(50) default NULL,
+  `operator` varchar(30) default NULL,
+  `log_file` varchar(511) default NULL,
+  `jobregister_dbid` int(15) default NULL,
+  `comment` text,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `array_design_id` (`array_design_id`),
+  KEY `experiment_id` (`experiment_id`),
+  CONSTRAINT `events_ibfk_1` FOREIGN KEY (`array_design_id`) REFERENCES `array_designs` (`id`),
+  CONSTRAINT `events_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `events`
+--
+
+LOCK TABLES `events` WRITE;
+/*!40000 ALTER TABLE `events` DISABLE KEYS */;
+/*!40000 ALTER TABLE `events` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `experiments`
+--
+
+DROP TABLE IF EXISTS `experiments`;
+CREATE TABLE `experiments` (
+  `id` int(11) NOT NULL auto_increment,
+  `accession` varchar(255) default NULL,
+  `name` varchar(255) default NULL,
+  `user_id` int(11) default NULL,
+  `checker_score` int(11) default NULL,
+  `software` varchar(100) default NULL,
+  `status` varchar(50) default NULL,
+  `data_warehouse_ready` int(11) default NULL,
+  `date_last_edited` datetime default NULL,
+  `date_submitted` datetime default NULL,
+  `date_last_processed` datetime default NULL,
+  `in_curation` int(11) default NULL,
+  `curator` char(30) default NULL,
+  `comment` text,
+  `experiment_type` char(30) default NULL,
+  `miamexpress_login` char(30) default NULL,
+  `miamexpress_subid` int(11) default NULL,
+  `is_affymetrix` int(11) default NULL,
+  `is_mx_batchloader` int(11) default NULL,
+  `is_deleted` int(11) NOT NULL,
+  `miame_score` int(11) default NULL,
+  `in_data_warehouse` int(11) default NULL,
+  `num_submissions` int(11) default NULL,
+  `submitter_description` text,
+  `curated_name` varchar(255) default NULL,
+  `num_samples` int(11) default NULL,
+  `num_hybridizations` int(11) default NULL,
+  `has_raw_data` int(11) default NULL,
+  `has_processed_data` int(11) default NULL,
+  `release_date` datetime default NULL,
+  `is_released` int(11) default NULL,
+  `ae_miame_score` int(11) default NULL,
+  `ae_data_warehouse_score` int(11) default NULL,
+  PRIMARY KEY  (`id`),
+  KEY `user_id` (`user_id`),
+  CONSTRAINT `experiments_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `experiments`
+--
+
+LOCK TABLES `experiments` WRITE;
+/*!40000 ALTER TABLE `experiments` DISABLE KEYS */;
+/*!40000 ALTER TABLE `experiments` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `experiments_factors`
+--
+
+DROP TABLE IF EXISTS `experiments_factors`;
+CREATE TABLE `experiments_factors` (
+  `id` int(11) NOT NULL auto_increment,
+  `experiment_id` int(11) NOT NULL,
+  `factor_id` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `experiment_id` (`experiment_id`),
+  KEY `factor_id` (`factor_id`),
+  CONSTRAINT `experiments_factors_ibfk_1` FOREIGN KEY (`factor_id`) REFERENCES `factors` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `experiments_factors_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `experiments_factors`
+--
+
+LOCK TABLES `experiments_factors` WRITE;
+/*!40000 ALTER TABLE `experiments_factors` DISABLE KEYS */;
+/*!40000 ALTER TABLE `experiments_factors` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `experiments_loaded_data`
+--
+
+DROP TABLE IF EXISTS `experiments_loaded_data`;
+CREATE TABLE `experiments_loaded_data` (
+  `experiment_id` int(11) NOT NULL,
+  `loaded_data_id` int(11) NOT NULL,
+  KEY `experiment_id` (`experiment_id`),
+  KEY `loaded_data_id` (`loaded_data_id`),
+  CONSTRAINT `loaded_data_experiment_ibfk_1` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `loaded_data_loaded_data_ibfk_1` FOREIGN KEY (`loaded_data_id`) REFERENCES `loaded_data` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `experiments_loaded_data`
+--
+
+LOCK TABLES `experiments_loaded_data` WRITE;
+/*!40000 ALTER TABLE `experiments_loaded_data` DISABLE KEYS */;
+/*!40000 ALTER TABLE `experiments_loaded_data` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `experiments_quantitation_types`
+--
+
+DROP TABLE IF EXISTS `experiments_quantitation_types`;
+CREATE TABLE `experiments_quantitation_types` (
+  `id` int(11) NOT NULL auto_increment,
+  `quantitation_type_id` int(11) NOT NULL,
+  `experiment_id` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `quantitation_type_id` (`quantitation_type_id`),
+  KEY `experiment_id` (`experiment_id`),
+  CONSTRAINT `experiments_quantitation_types_ibfk_1` FOREIGN KEY (`quantitation_type_id`) REFERENCES `quantitation_types` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `experiments_quantitation_types_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `experiments_quantitation_types`
+--
+
+LOCK TABLES `experiments_quantitation_types` WRITE;
+/*!40000 ALTER TABLE `experiments_quantitation_types` DISABLE KEYS */;
+/*!40000 ALTER TABLE `experiments_quantitation_types` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `factors`
+--
+
+DROP TABLE IF EXISTS `factors`;
+CREATE TABLE `factors` (
+  `id` int(11) NOT NULL auto_increment,
+  `name` varchar(128) NOT NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `factors`
+--
+
+LOCK TABLES `factors` WRITE;
+/*!40000 ALTER TABLE `factors` DISABLE KEYS */;
+/*!40000 ALTER TABLE `factors` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `loaded_data`
+--
+
+DROP TABLE IF EXISTS `loaded_data`;
+CREATE TABLE `loaded_data` (
+  `id` int(11) NOT NULL auto_increment,
+  `identifier` varchar(255) character set latin1 collate latin1_general_cs NOT NULL,
+  `md5_hash` char(35) NOT NULL,
+  `data_format_id` int(11) NOT NULL,
+  `is_deleted` int(11) NOT NULL,
+  `platform_id` int(11) default NULL,
+  `needs_metrics_calculation` int(11) NOT NULL,
+  `date_hashed` datetime default NULL,
+  PRIMARY KEY  (`id`),
+  KEY `data_format_id` (`data_format_id`),
+  KEY `platform_loaded_data_ibfk_1` (`platform_id`),
+  CONSTRAINT `platform_loaded_data_ibfk_1` FOREIGN KEY (`platform_id`) REFERENCES `platforms` (`id`),
+  CONSTRAINT `data_format_loaded_data_ibfk_1` FOREIGN KEY (`data_format_id`) REFERENCES `data_formats` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `loaded_data`
+--
+
+LOCK TABLES `loaded_data` WRITE;
+/*!40000 ALTER TABLE `loaded_data` DISABLE KEYS */;
+/*!40000 ALTER TABLE `loaded_data` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `loaded_data_quality_metrics`
+--
+
+DROP TABLE IF EXISTS `loaded_data_quality_metrics`;
+CREATE TABLE `loaded_data_quality_metrics` (
+  `id` int(11) NOT NULL auto_increment,
+  `value` decimal(12,5) default NULL,
+  `quality_metric_id` int(11) NOT NULL,
+  `loaded_data_id` int(11) NOT NULL,
+  `date_calculated` datetime default NULL,
+  PRIMARY KEY  (`id`),
+  KEY `quality_metric_id` (`quality_metric_id`),
+  KEY `loaded_data_id` (`loaded_data_id`),
+  CONSTRAINT `quality_metric_loaded_data_ibfk_1` FOREIGN KEY (`loaded_data_id`) REFERENCES `loaded_data` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `quality_metric_quality_metric_ibfk_1` FOREIGN KEY (`quality_metric_id`) REFERENCES `quality_metrics` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `loaded_data_quality_metrics`
+--
+
+LOCK TABLES `loaded_data_quality_metrics` WRITE;
+/*!40000 ALTER TABLE `loaded_data_quality_metrics` DISABLE KEYS */;
+/*!40000 ALTER TABLE `loaded_data_quality_metrics` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `material_instances`
+--
+
+DROP TABLE IF EXISTS `material_instances`;
+CREATE TABLE `material_instances` (
+  `id` int(11) NOT NULL auto_increment,
+  `material_id` int(11) NOT NULL,
+  `experiment_id` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `material_id` (`material_id`),
+  KEY `experiment_id` (`experiment_id`),
+  CONSTRAINT `material_instances_ibfk_1` FOREIGN KEY (`material_id`) REFERENCES `materials` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `material_instances_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `material_instances`
+--
+
+LOCK TABLES `material_instances` WRITE;
+/*!40000 ALTER TABLE `material_instances` DISABLE KEYS */;
+/*!40000 ALTER TABLE `material_instances` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `materials`
+--
+
+DROP TABLE IF EXISTS `materials`;
+CREATE TABLE `materials` (
+  `id` int(11) NOT NULL auto_increment,
+  `display_label` varchar(50) default NULL,
+  `ontology_category` varchar(50) default NULL,
+  `ontology_value` varchar(50) default NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `materials`
+--
+
+LOCK TABLES `materials` WRITE;
+/*!40000 ALTER TABLE `materials` DISABLE KEYS */;
+INSERT INTO `materials` VALUES (1,'Organism part','MaterialType','organism_part',0),(2,'Cell culture','MaterialType','cell',0),(3,'Whole organism','MaterialType','whole_organism',0);
+/*!40000 ALTER TABLE `materials` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `meta`
+--
+
+DROP TABLE IF EXISTS `meta`;
+CREATE TABLE `meta` (
+  `name` varchar(128) NOT NULL default '',
+  `value` varchar(128) NOT NULL default '',
+  PRIMARY KEY  (`name`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `meta`
+--
+
+LOCK TABLES `meta` WRITE;
+/*!40000 ALTER TABLE `meta` DISABLE KEYS */;
+INSERT INTO `meta` VALUES ('serializer','Storable'),('index_subfeatures','1'),('autoindex','1');
+/*!40000 ALTER TABLE `meta` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `organism_instances`
+--
+
+DROP TABLE IF EXISTS `organism_instances`;
+CREATE TABLE `organism_instances` (
+  `id` int(11) NOT NULL auto_increment,
+  `organism_id` int(11) NOT NULL,
+  `experiment_id` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `organism_id` (`organism_id`),
+  KEY `experiment_id` (`experiment_id`),
+  CONSTRAINT `organism_instances_ibfk_1` FOREIGN KEY (`organism_id`) REFERENCES `organisms` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `organism_instances_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `organism_instances`
+--
+
+LOCK TABLES `organism_instances` WRITE;
+/*!40000 ALTER TABLE `organism_instances` DISABLE KEYS */;
+/*!40000 ALTER TABLE `organism_instances` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `organisms`
+--
+
+DROP TABLE IF EXISTS `organisms`;
+CREATE TABLE `organisms` (
+  `id` int(11) NOT NULL auto_increment,
+  `scientific_name` varchar(50) default NULL,
+  `common_name` varchar(50) default NULL,
+  `accession` int(11) default NULL,
+  `taxon_id` int(11) default NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `taxon_id` (`taxon_id`),
+  CONSTRAINT `organisms_ibfk_1` FOREIGN KEY (`taxon_id`) REFERENCES `taxons` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `organisms`
+--
+
+LOCK TABLES `organisms` WRITE;
+/*!40000 ALTER TABLE `organisms` DISABLE KEYS */;
+INSERT INTO `organisms` VALUES (1,'Homo sapiens','Human',9606,1,0),(2,'Saccharomyces cerevisiae','Baker\'s yeast',4932,2,0),(3,'Schizosaccharomyces pombe','Fission yeast',4896,2,0),(4,'Mus musculus','House mouse',10090,3,0),(5,'Escherichia coli','E. coli',562,6,0),(6,'Arabidopsis thaliana','Thale cress',3702,4,0),(7,'Rattus norvegicus','Norway rat',10116,3,0),(8,'Pan troglodytes','Chimpanzee',9598,1,0),(9,'Apis mellifera','Honey bee',7460,5,0),(11,'Plasmodium falciparum','Malaria parasit [...]
+/*!40000 ALTER TABLE `organisms` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `permissions`
+--
+
+DROP TABLE IF EXISTS `permissions`;
+CREATE TABLE `permissions` (
+  `id` int(11) NOT NULL auto_increment,
+  `name` varchar(40) NOT NULL,
+  `info` varchar(80) default NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `permissions`
+--
+
+LOCK TABLES `permissions` WRITE;
+/*!40000 ALTER TABLE `permissions` DISABLE KEYS */;
+INSERT INTO `permissions` VALUES (1,'.*/.*','Access All Areas',0),(2,'experiments/.*','Full experiment access',0),(4,'array_designs/.*','Full MIAMExpress array access',0),(5,'protocols/.*','Full Tab2MAGE protocol access',0),(6,'tab2mages/.*','Full Tab2MAGE experiment access',0),(7,'miamexps/.*','Full MIAMExpress experiment access',0),(8,'design/.*','Full designs access',0),(9,'material/.*','Full materials access',0),(10,'organism/.*','Full organisms access',0),(11,'taxon/.*','Full taxons [...]
+/*!40000 ALTER TABLE `permissions` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `permissions_roles`
+--
+
+DROP TABLE IF EXISTS `permissions_roles`;
+CREATE TABLE `permissions_roles` (
+  `role_id` int(11) NOT NULL,
+  `permission_id` int(11) NOT NULL,
+  KEY `role_id` (`role_id`),
+  KEY `permission_id` (`permission_id`),
+  CONSTRAINT `permissions_roles_ibfk_1` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `permissions_roles_ibfk_2` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `permissions_roles`
+--
+
+LOCK TABLES `permissions_roles` WRITE;
+/*!40000 ALTER TABLE `permissions_roles` DISABLE KEYS */;
+INSERT INTO `permissions_roles` VALUES (1,1),(2,4),(2,5),(2,6),(2,7),(2,8),(2,9),(2,10),(2,11),(2,12);
+/*!40000 ALTER TABLE `permissions_roles` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `platforms`
+--
+
+DROP TABLE IF EXISTS `platforms`;
+CREATE TABLE `platforms` (
+  `id` int(11) NOT NULL auto_increment,
+  `name` varchar(50) NOT NULL,
+  PRIMARY KEY  (`id`),
+  UNIQUE KEY `name` (`name`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `platforms`
+--
+
+LOCK TABLES `platforms` WRITE;
+/*!40000 ALTER TABLE `platforms` DISABLE KEYS */;
+/*!40000 ALTER TABLE `platforms` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `protocols`
+--
+
+DROP TABLE IF EXISTS `protocols`;
+CREATE TABLE `protocols` (
+  `id` int(11) NOT NULL auto_increment,
+  `accession` char(15) default NULL,
+  `user_accession` varchar(100) default NULL,
+  `expt_accession` char(15) default NULL,
+  `name` varchar(255) default NULL,
+  `date_last_processed` datetime default NULL,
+  `comment` text,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  UNIQUE KEY `accession` (`accession`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `protocols`
+--
+
+LOCK TABLES `protocols` WRITE;
+/*!40000 ALTER TABLE `protocols` DISABLE KEYS */;
+/*!40000 ALTER TABLE `protocols` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `quality_metrics`
+--
+
+DROP TABLE IF EXISTS `quality_metrics`;
+CREATE TABLE `quality_metrics` (
+  `id` int(11) NOT NULL auto_increment,
+  `type` varchar(50) NOT NULL,
+  `description` text,
+  PRIMARY KEY  (`id`),
+  UNIQUE KEY `type` (`type`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `quality_metrics`
+--
+
+LOCK TABLES `quality_metrics` WRITE;
+/*!40000 ALTER TABLE `quality_metrics` DISABLE KEYS */;
+/*!40000 ALTER TABLE `quality_metrics` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `quantitation_types`
+--
+
+DROP TABLE IF EXISTS `quantitation_types`;
+CREATE TABLE `quantitation_types` (
+  `id` int(11) NOT NULL auto_increment,
+  `name` varchar(128) character set latin1 collate latin1_general_cs NOT NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `quantitation_types`
+--
+
+LOCK TABLES `quantitation_types` WRITE;
+/*!40000 ALTER TABLE `quantitation_types` DISABLE KEYS */;
+/*!40000 ALTER TABLE `quantitation_types` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `roles`
+--
+
+DROP TABLE IF EXISTS `roles`;
+CREATE TABLE `roles` (
+  `id` int(11) NOT NULL auto_increment,
+  `name` varchar(40) NOT NULL,
+  `info` varchar(80) default NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `roles`
+--
+
+LOCK TABLES `roles` WRITE;
+/*!40000 ALTER TABLE `roles` DISABLE KEYS */;
+INSERT INTO `roles` VALUES (1,'admin','System superuser',0),(2,'curator','Annotation and automation access',0),(3,'submitter','Low-privilege user',0);
+/*!40000 ALTER TABLE `roles` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `roles_users`
+--
+
+DROP TABLE IF EXISTS `roles_users`;
+CREATE TABLE `roles_users` (
+  `user_id` int(11) NOT NULL,
+  `role_id` int(11) NOT NULL,
+  KEY `user_id` (`user_id`),
+  KEY `role_id` (`role_id`),
+  CONSTRAINT `roles_users_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `roles_users_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `roles_users`
+--
+
+LOCK TABLES `roles_users` WRITE;
+/*!40000 ALTER TABLE `roles_users` DISABLE KEYS */;
+INSERT INTO `roles_users` VALUES (1,1),(2,1),(2,2),(3,3);
+/*!40000 ALTER TABLE `roles_users` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `spreadsheets`
+--
+
+DROP TABLE IF EXISTS `spreadsheets`;
+CREATE TABLE `spreadsheets` (
+  `id` int(11) NOT NULL auto_increment,
+  `experiment_id` int(11) NOT NULL,
+  `name` varchar(255) default NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `experiment_id` (`experiment_id`),
+  CONSTRAINT `spreadsheets_ibfk_1` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `spreadsheets`
+--
+
+LOCK TABLES `spreadsheets` WRITE;
+/*!40000 ALTER TABLE `spreadsheets` DISABLE KEYS */;
+/*!40000 ALTER TABLE `spreadsheets` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `taxons`
+--
+
+DROP TABLE IF EXISTS `taxons`;
+CREATE TABLE `taxons` (
+  `id` int(11) NOT NULL auto_increment,
+  `scientific_name` varchar(50) default NULL,
+  `common_name` varchar(50) default NULL,
+  `accession` int(11) default NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `taxons`
+--
+
+LOCK TABLES `taxons` WRITE;
+/*!40000 ALTER TABLE `taxons` DISABLE KEYS */;
+INSERT INTO `taxons` VALUES (1,'Primates','Primates',9443,0),(2,'Ascomycota','Yeasts',4890,0),(3,'Rodentia','Rodents',9989,0),(4,'Viridiplantae','Green plants',33090,0),(5,'Insecta','True insects',50557,0),(6,'Bacteria','Eubacteria',2,0),(7,'Plasmodium','Plasmodium',5820,0),(8,'Nematoda','Nematodes',6231,0),(9,'Metazoa','Metazoans',33208,0);
+/*!40000 ALTER TABLE `taxons` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `users`
+--
+
+DROP TABLE IF EXISTS `users`;
+CREATE TABLE `users` (
+  `id` int(11) NOT NULL auto_increment,
+  `login` varchar(40) NOT NULL,
+  `name` varchar(40) default NULL,
+  `password` varchar(40) NOT NULL,
+  `email` varchar(100) default NULL,
+  `modified_at` datetime default NULL,
+  `created_at` datetime default NULL,
+  `access` datetime default NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  UNIQUE KEY `login` (`login`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `users`
+--
+
+LOCK TABLES `users` WRITE;
+/*!40000 ALTER TABLE `users` DISABLE KEYS */;
+INSERT INTO `users` VALUES (1,'admin','Administrator','admin','noreply at ebi.ac.uk',NULL,NULL,NULL,0),(2,'curator','Curator','curator','noreply at ebi.ac.uk',NULL,NULL,NULL,0),(3,'test','Test submitter','test','noreply at ebi.ac.uk',NULL,NULL,NULL,0);
+/*!40000 ALTER TABLE `users` ENABLE KEYS */;
+UNLOCK TABLES;
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+
+-- Dump completed on 2008-04-28 11:51:32
diff --git a/automation/admin/db/database-mysql.schema b/automation/admin/db/database-mysql.schema
new file mode 100644
index 0000000..23632a7
--- /dev/null
+++ b/automation/admin/db/database-mysql.schema
@@ -0,0 +1,829 @@
+-- MySQL dump 10.10
+--
+-- Host: localhost    Database: test
+-- ------------------------------------------------------
+-- Server version	5.0.27
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+--
+-- Table structure for table `array_designs`
+--
+
+DROP TABLE IF EXISTS `array_designs`;
+CREATE TABLE `array_designs` (
+  `id` int(11) NOT NULL auto_increment,
+  `miamexpress_subid` int(11) default NULL,
+  `accession` char(15) default NULL,
+  `name` varchar(255) default NULL,
+  `miamexpress_login` varchar(50) default NULL,
+  `status` varchar(50) default NULL,
+  `data_warehouse_ready` char(15) default NULL,
+  `date_last_processed` datetime default NULL,
+  `comment` text,
+  `is_deleted` int(11) NOT NULL,
+  `miame_score` int(11) default NULL,
+  `in_data_warehouse` int(11) default NULL,
+  PRIMARY KEY  (`id`),
+  UNIQUE KEY `miamexpress_subid` (`miamexpress_subid`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `array_designs`
+--
+
+LOCK TABLES `array_designs` WRITE;
+/*!40000 ALTER TABLE `array_designs` DISABLE KEYS */;
+/*!40000 ALTER TABLE `array_designs` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `categories`
+--
+
+DROP TABLE IF EXISTS `categories`;
+CREATE TABLE `categories` (
+  `id` int(11) NOT NULL auto_increment,
+  `ontology_term` varchar(50) default NULL,
+  `display_label` varchar(50) default NULL,
+  `is_common` int(11) default NULL,
+  `is_bmc` int(11) default NULL,
+  `is_fv` int(11) default NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `categories`
+--
+
+LOCK TABLES `categories` WRITE;
+/*!40000 ALTER TABLE `categories` DISABLE KEYS */;
+/*!40000 ALTER TABLE `categories` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `categories_designs`
+--
+
+DROP TABLE IF EXISTS `categories_designs`;
+CREATE TABLE `categories_designs` (
+  `category_id` int(11) NOT NULL,
+  `design_id` int(11) NOT NULL,
+  KEY `category_id` (`category_id`),
+  KEY `design_id` (`design_id`),
+  CONSTRAINT `categories_designs_ibfk_1` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `categories_designs_ibfk_2` FOREIGN KEY (`design_id`) REFERENCES `designs` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `categories_designs`
+--
+
+LOCK TABLES `categories_designs` WRITE;
+/*!40000 ALTER TABLE `categories_designs` DISABLE KEYS */;
+/*!40000 ALTER TABLE `categories_designs` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `categories_materials`
+--
+
+DROP TABLE IF EXISTS `categories_materials`;
+CREATE TABLE `categories_materials` (
+  `category_id` int(11) NOT NULL,
+  `material_id` int(11) NOT NULL,
+  KEY `category_id` (`category_id`),
+  KEY `material_id` (`material_id`),
+  CONSTRAINT `categories_materials_ibfk_1` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `categories_materials_ibfk_2` FOREIGN KEY (`material_id`) REFERENCES `materials` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `categories_materials`
+--
+
+LOCK TABLES `categories_materials` WRITE;
+/*!40000 ALTER TABLE `categories_materials` DISABLE KEYS */;
+/*!40000 ALTER TABLE `categories_materials` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `categories_taxons`
+--
+
+DROP TABLE IF EXISTS `categories_taxons`;
+CREATE TABLE `categories_taxons` (
+  `category_id` int(11) NOT NULL,
+  `taxon_id` int(11) NOT NULL,
+  KEY `category_id` (`category_id`),
+  KEY `taxon_id` (`taxon_id`),
+  CONSTRAINT `categories_taxons_ibfk_1` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `categories_taxons_ibfk_2` FOREIGN KEY (`taxon_id`) REFERENCES `taxons` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `categories_taxons`
+--
+
+LOCK TABLES `categories_taxons` WRITE;
+/*!40000 ALTER TABLE `categories_taxons` DISABLE KEYS */;
+/*!40000 ALTER TABLE `categories_taxons` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `data_files`
+--
+
+DROP TABLE IF EXISTS `data_files`;
+CREATE TABLE `data_files` (
+  `id` int(11) NOT NULL auto_increment,
+  `experiment_id` int(11) NOT NULL,
+  `name` varchar(255) default NULL,
+  `is_unpacked` int(11) default NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `experiment_id` (`experiment_id`),
+  CONSTRAINT `data_files_ibfk_1` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE RESTRICT
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `data_files`
+--
+
+LOCK TABLES `data_files` WRITE;
+/*!40000 ALTER TABLE `data_files` DISABLE KEYS */;
+/*!40000 ALTER TABLE `data_files` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `design_instances`
+--
+
+DROP TABLE IF EXISTS `design_instances`;
+CREATE TABLE `design_instances` (
+  `id` int(11) NOT NULL auto_increment,
+  `design_id` int(11) NOT NULL,
+  `experiment_id` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `design_id` (`design_id`),
+  KEY `experiment_id` (`experiment_id`),
+  CONSTRAINT `design_instances_ibfk_1` FOREIGN KEY (`design_id`) REFERENCES `designs` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `design_instances_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `design_instances`
+--
+
+LOCK TABLES `design_instances` WRITE;
+/*!40000 ALTER TABLE `design_instances` DISABLE KEYS */;
+/*!40000 ALTER TABLE `design_instances` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `designs`
+--
+
+DROP TABLE IF EXISTS `designs`;
+CREATE TABLE `designs` (
+  `id` int(11) NOT NULL auto_increment,
+  `display_label` varchar(50) default NULL,
+  `ontology_category` varchar(50) default NULL,
+  `ontology_value` varchar(50) default NULL,
+  `design_type` char(15) NOT NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `designs`
+--
+
+LOCK TABLES `designs` WRITE;
+/*!40000 ALTER TABLE `designs` DISABLE KEYS */;
+/*!40000 ALTER TABLE `designs` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `experiments`
+--
+
+DROP TABLE IF EXISTS `experiments`;
+CREATE TABLE `experiments` (
+  `id` int(11) NOT NULL auto_increment,
+  `accession` char(15) default NULL,
+  `name` varchar(255) default NULL,
+  `user_id` int(11) default NULL,
+  `checker_score` int(11) default NULL,
+  `software` varchar(100) default NULL,
+  `status` varchar(50) default NULL,
+  `data_warehouse_ready` int(11) default NULL,
+  `date_last_edited` datetime default NULL,
+  `date_submitted` datetime default NULL,
+  `date_last_processed` datetime default NULL,
+  `in_curation` int(11) default NULL,
+  `curator` char(30) default NULL,
+  `comment` text,
+  `experiment_type` char(30) default NULL,
+  `miamexpress_login` char(30) default NULL,
+  `miamexpress_subid` int(11) default NULL,
+  `is_affymetrix` int(11) default NULL,
+  `is_mx_batchloader` int(11) default NULL,
+  `is_deleted` int(11) NOT NULL,
+  `miame_score` int(11) default NULL,
+  `in_data_warehouse` int(11) default NULL,
+  PRIMARY KEY  (`id`),
+  KEY `user_id` (`user_id`),
+  CONSTRAINT `experiments_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE RESTRICT
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `experiments`
+--
+
+LOCK TABLES `experiments` WRITE;
+/*!40000 ALTER TABLE `experiments` DISABLE KEYS */;
+/*!40000 ALTER TABLE `experiments` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `material_instances`
+--
+
+DROP TABLE IF EXISTS `material_instances`;
+CREATE TABLE `material_instances` (
+  `id` int(11) NOT NULL auto_increment,
+  `material_id` int(11) NOT NULL,
+  `experiment_id` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `material_id` (`material_id`),
+  KEY `experiment_id` (`experiment_id`),
+  CONSTRAINT `material_instances_ibfk_1` FOREIGN KEY (`material_id`) REFERENCES `materials` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `material_instances_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `material_instances`
+--
+
+LOCK TABLES `material_instances` WRITE;
+/*!40000 ALTER TABLE `material_instances` DISABLE KEYS */;
+/*!40000 ALTER TABLE `material_instances` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `materials`
+--
+
+DROP TABLE IF EXISTS `materials`;
+CREATE TABLE `materials` (
+  `id` int(11) NOT NULL auto_increment,
+  `display_label` varchar(50) default NULL,
+  `ontology_category` varchar(50) default NULL,
+  `ontology_value` varchar(50) default NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `materials`
+--
+
+LOCK TABLES `materials` WRITE;
+/*!40000 ALTER TABLE `materials` DISABLE KEYS */;
+/*!40000 ALTER TABLE `materials` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `meta`
+--
+
+DROP TABLE IF EXISTS `meta`;
+CREATE TABLE `meta` (
+  `name` varchar(128) NOT NULL default '',
+  `value` varchar(128) NOT NULL default '',
+  PRIMARY KEY  (`name`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `meta`
+--
+
+LOCK TABLES `meta` WRITE;
+/*!40000 ALTER TABLE `meta` DISABLE KEYS */;
+INSERT INTO `meta` VALUES ('serializer','Storable'),('index_subfeatures','1'),('autoindex','1');
+/*!40000 ALTER TABLE `meta` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `organism_instances`
+--
+
+DROP TABLE IF EXISTS `organism_instances`;
+CREATE TABLE `organism_instances` (
+  `id` int(11) NOT NULL auto_increment,
+  `organism_id` int(11) NOT NULL,
+  `experiment_id` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `organism_id` (`organism_id`),
+  KEY `experiment_id` (`experiment_id`),
+  CONSTRAINT `organism_instances_ibfk_1` FOREIGN KEY (`organism_id`) REFERENCES `organisms` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `organism_instances_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `organism_instances`
+--
+
+LOCK TABLES `organism_instances` WRITE;
+/*!40000 ALTER TABLE `organism_instances` DISABLE KEYS */;
+/*!40000 ALTER TABLE `organism_instances` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `organisms`
+--
+
+DROP TABLE IF EXISTS `organisms`;
+CREATE TABLE `organisms` (
+  `id` int(11) NOT NULL auto_increment,
+  `scientific_name` varchar(50) default NULL,
+  `common_name` varchar(50) default NULL,
+  `accession` int(11) default NULL,
+  `taxon_id` int(11) NOT NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `taxon_id` (`taxon_id`),
+  CONSTRAINT `organisms_ibfk_1` FOREIGN KEY (`taxon_id`) REFERENCES `taxons` (`id`) ON DELETE RESTRICT
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `organisms`
+--
+
+LOCK TABLES `organisms` WRITE;
+/*!40000 ALTER TABLE `organisms` DISABLE KEYS */;
+/*!40000 ALTER TABLE `organisms` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `permissions`
+--
+
+DROP TABLE IF EXISTS `permissions`;
+CREATE TABLE `permissions` (
+  `id` int(11) NOT NULL auto_increment,
+  `name` varchar(40) NOT NULL,
+  `info` varchar(80) default NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `permissions`
+--
+
+LOCK TABLES `permissions` WRITE;
+/*!40000 ALTER TABLE `permissions` DISABLE KEYS */;
+/*!40000 ALTER TABLE `permissions` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `permissions_roles`
+--
+
+DROP TABLE IF EXISTS `permissions_roles`;
+CREATE TABLE `permissions_roles` (
+  `role_id` int(11) NOT NULL,
+  `permission_id` int(11) NOT NULL,
+  KEY `role_id` (`role_id`),
+  KEY `permission_id` (`permission_id`),
+  CONSTRAINT `permissions_roles_ibfk_1` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `permissions_roles_ibfk_2` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `permissions_roles`
+--
+
+LOCK TABLES `permissions_roles` WRITE;
+/*!40000 ALTER TABLE `permissions_roles` DISABLE KEYS */;
+/*!40000 ALTER TABLE `permissions_roles` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `protocols`
+--
+
+DROP TABLE IF EXISTS `protocols`;
+CREATE TABLE `protocols` (
+  `id` int(11) NOT NULL auto_increment,
+  `accession` char(15) default NULL,
+  `user_accession` varchar(100) default NULL,
+  `expt_accession` char(15) default NULL,
+  `name` varchar(255) default NULL,
+  `date_last_processed` datetime default NULL,
+  `comment` text,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  UNIQUE KEY `accession` (`accession`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `protocols`
+--
+
+LOCK TABLES `protocols` WRITE;
+/*!40000 ALTER TABLE `protocols` DISABLE KEYS */;
+/*!40000 ALTER TABLE `protocols` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `roles`
+--
+
+DROP TABLE IF EXISTS `roles`;
+CREATE TABLE `roles` (
+  `id` int(11) NOT NULL auto_increment,
+  `name` varchar(40) NOT NULL,
+  `info` varchar(80) default NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `roles`
+--
+
+LOCK TABLES `roles` WRITE;
+/*!40000 ALTER TABLE `roles` DISABLE KEYS */;
+/*!40000 ALTER TABLE `roles` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `roles_users`
+--
+
+DROP TABLE IF EXISTS `roles_users`;
+CREATE TABLE `roles_users` (
+  `user_id` int(11) NOT NULL,
+  `role_id` int(11) NOT NULL,
+  KEY `user_id` (`user_id`),
+  KEY `role_id` (`role_id`),
+  CONSTRAINT `roles_users_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `roles_users_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `roles_users`
+--
+
+LOCK TABLES `roles_users` WRITE;
+/*!40000 ALTER TABLE `roles_users` DISABLE KEYS */;
+/*!40000 ALTER TABLE `roles_users` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `spreadsheets`
+--
+
+DROP TABLE IF EXISTS `spreadsheets`;
+CREATE TABLE `spreadsheets` (
+  `id` int(11) NOT NULL auto_increment,
+  `experiment_id` int(11) NOT NULL,
+  `name` varchar(255) default NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `experiment_id` (`experiment_id`),
+  CONSTRAINT `spreadsheets_ibfk_1` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE RESTRICT
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `spreadsheets`
+--
+
+LOCK TABLES `spreadsheets` WRITE;
+/*!40000 ALTER TABLE `spreadsheets` DISABLE KEYS */;
+/*!40000 ALTER TABLE `spreadsheets` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `taxons`
+--
+
+DROP TABLE IF EXISTS `taxons`;
+CREATE TABLE `taxons` (
+  `id` int(11) NOT NULL auto_increment,
+  `scientific_name` varchar(50) default NULL,
+  `common_name` varchar(50) default NULL,
+  `accession` int(11) default NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `taxons`
+--
+
+LOCK TABLES `taxons` WRITE;
+/*!40000 ALTER TABLE `taxons` DISABLE KEYS */;
+/*!40000 ALTER TABLE `taxons` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `users`
+--
+
+DROP TABLE IF EXISTS `users`;
+CREATE TABLE `users` (
+  `id` int(11) NOT NULL auto_increment,
+  `login` varchar(40) NOT NULL,
+  `name` varchar(40) default NULL,
+  `password` varchar(40) NOT NULL,
+  `email` varchar(100) default NULL,
+  `modified_at` datetime default NULL,
+  `created_at` datetime default NULL,
+  `access` datetime default NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  UNIQUE KEY `login` (`login`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `users`
+--
+
+LOCK TABLES `users` WRITE;
+/*!40000 ALTER TABLE `users` DISABLE KEYS */;
+/*!40000 ALTER TABLE `users` ENABLE KEYS */;
+UNLOCK TABLES;
+
+-- Dump completed on 2007-04-24 15:37:45
+
+
+-- #####################################################################
+-- The following are additional tables for the extended tracking system.
+-- #####################################################################
+
+--
+-- Table structure for table `array_designs_organisms`
+--
+
+DROP TABLE IF EXISTS `array_designs_organisms`;
+CREATE TABLE `array_designs_organisms` (
+  `id` int(11) NOT NULL auto_increment,
+  `organism_id` int(11) NOT NULL,
+  `array_design_id` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `organism_id` (`organism_id`),
+  KEY `array_design_id` (`array_design_id`),
+  CONSTRAINT `array_designs_organisms_ibfk_1` FOREIGN KEY (`organism_id`) REFERENCES `organisms` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `array_designs_organisms_ibfk_2` FOREIGN KEY (`array_design_id`) REFERENCES `array_designs` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `quantitation_types`
+--
+
+DROP TABLE IF EXISTS `quantitation_types`;
+CREATE TABLE `quantitation_types` (
+  `id` int(11) NOT NULL auto_increment,
+  `name` varchar(128) COLLATE latin1_general_cs NOT NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `experiments_quantitation_types`
+--
+
+DROP TABLE IF EXISTS `experiments_quantitation_types`;
+CREATE TABLE `experiments_quantitation_types` (
+  `id` int(11) NOT NULL auto_increment,
+  `quantitation_type_id` int(11) NOT NULL,
+  `experiment_id` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `quantitation_type_id` (`quantitation_type_id`),
+  KEY `experiment_id` (`experiment_id`),
+  CONSTRAINT `experiments_quantitation_types_ibfk_1` FOREIGN KEY (`quantitation_type_id`) REFERENCES `quantitation_types` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `experiments_quantitation_types_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `array_designs_experiments`
+--
+
+DROP TABLE IF EXISTS `array_designs_experiments`;
+CREATE TABLE `array_designs_experiments` (
+  `id` int(11) NOT NULL auto_increment,
+  `array_design_id` int(11) NOT NULL,
+  `experiment_id` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `array_design_id` (`array_design_id`),
+  KEY `experiment_id` (`experiment_id`),
+  CONSTRAINT `array_designs_experiments_ibfk_1` FOREIGN KEY (`array_design_id`) REFERENCES `array_designs` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `array_designs_experiments_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `factors`
+--
+
+DROP TABLE IF EXISTS `factors`;
+CREATE TABLE `factors` (
+  `id` int(11) NOT NULL auto_increment,
+  `name` varchar(128) NOT NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `experiments_factors`
+--
+
+DROP TABLE IF EXISTS `experiments_factors`;
+CREATE TABLE `experiments_factors` (
+  `id` int(11) NOT NULL auto_increment,
+  `experiment_id` int(11) NOT NULL,
+  `factor_id` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `experiment_id` (`experiment_id`),
+  KEY `factor_id` (`factor_id`),
+  CONSTRAINT `experiments_factors_ibfk_1` FOREIGN KEY (`factor_id`) REFERENCES `factors` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `experiments_factors_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `events`
+--
+
+DROP TABLE IF EXISTS `events`;
+CREATE TABLE `events` (
+  `id` int(11) NOT NULL auto_increment,
+  `array_design_id` int(11) default NULL,
+  `experiment_id` int(11) default NULL,
+  `event_type` varchar(50) NOT NULL,
+  `was_successful` int(11) default NULL,
+  `source_db` varchar(30) default NULL,
+  `target_db` varchar(30) default NULL,
+  `start_time` datetime default NULL,        -- REDUNDANT
+  `end_time` datetime default NULL,          -- REDUNDANT
+  `machine` varchar(50) default NULL,
+  `operator` varchar(30) default NULL,
+  `log_file` varchar(511) default NULL,
+  `jobregister_dbid` int(15) default NULL,
+  `comment` text,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `array_design_id` (`array_design_id`),
+  KEY `experiment_id` (`experiment_id`),
+  CONSTRAINT `events_ibfk_1` FOREIGN KEY (`array_design_id`) REFERENCES `array_designs` (`id`) ON DELETE RESTRICT,
+  CONSTRAINT `events_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE RESTRICT
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+-- #######################################
+-- Additional columns for existing tables.
+-- #######################################
+
+ALTER TABLE experiments ADD COLUMN (
+  `num_submissions` int(11) default NULL,
+  `submitter_description` text,                    -- REDUNDANT
+  `curated_name` varchar(255) default NULL,        -- REDUNDANT
+  `num_samples` int(11) default NULL,              -- REDUNDANT
+  `num_hybridizations` int(11) default NULL,       -- REDUNDANT
+  `has_raw_data` int(11) default NULL,             -- REDUNDANT
+  `has_processed_data` int(11) default NULL,       -- REDUNDANT
+  `release_date` datetime default NULL,            -- REDUNDANT
+  `is_released` int(11) default NULL,              -- REDUNDANT
+  `ae_miame_score` int(11) default NULL,           -- REDUNDANT
+  `ae_data_warehouse_score` int(11) default NULL   -- REDUNDANT
+);
+
+ALTER TABLE array_designs ADD COLUMN (
+  `annotation_source` varchar(50) default NULL,    -- REDUNDANT?
+  `annotation_version` varchar(50) default NULL,   -- REDUNDANT?
+  `biomart_table_name` varchar(50) default NULL,   -- REDUNDANT?
+  `release_date` datetime default NULL,            -- REDUNDANT
+  `is_released` int(11) default NULL               -- REDUNDANT
+);
+
+-- ###############################################################
+-- We need to relax the requirement that all organisms have taxon;
+-- otherwise auto-inserting new organisms gets ugly.
+-- ###############################################################
+ALTER TABLE organisms MODIFY COLUMN taxon_id int(11) DEFAULT NULL;
+ALTER TABLE array_designs MODIFY COLUMN accession varchar(255) DEFAULT NULL;
+ALTER TABLE experiments MODIFY COLUMN accession varchar(255) DEFAULT NULL;
+
+-- Fix our legacy submissions.
+UPDATE experiments SET num_submissions=1 where in_curation=1;
+
+-- #######################################################################
+-- The following are additional tables for the loaded data hashing system.
+-- #######################################################################
+
+--
+-- Table structure for table `data_formats`
+--
+
+DROP TABLE IF EXISTS `data_formats`;
+CREATE TABLE `data_formats` (
+  `id` int(11) NOT NULL auto_increment,
+  `name` varchar(50) COLLATE latin1_general_cs UNIQUE NOT NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `platforms`
+--
+
+DROP TABLE IF EXISTS `platforms`;
+CREATE TABLE `platforms` (
+  `id` int(11) NOT NULL auto_increment,
+  `name` varchar(50) UNIQUE NOT NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `loaded_data`
+--
+
+DROP TABLE IF EXISTS `loaded_data`;
+CREATE TABLE `loaded_data` (
+  `id` int(11) NOT NULL auto_increment,
+  `identifier` varchar(255) COLLATE latin1_general_cs NOT NULL,
+  `md5_hash` char(35) NOT NULL,
+  `data_format_id` int(11) NOT NULL,
+  `platform_id` int(11) NOT NULL,
+  `needs_metrics_calculation` int(11) NOT NULL,
+  `date_hashed` datetime,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `data_format_id` (`data_format_id`),
+  CONSTRAINT `data_format_loaded_data_ibfk_1` FOREIGN KEY (`data_format_id`) REFERENCES `data_formats` (`id`) ON DELETE RESTRICT,
+  CONSTRAINT `platform_loaded_data_ibfk_1` FOREIGN KEY (`platform_id`) REFERENCES `platforms` (`id`) ON DELETE RESTRICT
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `quality_metrics`
+--
+
+DROP TABLE IF EXISTS `quality_metrics`;
+CREATE TABLE `quality_metrics` (
+  `id` int(11) NOT NULL auto_increment,
+  `type` varchar(50) UNIQUE NOT NULL,
+  `description` text,
+  PRIMARY KEY  (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `experiments_loaded_data`
+--
+
+DROP TABLE IF EXISTS `experiments_loaded_data`;
+CREATE TABLE `experiments_loaded_data` (
+  `experiment_id` int(11) NOT NULL,
+  `loaded_data_id` int(11) NOT NULL,
+  KEY `experiment_id` (`experiment_id`),
+  KEY `loaded_data_id` (`loaded_data_id`),
+  CONSTRAINT `loaded_data_experiment_ibfk_1` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `loaded_data_loaded_data_ibfk_1` FOREIGN KEY (`loaded_data_id`) REFERENCES `loaded_data` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `loaded_data_quality_metrics`
+--
+
+DROP TABLE IF EXISTS `loaded_data_quality_metrics`;
+CREATE TABLE `loaded_data_quality_metrics` (
+  `id` int(11) NOT NULL auto_increment,
+  `value` numeric,
+  `date_calculated` datetime,
+  `quality_metric_id` int(11) NOT NULL,
+  `loaded_data_id` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `quality_metric_id` (`quality_metric_id`),
+  KEY `loaded_data_id` (`loaded_data_id`),
+  CONSTRAINT `quality_metric_quality_metric_ibfk_1` FOREIGN KEY (`quality_metric_id`) REFERENCES `quality_metrics` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `quality_metric_loaded_data_ibfk_1` FOREIGN KEY (`loaded_data_id`) REFERENCES `loaded_data` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
diff --git a/automation/admin/db/migration/migrate_2.0_to_2.1.sql b/automation/admin/db/migration/migrate_2.0_to_2.1.sql
new file mode 100644
index 0000000..46e21e1
--- /dev/null
+++ b/automation/admin/db/migration/migrate_2.0_to_2.1.sql
@@ -0,0 +1,183 @@
+-- MySQL dump 10.10
+--
+-- Host: localhost    Database: test
+-- ------------------------------------------------------
+-- Server version	5.0.27
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+-- #####################################################################
+-- The following are additional tables for the extended tracking system.
+-- #####################################################################
+
+--
+-- Table structure for table `array_designs_organisms`
+--
+
+DROP TABLE IF EXISTS `array_designs_organisms`;
+CREATE TABLE `array_designs_organisms` (
+  `id` int(11) NOT NULL auto_increment,
+  `organism_id` int(11) NOT NULL,
+  `array_design_id` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `organism_id` (`organism_id`),
+  KEY `array_design_id` (`array_design_id`),
+  CONSTRAINT `array_designs_organisms_ibfk_1` FOREIGN KEY (`organism_id`) REFERENCES `organisms` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `array_designs_organisms_ibfk_2` FOREIGN KEY (`array_design_id`) REFERENCES `array_designs` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `quantitation_types`
+--
+
+DROP TABLE IF EXISTS `quantitation_types`;
+CREATE TABLE `quantitation_types` (
+  `id` int(11) NOT NULL auto_increment,
+  `name` varchar(128) NOT NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `experiments_quantitation_types`
+--
+
+DROP TABLE IF EXISTS `experiments_quantitation_types`;
+CREATE TABLE `experiments_quantitation_types` (
+  `id` int(11) NOT NULL auto_increment,
+  `quantitation_type_id` int(11) NOT NULL,
+  `experiment_id` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `quantitation_type_id` (`quantitation_type_id`),
+  KEY `experiment_id` (`experiment_id`),
+  CONSTRAINT `experiments_quantitation_types_ibfk_1` FOREIGN KEY (`quantitation_type_id`) REFERENCES `quantitation_types` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `experiments_quantitation_types_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `array_designs_experiments`
+--
+
+DROP TABLE IF EXISTS `array_designs_experiments`;
+CREATE TABLE `array_designs_experiments` (
+  `id` int(11) NOT NULL auto_increment,
+  `array_design_id` int(11) NOT NULL,
+  `experiment_id` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `array_design_id` (`array_design_id`),
+  KEY `experiment_id` (`experiment_id`),
+  CONSTRAINT `array_designs_experiments_ibfk_1` FOREIGN KEY (`array_design_id`) REFERENCES `array_designs` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `array_designs_experiments_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `factors`
+--
+
+DROP TABLE IF EXISTS `factors`;
+CREATE TABLE `factors` (
+  `id` int(11) NOT NULL auto_increment,
+  `name` varchar(128) NOT NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `experiments_factors`
+--
+
+DROP TABLE IF EXISTS `experiments_factors`;
+CREATE TABLE `experiments_factors` (
+  `id` int(11) NOT NULL auto_increment,
+  `experiment_id` int(11) NOT NULL,
+  `factor_id` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `experiment_id` (`experiment_id`),
+  KEY `factor_id` (`factor_id`),
+  CONSTRAINT `experiments_factors_ibfk_1` FOREIGN KEY (`factor_id`) REFERENCES `factors` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `experiments_factors_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `events`
+--
+
+DROP TABLE IF EXISTS `events`;
+CREATE TABLE `events` (
+  `id` int(11) NOT NULL auto_increment,
+  `array_design_id` int(11) default NULL,
+  `experiment_id` int(11) default NULL,
+  `event_type` varchar(50) NOT NULL,
+  `was_successful` int(11) default NULL,
+  `source_db` varchar(30) default NULL,
+  `target_db` varchar(30) default NULL,
+  `start_time` datetime default NULL,        -- REDUNDANT
+  `end_time` datetime default NULL,          -- REDUNDANT
+  `machine` varchar(50) default NULL,
+  `operator` varchar(30) default NULL,
+  `log_file` varchar(511) default NULL,
+  `jobregister_dbid` int(15) default NULL,
+  `comment` text,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `array_design_id` (`array_design_id`),
+  KEY `experiment_id` (`experiment_id`),
+  CONSTRAINT `events_ibfk_1` FOREIGN KEY (`array_design_id`) REFERENCES `array_designs` (`id`) ON DELETE RESTRICT,
+  CONSTRAINT `events_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE RESTRICT
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+-- #######################################
+-- Additional columns for existing tables.
+-- #######################################
+
+ALTER TABLE experiments ADD COLUMN (
+  `num_submissions` int(11) default NULL,
+  `submitter_description` text,                    -- REDUNDANT
+  `curated_name` varchar(255) default NULL,        -- REDUNDANT
+  `num_samples` int(11) default NULL,              -- REDUNDANT
+  `num_hybridizations` int(11) default NULL,       -- REDUNDANT
+  `has_raw_data` int(11) default NULL,             -- REDUNDANT
+  `has_processed_data` int(11) default NULL,       -- REDUNDANT
+  `release_date` datetime default NULL,            -- REDUNDANT
+  `is_released` int(11) default NULL,              -- REDUNDANT
+  `ae_miame_score` int(11) default NULL,           -- REDUNDANT
+  `ae_data_warehouse_score` int(11) default NULL   -- REDUNDANT
+);
+
+ALTER TABLE array_designs ADD COLUMN (
+  `annotation_source` varchar(50) default NULL,    -- REDUNDANT?
+  `annotation_version` varchar(50) default NULL,   -- REDUNDANT?
+  `biomart_table_name` varchar(50) default NULL,   -- REDUNDANT?
+  `release_date` datetime default NULL,            -- REDUNDANT
+  `is_released` int(11) default NULL               -- REDUNDANT
+);
+
+-- ###############################################################
+-- We need to relax the requirement that all organisms have taxon;
+-- otherwise auto-inserting new organisms gets ugly.
+-- ###############################################################
+ALTER TABLE organisms MODIFY COLUMN taxon_id int(11) DEFAULT NULL;
+ALTER TABLE array_designs MODIFY COLUMN accession varchar(255) DEFAULT NULL;
+ALTER TABLE experiments MODIFY COLUMN accession varchar(255) DEFAULT NULL;
+
+-- Fix our legacy submissions.
+UPDATE experiments SET num_submissions=1 where in_curation=1;
+
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
diff --git a/automation/admin/db/migration/migrate_2.1_to_2.2.sql b/automation/admin/db/migration/migrate_2.1_to_2.2.sql
new file mode 100644
index 0000000..795a3ba
--- /dev/null
+++ b/automation/admin/db/migration/migrate_2.1_to_2.2.sql
@@ -0,0 +1,125 @@
+-- MySQL dump 10.10
+--
+-- Host: localhost    Database: test
+-- ------------------------------------------------------
+-- Server version	5.0.27
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+-- #######################################################################
+-- The following are additional tables for the loaded data hashing system.
+-- #######################################################################
+
+--
+-- Table structure for table `data_formats`
+--
+
+DROP TABLE IF EXISTS `data_formats`;
+CREATE TABLE `data_formats` (
+  `id` int(11) NOT NULL auto_increment,
+  `name` varchar(50) COLLATE latin1_general_cs UNIQUE NOT NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `loaded_data`
+--
+
+DROP TABLE IF EXISTS `loaded_data`;
+CREATE TABLE `loaded_data` (
+  `id` int(11) NOT NULL auto_increment,
+  `identifier` varchar(255) COLLATE latin1_general_cs NOT NULL,
+  `md5_hash` char(35) NOT NULL,
+  `data_format_id` int(11) NOT NULL,
+  `is_deleted` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `data_format_id` (`data_format_id`),
+  CONSTRAINT `data_format_loaded_data_ibfk_1` FOREIGN KEY (`data_format_id`) REFERENCES `data_formats` (`id`) ON DELETE RESTRICT
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `quality_metrics`
+--
+
+DROP TABLE IF EXISTS `quality_metrics`;
+CREATE TABLE `quality_metrics` (
+  `id` int(11) NOT NULL auto_increment,
+  `type` varchar(50) UNIQUE NOT NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `experiments_loaded_data`
+--
+
+DROP TABLE IF EXISTS `experiments_loaded_data`;
+CREATE TABLE `experiments_loaded_data` (
+  `experiment_id` int(11) NOT NULL,
+  `loaded_data_id` int(11) NOT NULL,
+  KEY `experiment_id` (`experiment_id`),
+  KEY `loaded_data_id` (`loaded_data_id`),
+  CONSTRAINT `loaded_data_experiment_ibfk_1` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `loaded_data_loaded_data_ibfk_1` FOREIGN KEY (`loaded_data_id`) REFERENCES `loaded_data` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `loaded_data_quality_metrics`
+--
+
+DROP TABLE IF EXISTS `loaded_data_quality_metrics`;
+CREATE TABLE `loaded_data_quality_metrics` (
+  `id` int(11) NOT NULL auto_increment,
+  `value` decimal(12,5),
+  `quality_metric_id` int(11) NOT NULL,
+  `loaded_data_id` int(11) NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `quality_metric_id` (`quality_metric_id`),
+  KEY `loaded_data_id` (`loaded_data_id`),
+  CONSTRAINT `quality_metric_quality_metric_ibfk_1` FOREIGN KEY (`quality_metric_id`) REFERENCES `quality_metrics` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `quality_metric_loaded_data_ibfk_1` FOREIGN KEY (`loaded_data_id`) REFERENCES `loaded_data` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Fix the matching of QT so that it's case sensitive
+--
+ALTER TABLE `quantitation_types` MODIFY COLUMN `name` varchar(128) COLLATE latin1_general_cs NOT NULL;
+
+--
+-- Table structure for table `platforms`
+--
+
+CREATE TABLE `platforms` (
+  `id` int(11) NOT NULL auto_increment,
+  `name` varchar(50) UNIQUE NOT NULL,
+  PRIMARY KEY  (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Other additions noted during testing.
+--
+
+alter table loaded_data add column platform_id int(11);
+alter table loaded_data add column needs_metrics_calculation int(11) not null;
+alter table loaded_data add column date_hashed datetime;
+alter table loaded_data add constraint `platform_loaded_data_ibfk_1` FOREIGN KEY (`platform_id`) REFERENCES `platforms` (`id`) ON DELETE RESTRICT;
+alter table quality_metrics add column description text;
+alter table loaded_data_quality_metrics add column date_calculated datetime;
+
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
diff --git a/automation/admin/db/migration/migrate_csv2sqlite.pl b/automation/admin/db/migration/migrate_csv2sqlite.pl
new file mode 100755
index 0000000..5448d03
--- /dev/null
+++ b/automation/admin/db/migration/migrate_csv2sqlite.pl
@@ -0,0 +1,190 @@
+#!/usr/bin/env perl
+
+# Script used to migrate 1.8 series DBD::CSV accession cache to 1.9
+# series DBD::SQLite autosubmissions database. Note that the files in
+# question must have Unix, not DOS linebreaks.
+
+# Tim Rayner 2006 ArrayExpress Team, EBI
+# $Id: migrate_csv2sqlite.pl 1862 2007-12-27 16:30:18Z tfrayner $
+
+use strict;
+use warnings;
+
+##############################################
+package AccessionCacheDB;
+use base 'Class::DBI';
+
+unless (@ARGV) {
+    warn <<USAGE;
+    Usage: $0 <DBD::CSV database directory> [ Tab2MAGE experiment web page (optional) ]
+
+USAGE
+    exit 255;
+}
+
+my $csvdir = shift @ARGV;
+__PACKAGE__->connection(
+    "dbi:CSV:f_dir=$csvdir",
+    undef,
+    undef,
+    {csv_sep_char => "\t",
+     csv_eol      => "\n",
+     RaiseError   => 1,}
+) or die();
+
+##############################################
+package AccessionCacheDB::Experiment;
+use base 'AccessionCacheDB';
+__PACKAGE__->table('mx_experiment');
+__PACKAGE__->columns(
+    All => qw(
+	      subid
+	      accession
+	      name
+	      user
+	      checker_score
+	      software
+	      status
+	      date_last_processed
+	      comment
+	      curator
+	      data_warehouse_ready
+	  )
+);
+
+##############################################
+package AccessionCacheDB::Array;
+use base 'AccessionCacheDB';
+__PACKAGE__->table('mx_array');
+__PACKAGE__->columns(
+    All => qw(
+	      subid
+	      accession
+	      name
+	      user
+	      status
+	      date_last_processed
+	      comment
+	      data_warehouse_ready
+	  )
+);
+
+##############################################
+package AccessionCacheDB::Protocol;
+use base 'AccessionCacheDB';
+__PACKAGE__->table('t2m_protocol');
+__PACKAGE__->columns(
+    All => qw(
+	      accession
+	      user_accession
+	      expt_accession
+	      name
+	  )
+);
+
+##############################################
+package main;
+require ArrayExpress::AutoSubmission::DB::Experiment;
+require ArrayExpress::AutoSubmission::DB::ArrayDesign;
+require ArrayExpress::AutoSubmission::DB::Protocol;
+use ArrayExpress::Curator::Common qw(date_now);
+
+my $date = date_now();
+
+print STDERR ("Loading MIAMExpress experiments...\n");
+foreach my $expt ( AccessionCacheDB::Experiment->retrieve_all() ) {
+    ArrayExpress::AutoSubmission::DB::Experiment->insert({
+	experiment_type => 'MIAMExpress',
+        accession       => $expt->accession,
+        name            => $expt->name,
+        checker_score   => $expt->checker_score,
+        software        => $expt->software,
+        status          => $expt->status,
+        data_warehouse_ready => $expt->data_warehouse_ready,
+        date_last_processed  => $expt->date_last_processed,
+        curator              => $expt->curator,
+        comment              => $expt->comment,
+        miamexpress_login    => $expt->user,
+        miamexpress_subid    => $expt->subid,
+	is_deleted           => 0,
+    });
+    print STDERR q{.};
+}
+print STDERR ("\n");
+
+print STDERR ("Loading MIAMExpress arrays...\n");
+foreach my $array ( AccessionCacheDB::Array->retrieve_all() ) {
+    ArrayExpress::AutoSubmission::DB::ArrayDesign->insert({
+        miamexpress_subid     => $array->subid,
+        accession             => $array->accession,
+        name                  => $array->name,
+        miamexpress_login     => $array->user,
+        status                => $array->status,
+        data_warehouse_ready  => $array->data_warehouse_ready,
+        date_last_processed   => $array->date_last_processed,
+        comment               => $array->comment,
+	is_deleted            => 0,
+    });
+    print STDERR q{.};
+}
+print STDERR ("\n");
+
+print STDERR ("Loading Tab2MAGE protocols...\n");
+foreach my $protocol ( AccessionCacheDB::Protocol->retrieve_all() ) {
+    ArrayExpress::AutoSubmission::DB::Protocol->insert({
+        accession           => $protocol->accession,
+        user_accession      => $protocol->user_accession,
+        expt_accession      => $protocol->expt_accession,
+        name                => $protocol->name,
+        date_last_processed => $date,
+	comment             => 'from old system',
+        is_deleted          => 0,	
+    });
+    print STDERR q{.};
+}
+print STDERR ("\n");
+
+# If possible, parse an optional web page with tab2mage experiment info.
+if ( my $t2m_webpage = shift @ARGV ) {
+
+    print STDERR ("Loading Tab2MAGE experiments...\n");
+
+    require LWP::UserAgent;
+    require HTML::TableExtract;
+
+    my $ua = LWP::UserAgent->new();
+
+    my $response = $ua->get( $t2m_webpage );
+
+    unless ( $response->is_success ) {
+	croak(    "Error retrieving tab2mage experiments: "
+		      . $response->status_line );
+    }
+
+    my $te = new HTML::TableExtract(
+	headers => [
+	    'Accession',
+	    'Person',
+	    'ExperimentDesignType',
+	    'Experiment Name'
+	]
+    );
+    $te->parse( $response->content );
+
+    # Go for the first and only table.
+    foreach my $row ($te->rows) {
+	if ( $row->[0] =~ /E-TABM-\d+/ ) {
+	    ArrayExpress::AutoSubmission::DB::Experiment->insert({
+		experiment_type => 'Tab2MAGE',
+		accession       => $row->[0],
+		name            => $row->[3],
+		status          => 'Complete',
+		date_last_processed  => $date,
+		comment         => "submitter: $row->[1]; design: $row->[2]",
+		is_deleted      => 0,
+	    });
+	    print STDERR q{.};
+	}
+    }
+}
+print STDERR ("\n");
diff --git a/automation/admin/db/migration/migrate_sqlite2mysql.pl b/automation/admin/db/migration/migrate_sqlite2mysql.pl
new file mode 100755
index 0000000..cff195e
--- /dev/null
+++ b/automation/admin/db/migration/migrate_sqlite2mysql.pl
@@ -0,0 +1,64 @@
+#!/usr/bin/env perl
+#
+# Very basic script to take the SQLite dump from the old autosubs
+# database and convert it into something MySQL can load. The MySQL
+# output assumes that the tables have already been created using the
+# SQL in the automation/admin/db directory. This is a one-off job,
+# provided here as a convenience.
+#
+# $Id: migrate_sqlite2mysql.pl 1862 2007-12-27 16:30:18Z tfrayner $
+
+use strict;
+use warnings;
+
+my $sqlite_file = shift;
+
+unless ($sqlite_file) {
+    die ("Usage: $0 sqlite_sql_dump_file.txt > mysql_sql.txt\n");
+}
+
+open(my $sqlite, '<', $sqlite_file) or die($!);
+
+# Split on SQL statements, each terminated with a semicolon. Note that
+# we're assuming Unix line-endings here.
+local $/ = ";\n";
+
+my $create_table = qr/\A [ ]* CREATE [ ]+ TABLE [ ]+ `?(\w+)`?/xms;
+my $insert_into  = qr/\A [ ]* INSERT [ ]+ INTO/xms;
+
+my %schema;
+CHUNK:
+while ( my $chunk = <$sqlite> ) {
+
+    next CHUNK if ( $chunk =~ /sqlite_sequence/i );
+
+    if ( my ($table) = ($chunk =~ /$create_table/i) ) {
+	$chunk =~ s/$create_table [ ]+ \(\n//xms;
+	$chunk =~ s/\);\n \z//xms;
+	chomp $chunk;
+	my @cols = map { ($_ =~ /\A [ ]* (\w+)/xms) } split /,[\n ]*/, $chunk;
+	$schema{$table} = \@cols;
+    }
+
+    if ( $chunk =~ /$insert_into/i ) {
+	$chunk =~ s/"/`/g;
+	if ( my ($table) = ($chunk =~ /$insert_into [ ]+ `([^`]+)`/ixms) ) {
+
+	    unless ( $schema{$table} ) {
+		die(qq{Error: Table "$table" has undefined schema.\n});
+	    }
+
+	    my $columnlist = q{(} . join(',', map {"`$_`"} @{ $schema{$table} }) . q{)};
+
+	    $chunk =~ s/($insert_into [ ]+ `?${table}`?) [ ]+ VALUES/$1 $columnlist VALUES/ixms
+		or die("Error: can't substitute into chunk:\n\n$chunk\n");
+
+	    print $chunk;
+	}
+	else {
+	    die("Error: can't detect table name in chunk:\n\n$chunk\n");
+	}
+    }
+}
+
+close($sqlite);
diff --git a/automation/admin/db/schema.pdf b/automation/admin/db/schema.pdf
new file mode 100644
index 0000000..cc123ac
Binary files /dev/null and b/automation/admin/db/schema.pdf differ
diff --git a/automation/admin/db/schema.rb b/automation/admin/db/schema.rb
new file mode 100644
index 0000000..24eccb1
--- /dev/null
+++ b/automation/admin/db/schema.rb
@@ -0,0 +1,347 @@
+# This file is auto-generated from the current state of the database. Instead of editing this file, 
+# please use the migrations feature of ActiveRecord to incrementally modify your database, and
+# then regenerate this schema definition.
+#
+# Note that this schema.rb definition is the authoritative source for your database schema. If you need
+# to create the application database on another system, you should be using db:schema:load, not running
+# all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
+# you'll amass, the slower it'll run and the greater likelihood for issues).
+#
+# It's strongly recommended to check this file into your version control system.
+
+ActiveRecord::Schema.define(:version => 0) do
+
+  create_table "array_designs", :force => true do |t|
+    t.integer  "miamexpress_subid"
+    t.string   "accession"
+    t.string   "name"
+    t.string   "miamexpress_login",    :limit => 50
+    t.string   "status",               :limit => 50
+    t.string   "data_warehouse_ready", :limit => 15
+    t.datetime "date_last_processed"
+    t.text     "comment"
+    t.integer  "is_deleted",                         :null => false
+    t.integer  "miame_score"
+    t.integer  "in_data_warehouse"
+    t.string   "annotation_source",    :limit => 50
+    t.string   "annotation_version",   :limit => 50
+    t.string   "biomart_table_name",   :limit => 50
+    t.datetime "release_date"
+    t.integer  "is_released"
+  end
+
+  add_index "array_designs", ["miamexpress_subid"], :name => "miamexpress_subid", :unique => true
+
+  create_table "array_designs_experiments", :force => true do |t|
+    t.integer "array_design_id", :null => false
+    t.integer "experiment_id",   :null => false
+  end
+
+  add_index "array_designs_experiments", ["array_design_id"], :name => "array_design_id"
+  add_index "array_designs_experiments", ["experiment_id"], :name => "experiment_id"
+
+  create_table "array_designs_organisms", :force => true do |t|
+    t.integer "organism_id",     :null => false
+    t.integer "array_design_id", :null => false
+  end
+
+  add_index "array_designs_organisms", ["organism_id"], :name => "organism_id"
+  add_index "array_designs_organisms", ["array_design_id"], :name => "array_design_id"
+
+  create_table "categories", :force => true do |t|
+    t.string  "ontology_term", :limit => 50
+    t.string  "display_label", :limit => 50
+    t.integer "is_common"
+    t.integer "is_bmc"
+    t.integer "is_fv"
+    t.integer "is_deleted",                  :null => false
+  end
+
+  create_table "categories_designs", :id => false, :force => true do |t|
+    t.integer "category_id", :null => false
+    t.integer "design_id",   :null => false
+  end
+
+  add_index "categories_designs", ["category_id"], :name => "category_id"
+  add_index "categories_designs", ["design_id"], :name => "design_id"
+
+  create_table "categories_materials", :id => false, :force => true do |t|
+    t.integer "category_id", :null => false
+    t.integer "material_id", :null => false
+  end
+
+  add_index "categories_materials", ["category_id"], :name => "category_id"
+  add_index "categories_materials", ["material_id"], :name => "material_id"
+
+  create_table "categories_taxons", :id => false, :force => true do |t|
+    t.integer "category_id", :null => false
+    t.integer "taxon_id",    :null => false
+  end
+
+  add_index "categories_taxons", ["category_id"], :name => "category_id"
+  add_index "categories_taxons", ["taxon_id"], :name => "taxon_id"
+
+  create_table "data_files", :force => true do |t|
+    t.integer "experiment_id", :null => false
+    t.string  "name"
+    t.integer "is_unpacked"
+    t.integer "is_deleted",    :null => false
+  end
+
+  add_index "data_files", ["experiment_id"], :name => "experiment_id"
+
+  create_table "data_formats", :force => true do |t|
+    t.string "name", :limit => 50, :default => "", :null => false
+  end
+
+  add_index "data_formats", ["name"], :name => "name", :unique => true
+
+  create_table "design_instances", :force => true do |t|
+    t.integer "design_id",     :null => false
+    t.integer "experiment_id", :null => false
+  end
+
+  add_index "design_instances", ["design_id"], :name => "design_id"
+  add_index "design_instances", ["experiment_id"], :name => "experiment_id"
+
+  create_table "designs", :force => true do |t|
+    t.string  "display_label",     :limit => 50
+    t.string  "ontology_category", :limit => 50
+    t.string  "ontology_value",    :limit => 50
+    t.string  "design_type",       :limit => 15, :default => "", :null => false
+    t.integer "is_deleted",                                      :null => false
+  end
+
+  create_table "events", :force => true do |t|
+    t.integer  "array_design_id"
+    t.integer  "experiment_id"
+    t.string   "event_type",       :limit => 50,  :default => "", :null => false
+    t.integer  "was_successful"
+    t.string   "source_db",        :limit => 30
+    t.string   "target_db",        :limit => 30
+    t.datetime "start_time"
+    t.datetime "end_time"
+    t.string   "machine",          :limit => 50
+    t.string   "operator",         :limit => 30
+    t.string   "log_file",         :limit => 511
+    t.integer  "jobregister_dbid", :limit => 15
+    t.text     "comment"
+    t.integer  "is_deleted",                                      :null => false
+  end
+
+  add_index "events", ["array_design_id"], :name => "array_design_id"
+  add_index "events", ["experiment_id"], :name => "experiment_id"
+
+  create_table "experiments", :force => true do |t|
+    t.string   "accession"
+    t.string   "name"
+    t.integer  "user_id"
+    t.integer  "checker_score"
+    t.string   "software",                :limit => 100
+    t.string   "status",                  :limit => 50
+    t.integer  "data_warehouse_ready"
+    t.datetime "date_last_edited"
+    t.datetime "date_submitted"
+    t.datetime "date_last_processed"
+    t.integer  "in_curation"
+    t.string   "curator",                 :limit => 30
+    t.text     "comment"
+    t.string   "experiment_type",         :limit => 30
+    t.string   "miamexpress_login",       :limit => 30
+    t.integer  "miamexpress_subid"
+    t.integer  "is_affymetrix"
+    t.integer  "is_mx_batchloader"
+    t.integer  "is_deleted",                             :null => false
+    t.integer  "miame_score"
+    t.integer  "in_data_warehouse"
+    t.integer  "num_submissions"
+    t.text     "submitter_description"
+    t.string   "curated_name"
+    t.integer  "num_samples"
+    t.integer  "num_hybridizations"
+    t.integer  "has_raw_data"
+    t.integer  "has_processed_data"
+    t.datetime "release_date"
+    t.integer  "is_released"
+    t.integer  "ae_miame_score"
+    t.integer  "ae_data_warehouse_score"
+  end
+
+  add_index "experiments", ["user_id"], :name => "user_id"
+
+  create_table "experiments_factors", :force => true do |t|
+    t.integer "experiment_id", :null => false
+    t.integer "factor_id",     :null => false
+  end
+
+  add_index "experiments_factors", ["experiment_id"], :name => "experiment_id"
+  add_index "experiments_factors", ["factor_id"], :name => "factor_id"
+
+  create_table "experiments_loaded_data", :id => false, :force => true do |t|
+    t.integer "experiment_id",  :null => false
+    t.integer "loaded_data_id", :null => false
+  end
+
+  add_index "experiments_loaded_data", ["experiment_id"], :name => "experiment_id"
+  add_index "experiments_loaded_data", ["loaded_data_id"], :name => "loaded_data_id"
+
+  create_table "experiments_quantitation_types", :force => true do |t|
+    t.integer "quantitation_type_id", :null => false
+    t.integer "experiment_id",        :null => false
+  end
+
+  add_index "experiments_quantitation_types", ["quantitation_type_id"], :name => "quantitation_type_id"
+  add_index "experiments_quantitation_types", ["experiment_id"], :name => "experiment_id"
+
+  create_table "factors", :force => true do |t|
+    t.string  "name",       :limit => 128, :default => "", :null => false
+    t.integer "is_deleted",                                :null => false
+  end
+
+  create_table "loaded_data", :force => true do |t|
+    t.string   "identifier",                              :default => "", :null => false
+    t.string   "md5_hash",                  :limit => 35, :default => "", :null => false
+    t.integer  "data_format_id",                                          :null => false
+    t.integer  "is_deleted",                                              :null => false
+    t.integer  "platform_id"
+    t.integer  "needs_metrics_calculation",                               :null => false
+    t.datetime "date_hashed"
+  end
+
+  add_index "loaded_data", ["data_format_id"], :name => "data_format_id"
+  add_index "loaded_data", ["platform_id"], :name => "platform_loaded_data_ibfk_1"
+
+  create_table "loaded_data_quality_metrics", :force => true do |t|
+    t.decimal  "value",             :precision => 12, :scale => 5
+    t.integer  "quality_metric_id",                                :null => false
+    t.integer  "loaded_data_id",                                   :null => false
+    t.datetime "date_calculated"
+  end
+
+  add_index "loaded_data_quality_metrics", ["quality_metric_id"], :name => "quality_metric_id"
+  add_index "loaded_data_quality_metrics", ["loaded_data_id"], :name => "loaded_data_id"
+
+  create_table "material_instances", :force => true do |t|
+    t.integer "material_id",   :null => false
+    t.integer "experiment_id", :null => false
+  end
+
+  add_index "material_instances", ["material_id"], :name => "material_id"
+  add_index "material_instances", ["experiment_id"], :name => "experiment_id"
+
+  create_table "materials", :force => true do |t|
+    t.string  "display_label",     :limit => 50
+    t.string  "ontology_category", :limit => 50
+    t.string  "ontology_value",    :limit => 50
+    t.integer "is_deleted",                      :null => false
+  end
+
+  create_table "meta", :primary_key => "name", :force => true do |t|
+    t.string "value", :limit => 128, :default => "", :null => false
+  end
+
+  create_table "organism_instances", :force => true do |t|
+    t.integer "organism_id",   :null => false
+    t.integer "experiment_id", :null => false
+  end
+
+  add_index "organism_instances", ["organism_id"], :name => "organism_id"
+  add_index "organism_instances", ["experiment_id"], :name => "experiment_id"
+
+  create_table "organisms", :force => true do |t|
+    t.string  "scientific_name", :limit => 50
+    t.string  "common_name",     :limit => 50
+    t.integer "accession"
+    t.integer "taxon_id"
+    t.integer "is_deleted",                    :null => false
+  end
+
+  add_index "organisms", ["taxon_id"], :name => "taxon_id"
+
+  create_table "permissions", :force => true do |t|
+    t.string  "name",       :limit => 40, :default => "", :null => false
+    t.string  "info",       :limit => 80
+    t.integer "is_deleted",                               :null => false
+  end
+
+  create_table "permissions_roles", :id => false, :force => true do |t|
+    t.integer "role_id",       :null => false
+    t.integer "permission_id", :null => false
+  end
+
+  add_index "permissions_roles", ["role_id"], :name => "role_id"
+  add_index "permissions_roles", ["permission_id"], :name => "permission_id"
+
+  create_table "platforms", :force => true do |t|
+    t.string "name", :limit => 50, :default => "", :null => false
+  end
+
+  add_index "platforms", ["name"], :name => "name", :unique => true
+
+  create_table "protocols", :force => true do |t|
+    t.string   "accession",           :limit => 15
+    t.string   "user_accession",      :limit => 100
+    t.string   "expt_accession",      :limit => 15
+    t.string   "name"
+    t.datetime "date_last_processed"
+    t.text     "comment"
+    t.integer  "is_deleted",                         :null => false
+  end
+
+  add_index "protocols", ["accession"], :name => "accession", :unique => true
+
+  create_table "quality_metrics", :force => true do |t|
+    t.string "type",        :limit => 50, :default => "", :null => false
+    t.text   "description"
+  end
+
+  add_index "quality_metrics", ["type"], :name => "type", :unique => true
+
+  create_table "quantitation_types", :force => true do |t|
+    t.string  "name",       :limit => 128, :default => "", :null => false
+    t.integer "is_deleted",                                :null => false
+  end
+
+  create_table "roles", :force => true do |t|
+    t.string  "name",       :limit => 40, :default => "", :null => false
+    t.string  "info",       :limit => 80
+    t.integer "is_deleted",                               :null => false
+  end
+
+  create_table "roles_users", :id => false, :force => true do |t|
+    t.integer "user_id", :null => false
+    t.integer "role_id", :null => false
+  end
+
+  add_index "roles_users", ["user_id"], :name => "user_id"
+  add_index "roles_users", ["role_id"], :name => "role_id"
+
+  create_table "spreadsheets", :force => true do |t|
+    t.integer "experiment_id", :null => false
+    t.string  "name"
+    t.integer "is_deleted",    :null => false
+  end
+
+  add_index "spreadsheets", ["experiment_id"], :name => "experiment_id"
+
+  create_table "taxons", :force => true do |t|
+    t.string  "scientific_name", :limit => 50
+    t.string  "common_name",     :limit => 50
+    t.integer "accession"
+    t.integer "is_deleted",                    :null => false
+  end
+
+  create_table "users", :force => true do |t|
+    t.string   "login",       :limit => 40,  :default => "", :null => false
+    t.string   "name",        :limit => 40
+    t.string   "password",    :limit => 40,  :default => "", :null => false
+    t.string   "email",       :limit => 100
+    t.datetime "modified_at"
+    t.datetime "created_at"
+    t.datetime "access"
+    t.integer  "is_deleted",                                 :null => false
+  end
+
+  add_index "users", ["login"], :name => "login", :unique => true
+
+end
diff --git a/automation/admin/doc/README_FOR_APP b/automation/admin/doc/README_FOR_APP
new file mode 100644
index 0000000..ac6c149
--- /dev/null
+++ b/automation/admin/doc/README_FOR_APP
@@ -0,0 +1,2 @@
+Use this README file to introduce your application and point to useful places in the API for learning more.
+Run "rake appdoc" to generate API documentation for your models and controllers.
\ No newline at end of file
diff --git a/automation/admin/lib/acl_system.rb b/automation/admin/lib/acl_system.rb
new file mode 100644
index 0000000..006aa0e
--- /dev/null
+++ b/automation/admin/lib/acl_system.rb
@@ -0,0 +1,31 @@
+# See <a href="http://wiki.rubyonrails.com/rails/show/LoginGeneratorACLSystem">http://wiki.rubyonrails.com/rails/show/LoginGeneratorACLSystem</a>
+
+module ACLSystem
+  include LoginSystem
+
+  # This module wires itself into the LoginSystem authorize? method.  You
+  # should use the normal:
+  #
+  #   before_filter :login_required
+  #
+  # or to leave some actions unprotected:
+  #
+  #   before_filter :login_required, :except => [ :list, :show ]
+  #
+
+  protected
+
+  # Authorizes the user for an action.
+  # This works in conjunction with the LoginController.
+  # The LoginController loads the User object.
+  def authorize?(user)
+    required_perm = "%s/%s" % [ params[:controller], params[:action] ]
+
+    if user.authorized? required_perm
+      return true
+    end
+
+    return false
+  end
+end
+
diff --git a/automation/admin/lib/annotation.rb b/automation/admin/lib/annotation.rb
new file mode 100644
index 0000000..e53365e
--- /dev/null
+++ b/automation/admin/lib/annotation.rb
@@ -0,0 +1,96 @@
+module Annotation
+
+  def annotate(annotation)
+
+    if annotation
+      self.map_to_designs(annotation[:design_ids])
+      self.map_to_materials(annotation[:material_ids])
+      self.map_to_organisms(annotation[:organism_ids])
+    else
+
+      # empty annotation, so pass empty arrays to the mapping methods
+      self.map_to_designs([])
+      self.map_to_materials([])
+      self.map_to_organisms([])
+    end
+
+    true # I.e, nothing failed (FIXME to actually test this)...
+
+  end
+
+  def map_to_designs(new_design_ids)
+
+    if new_design_ids && new_design_ids.any?
+
+      # Remove unwanted designs
+      self.design_instances.each do |old|
+	unless new_design_ids.include?(old.design_id)
+	  old.destroy
+	end
+      end
+
+      # Add new designs
+      new_design_ids.each do |newid|
+	unless self.design_instances.find(:first, :conditions => "design_id = " + newid.to_s)	  
+	  design = Design.find(newid)
+	  self.design_instances.create(:design => design)
+	end
+      end
+
+    else
+      self.design_instances.destroy_all
+    end
+    
+  end
+
+  def map_to_materials(new_material_ids)
+
+    if new_material_ids && new_material_ids.any?
+
+      # Remove unwanted materials
+      self.material_instances.each do |old|
+	unless new_material_ids.include?(old.material_id)
+	  old.destroy
+	end
+      end
+
+      # Add new materials
+      new_material_ids.each do |newid|
+	unless self.material_instances.find(:first, :conditions => "material_id = " + newid.to_s)	  
+	  material = Material.find(newid)
+	  self.material_instances.create(:material => material)
+	end
+      end
+
+    else
+      self.material_instances.destroy_all
+    end
+    
+  end
+
+  def map_to_organisms(new_organism_ids)
+
+    if new_organism_ids && new_organism_ids.any?
+
+      # Remove unwanted organisms
+      self.organism_instances.each do |old|
+	unless new_organism_ids.include?(old.organism_id)
+	  old.destroy
+	end
+      end
+
+      # Add new organisms
+      new_organism_ids.each do |newid|
+	unless self.organism_instances.find(:first, :conditions => "organism_id = " + newid.to_s)	  
+	  organism = Organism.find(newid)
+	  self.organism_instances.create(:organism => organism)
+	end
+      end
+
+    else
+      self.organism_instances.destroy_all
+    end
+    
+  end
+
+end
diff --git a/automation/admin/lib/login_system.rb b/automation/admin/lib/login_system.rb
new file mode 100644
index 0000000..2a9243c
--- /dev/null
+++ b/automation/admin/lib/login_system.rb
@@ -0,0 +1,87 @@
+require_dependency "user"
+
+module LoginSystem 
+  
+  protected
+  
+  # overwrite this if you want to restrict access to only a few actions
+  # or if you want to check if the user has the correct rights  
+  # example:
+  #
+  #  # only allow nonbobs
+  #  def authorize?(user)
+  #    user.login != "bob"
+  #  end
+  def authorize?(user)
+     true
+  end
+  
+  # overwrite this method if you only want to protect certain actions of the controller
+  # example:
+  # 
+  #  # don't protect the login and the about method
+  #  def protect?(action)
+  #    if ['action', 'about'].include?(action)
+  #       return false
+  #    else
+  #       return true
+  #    end
+  #  end
+  def protect?(action)
+    true
+  end
+   
+  # login_required filter. add 
+  #
+  #   before_filter :login_required
+  #
+  # if the controller should be under any rights management. 
+  # for finer access control you can overwrite
+  #   
+  #   def authorize?(user)
+  # 
+  def login_required
+    
+    if not protect?(action_name)
+      return true  
+    end
+
+    if session[:user] and authorize?(session[:user])
+      return true
+    end
+
+    # store current location so that we can 
+    # come back after the user logged in
+    store_location
+  
+    # call overwriteable reaction to unauthorized access
+    access_denied
+    return false 
+  end
+
+  # overwrite if you want to have special behavior in case the user is not authorized
+  # to access the current operation. 
+  # the default action is to redirect to the login screen
+  # example use :
+  # a popup window might just close itself for instance
+  def access_denied
+    redirect_to :controller=>"/account", :action =>"login"
+  end  
+  
+  # store current uri in  the session.
+  # we can return to this location by calling return_location
+  def store_location
+    session['return-to'] = request.request_uri
+  end
+
+  # move to the last store_location call or to the passed default one
+  def redirect_back_or_default(default)
+    if session['return-to'].nil?
+      redirect_to default
+    else
+      redirect_to( session['return-to'] )
+      session['return-to'] = nil
+    end
+  end
+
+end
diff --git a/automation/admin/log/server.log b/automation/admin/log/server.log
new file mode 100644
index 0000000..e69de29
diff --git a/automation/admin/public/.htaccess b/automation/admin/public/.htaccess
new file mode 100644
index 0000000..d3c9983
--- /dev/null
+++ b/automation/admin/public/.htaccess
@@ -0,0 +1,40 @@
+# General Apache options
+AddHandler fastcgi-script .fcgi
+AddHandler cgi-script .cgi
+Options +FollowSymLinks +ExecCGI
+
+# If you don't want Rails to look in certain directories,
+# use the following rewrite rules so that Apache won't rewrite certain requests
+# 
+# Example:
+#   RewriteCond %{REQUEST_URI} ^/notrails.*
+#   RewriteRule .* - [L]
+
+# Redirect all requests not available on the filesystem to Rails
+# By default the cgi dispatcher is used which is very slow
+# 
+# For better performance replace the dispatcher with the fastcgi one
+#
+# Example:
+#   RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]
+RewriteEngine On
+
+# If your Rails application is accessed via an Alias directive,
+# then you MUST also set the RewriteBase in this htaccess file.
+#
+# Example:
+#   Alias /myrailsapp /path/to/myrailsapp/public
+#   RewriteBase /myrailsapp
+
+RewriteRule ^$ index.html [QSA]
+RewriteRule ^([^.]+)$ $1.html [QSA]
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteRule ^(.*)$ dispatch.cgi [QSA,L]
+
+# In case Rails experiences terminal errors
+# Instead of displaying this message you can supply a file here which will be rendered instead
+# 
+# Example:
+#   ErrorDocument 500 /500.html
+
+ErrorDocument 500 "<h2>Application error</h2>Rails application failed to start properly"
\ No newline at end of file
diff --git a/automation/admin/public/404.html b/automation/admin/public/404.html
new file mode 100644
index 0000000..0e18456
--- /dev/null
+++ b/automation/admin/public/404.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+   "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<body>
+  <h1>File not found</h1>
+  <p>Change this error message for pages not found in public/404.html</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/automation/admin/public/500.html b/automation/admin/public/500.html
new file mode 100644
index 0000000..a1001a0
--- /dev/null
+++ b/automation/admin/public/500.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+   "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<body>
+  <h1>Application error (Apache)</h1>
+  <p>Change this error message for exceptions thrown outside of an action (like in Dispatcher setups or broken Ruby code) in public/500.html</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/automation/admin/public/dispatch.cgi b/automation/admin/public/dispatch.cgi
new file mode 100755
index 0000000..32fa3b2
--- /dev/null
+++ b/automation/admin/public/dispatch.cgi
@@ -0,0 +1,10 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT)
+
+# If you're using RubyGems and mod_ruby, this require should be changed to an absolute path one, like:
+# "/usr/local/lib/ruby/gems/1.8/gems/rails-0.8.0/lib/dispatcher" -- otherwise performance is severely impaired
+require "dispatcher"
+
+ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } if defined?(Apache::RubyRun)
+Dispatcher.dispatch
diff --git a/automation/admin/public/dispatch.fcgi b/automation/admin/public/dispatch.fcgi
new file mode 100755
index 0000000..664dbbb
--- /dev/null
+++ b/automation/admin/public/dispatch.fcgi
@@ -0,0 +1,24 @@
+#!/usr/bin/env ruby
+#
+# You may specify the path to the FastCGI crash log (a log of unhandled
+# exceptions which forced the FastCGI instance to exit, great for debugging)
+# and the number of requests to process before running garbage collection.
+#
+# By default, the FastCGI crash log is RAILS_ROOT/log/fastcgi.crash.log
+# and the GC period is nil (turned off).  A reasonable number of requests
+# could range from 10-100 depending on the memory footprint of your app.
+#
+# Example:
+#   # Default log path, normal GC behavior.
+#   RailsFCGIHandler.process!
+#
+#   # Default log path, 50 requests between GC.
+#   RailsFCGIHandler.process! nil, 50
+#
+#   # Custom log path, normal GC behavior.
+#   RailsFCGIHandler.process! '/var/log/myapp_fcgi_crash.log'
+#
+require File.dirname(__FILE__) + "/../config/environment"
+require 'fcgi_handler'
+
+RailsFCGIHandler.process!
diff --git a/automation/admin/public/dispatch.rb b/automation/admin/public/dispatch.rb
new file mode 100755
index 0000000..32fa3b2
--- /dev/null
+++ b/automation/admin/public/dispatch.rb
@@ -0,0 +1,10 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT)
+
+# If you're using RubyGems and mod_ruby, this require should be changed to an absolute path one, like:
+# "/usr/local/lib/ruby/gems/1.8/gems/rails-0.8.0/lib/dispatcher" -- otherwise performance is severely impaired
+require "dispatcher"
+
+ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } if defined?(Apache::RubyRun)
+Dispatcher.dispatch
diff --git a/automation/admin/public/favicon.ico b/automation/admin/public/favicon.ico
new file mode 100644
index 0000000..e69de29
diff --git a/automation/admin/public/images/rails.png b/automation/admin/public/images/rails.png
new file mode 100644
index 0000000..837bb9a
Binary files /dev/null and b/automation/admin/public/images/rails.png differ
diff --git a/automation/admin/public/index.html b/automation/admin/public/index.html
new file mode 100644
index 0000000..8fd0f41
--- /dev/null
+++ b/automation/admin/public/index.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+  <head>
+    <title>Curator Submissions Tracking</title>
+    <link rel="stylesheet" href="stylesheets/admin.css" type="text/css">
+  </head>
+
+  <body>
+    <h1>Curator Submissions Tracking</h1>
+
+    <p class="text">Please select from the following:</p>
+
+    <ul>
+      <li><a href="experiments/today_list"><b>Recent experiments</b></a></li>
+    </ul>
+    <ul>
+      <li><a href="tab2mages/list?experiment_type=Tab2MAGE">Tab2MAGE experiments</a></li>
+      <li><a href="miamexps/list?experiment_type=MIAMExpress">MIAMExpress experiments</a></li>
+      <li><a href="tab2mages/list?experiment_type=MAGE-TAB">MAGE-TAB experiments</a></li>
+      <li><a href="tab2mages/list?experiment_type=GEO">GEO experiments</a></li>
+      <li><a href="tab2mages/list?experiment_type=MUGEN">MUGEN experiments</a></li>
+      <li><a href="array_designs/list">MIAMExpress arrays</a></li>
+      <li><a href="protocols/list">Tab2MAGE protocols</a></li>
+    </ul>
+    <ul>
+      <li><a href="experiments/list">All Experiments</a></li>
+      <li><a href="array_designs/list_all">All Array Designs</a></li>
+    </ul>
+    <ul>
+      <li><a href="account/signup">Sign up new user</a></li>
+    </ul>
+
+  </body>
+</html>
diff --git a/automation/admin/public/javascripts/controls.js b/automation/admin/public/javascripts/controls.js
new file mode 100644
index 0000000..9742b69
--- /dev/null
+++ b/automation/admin/public/javascripts/controls.js
@@ -0,0 +1,750 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
+//           (c) 2005 Jon Tirsen (http://www.tirsen.com)
+// Contributors:
+//  Richard Livsey
+//  Rahul Bhargava
+//  Rob Wills
+// 
+// See scriptaculous.js for full license.
+
+// Autocompleter.Base handles all the autocompletion functionality 
+// that's independent of the data source for autocompletion. This
+// includes drawing the autocompletion menu, observing keyboard
+// and mouse events, and similar.
+//
+// Specific autocompleters need to provide, at the very least, 
+// a getUpdatedChoices function that will be invoked every time
+// the text inside the monitored textbox changes. This method 
+// should get the text for which to provide autocompletion by
+// invoking this.getToken(), NOT by directly accessing
+// this.element.value. This is to allow incremental tokenized
+// autocompletion. Specific auto-completion logic (AJAX, etc)
+// belongs in getUpdatedChoices.
+//
+// Tokenized incremental autocompletion is enabled automatically
+// when an autocompleter is instantiated with the 'tokens' option
+// in the options parameter, e.g.:
+// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
+// will incrementally autocomplete with a comma as the token.
+// Additionally, ',' in the above example can be replaced with
+// a token array, e.g. { tokens: [',', '\n'] } which
+// enables autocompletion on multiple tokens. This is most 
+// useful when one of the tokens is \n (a newline), as it 
+// allows smart autocompletion after linebreaks.
+
+var Autocompleter = {}
+Autocompleter.Base = function() {};
+Autocompleter.Base.prototype = {
+  baseInitialize: function(element, update, options) {
+    this.element     = $(element); 
+    this.update      = $(update);  
+    this.hasFocus    = false; 
+    this.changed     = false; 
+    this.active      = false; 
+    this.index       = 0;     
+    this.entryCount  = 0;
+
+    if (this.setOptions)
+      this.setOptions(options);
+    else
+      this.options = options || {};
+
+    this.options.paramName    = this.options.paramName || this.element.name;
+    this.options.tokens       = this.options.tokens || [];
+    this.options.frequency    = this.options.frequency || 0.4;
+    this.options.minChars     = this.options.minChars || 1;
+    this.options.onShow       = this.options.onShow || 
+    function(element, update){ 
+      if(!update.style.position || update.style.position=='absolute') {
+        update.style.position = 'absolute';
+        Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight});
+      }
+      Effect.Appear(update,{duration:0.15});
+    };
+    this.options.onHide = this.options.onHide || 
+    function(element, update){ new Effect.Fade(update,{duration:0.15}) };
+
+    if (typeof(this.options.tokens) == 'string') 
+      this.options.tokens = new Array(this.options.tokens);
+
+    this.observer = null;
+    
+    this.element.setAttribute('autocomplete','off');
+
+    Element.hide(this.update);
+
+    Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
+    Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
+  },
+
+  show: function() {
+    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
+    if(!this.iefix && 
+      (navigator.appVersion.indexOf('MSIE')>0) &&
+      (navigator.userAgent.indexOf('Opera')<0) &&
+      (Element.getStyle(this.update, 'position')=='absolute')) {
+      new Insertion.After(this.update, 
+       '<iframe id="' + this.update.id + '_iefix" '+
+       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
+       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
+      this.iefix = $(this.update.id+'_iefix');
+    }
+    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
+  },
+  
+  fixIEOverlapping: function() {
+    Position.clone(this.update, this.iefix);
+    this.iefix.style.zIndex = 1;
+    this.update.style.zIndex = 2;
+    Element.show(this.iefix);
+  },
+
+  hide: function() {
+    this.stopIndicator();
+    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
+    if(this.iefix) Element.hide(this.iefix);
+  },
+
+  startIndicator: function() {
+    if(this.options.indicator) Element.show(this.options.indicator);
+  },
+
+  stopIndicator: function() {
+    if(this.options.indicator) Element.hide(this.options.indicator);
+  },
+
+  onKeyPress: function(event) {
+    if(this.active)
+      switch(event.keyCode) {
+       case Event.KEY_TAB:
+       case Event.KEY_RETURN:
+         this.selectEntry();
+         Event.stop(event);
+       case Event.KEY_ESC:
+         this.hide();
+         this.active = false;
+         Event.stop(event);
+         return;
+       case Event.KEY_LEFT:
+       case Event.KEY_RIGHT:
+         return;
+       case Event.KEY_UP:
+         this.markPrevious();
+         this.render();
+         if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
+         return;
+       case Event.KEY_DOWN:
+         this.markNext();
+         this.render();
+         if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
+         return;
+      }
+     else 
+      if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN) 
+        return;
+
+    this.changed = true;
+    this.hasFocus = true;
+
+    if(this.observer) clearTimeout(this.observer);
+      this.observer = 
+        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
+  },
+
+  onHover: function(event) {
+    var element = Event.findElement(event, 'LI');
+    if(this.index != element.autocompleteIndex) 
+    {
+        this.index = element.autocompleteIndex;
+        this.render();
+    }
+    Event.stop(event);
+  },
+  
+  onClick: function(event) {
+    var element = Event.findElement(event, 'LI');
+    this.index = element.autocompleteIndex;
+    this.selectEntry();
+    this.hide();
+  },
+  
+  onBlur: function(event) {
+    // needed to make click events working
+    setTimeout(this.hide.bind(this), 250);
+    this.hasFocus = false;
+    this.active = false;     
+  }, 
+  
+  render: function() {
+    if(this.entryCount > 0) {
+      for (var i = 0; i < this.entryCount; i++)
+        this.index==i ? 
+          Element.addClassName(this.getEntry(i),"selected") : 
+          Element.removeClassName(this.getEntry(i),"selected");
+        
+      if(this.hasFocus) { 
+        this.show();
+        this.active = true;
+      }
+    } else {
+      this.active = false;
+      this.hide();
+    }
+  },
+  
+  markPrevious: function() {
+    if(this.index > 0) this.index--
+      else this.index = this.entryCount-1;
+  },
+  
+  markNext: function() {
+    if(this.index < this.entryCount-1) this.index++
+      else this.index = 0;
+  },
+  
+  getEntry: function(index) {
+    return this.update.firstChild.childNodes[index];
+  },
+  
+  getCurrentEntry: function() {
+    return this.getEntry(this.index);
+  },
+  
+  selectEntry: function() {
+    this.active = false;
+    this.updateElement(this.getCurrentEntry());
+  },
+
+  updateElement: function(selectedElement) {
+    if (this.options.updateElement) {
+      this.options.updateElement(selectedElement);
+      return;
+    }
+
+    var value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
+    var lastTokenPos = this.findLastToken();
+    if (lastTokenPos != -1) {
+      var newValue = this.element.value.substr(0, lastTokenPos + 1);
+      var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
+      if (whitespace)
+        newValue += whitespace[0];
+      this.element.value = newValue + value;
+    } else {
+      this.element.value = value;
+    }
+    this.element.focus();
+    
+    if (this.options.afterUpdateElement)
+      this.options.afterUpdateElement(this.element, selectedElement);
+  },
+
+  updateChoices: function(choices) {
+    if(!this.changed && this.hasFocus) {
+      this.update.innerHTML = choices;
+      Element.cleanWhitespace(this.update);
+      Element.cleanWhitespace(this.update.firstChild);
+
+      if(this.update.firstChild && this.update.firstChild.childNodes) {
+        this.entryCount = 
+          this.update.firstChild.childNodes.length;
+        for (var i = 0; i < this.entryCount; i++) {
+          var entry = this.getEntry(i);
+          entry.autocompleteIndex = i;
+          this.addObservers(entry);
+        }
+      } else { 
+        this.entryCount = 0;
+      }
+
+      this.stopIndicator();
+
+      this.index = 0;
+      this.render();
+    }
+  },
+
+  addObservers: function(element) {
+    Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
+    Event.observe(element, "click", this.onClick.bindAsEventListener(this));
+  },
+
+  onObserverEvent: function() {
+    this.changed = false;   
+    if(this.getToken().length>=this.options.minChars) {
+      this.startIndicator();
+      this.getUpdatedChoices();
+    } else {
+      this.active = false;
+      this.hide();
+    }
+  },
+
+  getToken: function() {
+    var tokenPos = this.findLastToken();
+    if (tokenPos != -1)
+      var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
+    else
+      var ret = this.element.value;
+
+    return /\n/.test(ret) ? '' : ret;
+  },
+
+  findLastToken: function() {
+    var lastTokenPos = -1;
+
+    for (var i=0; i<this.options.tokens.length; i++) {
+      var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
+      if (thisTokenPos > lastTokenPos)
+        lastTokenPos = thisTokenPos;
+    }
+    return lastTokenPos;
+  }
+}
+
+Ajax.Autocompleter = Class.create();
+Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
+  initialize: function(element, update, url, options) {
+	  this.baseInitialize(element, update, options);
+    this.options.asynchronous  = true;
+    this.options.onComplete    = this.onComplete.bind(this);
+    this.options.defaultParams = this.options.parameters || null;
+    this.url                   = url;
+  },
+
+  getUpdatedChoices: function() {
+    entry = encodeURIComponent(this.options.paramName) + '=' + 
+      encodeURIComponent(this.getToken());
+
+    this.options.parameters = this.options.callback ?
+      this.options.callback(this.element, entry) : entry;
+
+    if(this.options.defaultParams) 
+      this.options.parameters += '&' + this.options.defaultParams;
+
+    new Ajax.Request(this.url, this.options);
+  },
+
+  onComplete: function(request) {
+    this.updateChoices(request.responseText);
+  }
+
+});
+
+// The local array autocompleter. Used when you'd prefer to
+// inject an array of autocompletion options into the page, rather
+// than sending out Ajax queries, which can be quite slow sometimes.
+//
+// The constructor takes four parameters. The first two are, as usual,
+// the id of the monitored textbox, and id of the autocompletion menu.
+// The third is the array you want to autocomplete from, and the fourth
+// is the options block.
+//
+// Extra local autocompletion options:
+// - choices - How many autocompletion choices to offer
+//
+// - partialSearch - If false, the autocompleter will match entered
+//                    text only at the beginning of strings in the 
+//                    autocomplete array. Defaults to true, which will
+//                    match text at the beginning of any *word* in the
+//                    strings in the autocomplete array. If you want to
+//                    search anywhere in the string, additionally set
+//                    the option fullSearch to true (default: off).
+//
+// - fullSsearch - Search anywhere in autocomplete array strings.
+//
+// - partialChars - How many characters to enter before triggering
+//                   a partial match (unlike minChars, which defines
+//                   how many characters are required to do any match
+//                   at all). Defaults to 2.
+//
+// - ignoreCase - Whether to ignore case when autocompleting.
+//                 Defaults to true.
+//
+// It's possible to pass in a custom function as the 'selector' 
+// option, if you prefer to write your own autocompletion logic.
+// In that case, the other options above will not apply unless
+// you support them.
+
+Autocompleter.Local = Class.create();
+Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
+  initialize: function(element, update, array, options) {
+    this.baseInitialize(element, update, options);
+    this.options.array = array;
+  },
+
+  getUpdatedChoices: function() {
+    this.updateChoices(this.options.selector(this));
+  },
+
+  setOptions: function(options) {
+    this.options = Object.extend({
+      choices: 10,
+      partialSearch: true,
+      partialChars: 2,
+      ignoreCase: true,
+      fullSearch: false,
+      selector: function(instance) {
+        var ret       = []; // Beginning matches
+        var partial   = []; // Inside matches
+        var entry     = instance.getToken();
+        var count     = 0;
+
+        for (var i = 0; i < instance.options.array.length &&  
+          ret.length < instance.options.choices ; i++) { 
+
+          var elem = instance.options.array[i];
+          var foundPos = instance.options.ignoreCase ? 
+            elem.toLowerCase().indexOf(entry.toLowerCase()) : 
+            elem.indexOf(entry);
+
+          while (foundPos != -1) {
+            if (foundPos == 0 && elem.length != entry.length) { 
+              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" + 
+                elem.substr(entry.length) + "</li>");
+              break;
+            } else if (entry.length >= instance.options.partialChars && 
+              instance.options.partialSearch && foundPos != -1) {
+              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
+                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
+                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
+                  foundPos + entry.length) + "</li>");
+                break;
+              }
+            }
+
+            foundPos = instance.options.ignoreCase ? 
+              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 
+              elem.indexOf(entry, foundPos + 1);
+
+          }
+        }
+        if (partial.length)
+          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
+        return "<ul>" + ret.join('') + "</ul>";
+      }
+    }, options || {});
+  }
+});
+
+// AJAX in-place editor
+//
+// see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
+
+// Use this if you notice weird scrolling problems on some browsers,
+// the DOM might be a bit confused when this gets called so do this
+// waits 1 ms (with setTimeout) until it does the activation
+Field.scrollFreeActivate = function(field) {
+  setTimeout(function() {
+    Field.activate(field);
+  }, 1);
+}
+
+Ajax.InPlaceEditor = Class.create();
+Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
+Ajax.InPlaceEditor.prototype = {
+  initialize: function(element, url, options) {
+    this.url = url;
+    this.element = $(element);
+
+    this.options = Object.extend({
+      okText: "ok",
+      cancelText: "cancel",
+      savingText: "Saving...",
+      clickToEditText: "Click to edit",
+      okText: "ok",
+      rows: 1,
+      onComplete: function(transport, element) {
+        new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
+      },
+      onFailure: function(transport) {
+        alert("Error communicating with the server: " + transport.responseText.stripTags());
+      },
+      callback: function(form) {
+        return Form.serialize(form);
+      },
+      handleLineBreaks: true,
+      loadingText: 'Loading...',
+      savingClassName: 'inplaceeditor-saving',
+      loadingClassName: 'inplaceeditor-loading',
+      formClassName: 'inplaceeditor-form',
+      highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
+      highlightendcolor: "#FFFFFF",
+      externalControl:	null,
+      ajaxOptions: {}
+    }, options || {});
+
+    if(!this.options.formId && this.element.id) {
+      this.options.formId = this.element.id + "-inplaceeditor";
+      if ($(this.options.formId)) {
+        // there's already a form with that name, don't specify an id
+        this.options.formId = null;
+      }
+    }
+    
+    if (this.options.externalControl) {
+      this.options.externalControl = $(this.options.externalControl);
+    }
+    
+    this.originalBackground = Element.getStyle(this.element, 'background-color');
+    if (!this.originalBackground) {
+      this.originalBackground = "transparent";
+    }
+    
+    this.element.title = this.options.clickToEditText;
+    
+    this.onclickListener = this.enterEditMode.bindAsEventListener(this);
+    this.mouseoverListener = this.enterHover.bindAsEventListener(this);
+    this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
+    Event.observe(this.element, 'click', this.onclickListener);
+    Event.observe(this.element, 'mouseover', this.mouseoverListener);
+    Event.observe(this.element, 'mouseout', this.mouseoutListener);
+    if (this.options.externalControl) {
+      Event.observe(this.options.externalControl, 'click', this.onclickListener);
+      Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
+      Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
+    }
+  },
+  enterEditMode: function(evt) {
+    if (this.saving) return;
+    if (this.editing) return;
+    this.editing = true;
+    this.onEnterEditMode();
+    if (this.options.externalControl) {
+      Element.hide(this.options.externalControl);
+    }
+    Element.hide(this.element);
+    this.createForm();
+    this.element.parentNode.insertBefore(this.form, this.element);
+    Field.scrollFreeActivate(this.editField);
+    // stop the event to avoid a page refresh in Safari
+    if (evt) {
+      Event.stop(evt);
+    }
+    return false;
+  },
+  createForm: function() {
+    this.form = document.createElement("form");
+    this.form.id = this.options.formId;
+    Element.addClassName(this.form, this.options.formClassName)
+    this.form.onsubmit = this.onSubmit.bind(this);
+
+    this.createEditField();
+
+    if (this.options.textarea) {
+      var br = document.createElement("br");
+      this.form.appendChild(br);
+    }
+
+    okButton = document.createElement("input");
+    okButton.type = "submit";
+    okButton.value = this.options.okText;
+    this.form.appendChild(okButton);
+
+    cancelLink = document.createElement("a");
+    cancelLink.href = "#";
+    cancelLink.appendChild(document.createTextNode(this.options.cancelText));
+    cancelLink.onclick = this.onclickCancel.bind(this);
+    this.form.appendChild(cancelLink);
+  },
+  hasHTMLLineBreaks: function(string) {
+    if (!this.options.handleLineBreaks) return false;
+    return string.match(/<br/i) || string.match(/<p>/i);
+  },
+  convertHTMLLineBreaks: function(string) {
+    return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
+  },
+  createEditField: function() {
+    var text;
+    if(this.options.loadTextURL) {
+      text = this.options.loadingText;
+    } else {
+      text = this.getText();
+    }
+    
+    if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
+      this.options.textarea = false;
+      var textField = document.createElement("input");
+      textField.type = "text";
+      textField.name = "value";
+      textField.value = text;
+      textField.style.backgroundColor = this.options.highlightcolor;
+      var size = this.options.size || this.options.cols || 0;
+      if (size != 0) textField.size = size;
+      this.editField = textField;
+    } else {
+      this.options.textarea = true;
+      var textArea = document.createElement("textarea");
+      textArea.name = "value";
+      textArea.value = this.convertHTMLLineBreaks(text);
+      textArea.rows = this.options.rows;
+      textArea.cols = this.options.cols || 40;
+      this.editField = textArea;
+    }
+    
+    if(this.options.loadTextURL) {
+      this.loadExternalText();
+    }
+    this.form.appendChild(this.editField);
+  },
+  getText: function() {
+    return this.element.innerHTML;
+  },
+  loadExternalText: function() {
+    Element.addClassName(this.form, this.options.loadingClassName);
+    this.editField.disabled = true;
+    new Ajax.Request(
+      this.options.loadTextURL,
+      Object.extend({
+        asynchronous: true,
+        onComplete: this.onLoadedExternalText.bind(this)
+      }, this.options.ajaxOptions)
+    );
+  },
+  onLoadedExternalText: function(transport) {
+    Element.removeClassName(this.form, this.options.loadingClassName);
+    this.editField.disabled = false;
+    this.editField.value = transport.responseText.stripTags();
+  },
+  onclickCancel: function() {
+    this.onComplete();
+    this.leaveEditMode();
+    return false;
+  },
+  onFailure: function(transport) {
+    this.options.onFailure(transport);
+    if (this.oldInnerHTML) {
+      this.element.innerHTML = this.oldInnerHTML;
+      this.oldInnerHTML = null;
+    }
+    return false;
+  },
+  onSubmit: function() {
+    // onLoading resets these so we need to save them away for the Ajax call
+    var form = this.form;
+    var value = this.editField.value;
+    
+    // do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
+    // which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
+    // to be displayed indefinitely
+    this.onLoading();
+    
+    new Ajax.Updater(
+      { 
+        success: this.element,
+         // don't update on failure (this could be an option)
+        failure: null
+      },
+      this.url,
+      Object.extend({
+        parameters: this.options.callback(form, value),
+        onComplete: this.onComplete.bind(this),
+        onFailure: this.onFailure.bind(this)
+      }, this.options.ajaxOptions)
+    );
+    // stop the event to avoid a page refresh in Safari
+    if (arguments.length > 1) {
+      Event.stop(arguments[0]);
+    }
+    return false;
+  },
+  onLoading: function() {
+    this.saving = true;
+    this.removeForm();
+    this.leaveHover();
+    this.showSaving();
+  },
+  showSaving: function() {
+    this.oldInnerHTML = this.element.innerHTML;
+    this.element.innerHTML = this.options.savingText;
+    Element.addClassName(this.element, this.options.savingClassName);
+    this.element.style.backgroundColor = this.originalBackground;
+    Element.show(this.element);
+  },
+  removeForm: function() {
+    if(this.form) {
+      if (this.form.parentNode) Element.remove(this.form);
+      this.form = null;
+    }
+  },
+  enterHover: function() {
+    if (this.saving) return;
+    this.element.style.backgroundColor = this.options.highlightcolor;
+    if (this.effect) {
+      this.effect.cancel();
+    }
+    Element.addClassName(this.element, this.options.hoverClassName)
+  },
+  leaveHover: function() {
+    if (this.options.backgroundColor) {
+      this.element.style.backgroundColor = this.oldBackground;
+    }
+    Element.removeClassName(this.element, this.options.hoverClassName)
+    if (this.saving) return;
+    this.effect = new Effect.Highlight(this.element, {
+      startcolor: this.options.highlightcolor,
+      endcolor: this.options.highlightendcolor,
+      restorecolor: this.originalBackground
+    });
+  },
+  leaveEditMode: function() {
+    Element.removeClassName(this.element, this.options.savingClassName);
+    this.removeForm();
+    this.leaveHover();
+    this.element.style.backgroundColor = this.originalBackground;
+    Element.show(this.element);
+    if (this.options.externalControl) {
+      Element.show(this.options.externalControl);
+    }
+    this.editing = false;
+    this.saving = false;
+    this.oldInnerHTML = null;
+    this.onLeaveEditMode();
+  },
+  onComplete: function(transport) {
+    this.leaveEditMode();
+    this.options.onComplete.bind(this)(transport, this.element);
+  },
+  onEnterEditMode: function() {},
+  onLeaveEditMode: function() {},
+  dispose: function() {
+    if (this.oldInnerHTML) {
+      this.element.innerHTML = this.oldInnerHTML;
+    }
+    this.leaveEditMode();
+    Event.stopObserving(this.element, 'click', this.onclickListener);
+    Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
+    Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
+    if (this.options.externalControl) {
+      Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
+      Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
+      Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
+    }
+  }
+};
+
+// Delayed observer, like Form.Element.Observer, 
+// but waits for delay after last key input
+// Ideal for live-search fields
+
+Form.Element.DelayedObserver = Class.create();
+Form.Element.DelayedObserver.prototype = {
+  initialize: function(element, delay, callback) {
+    this.delay     = delay || 0.5;
+    this.element   = $(element);
+    this.callback  = callback;
+    this.timer     = null;
+    this.lastValue = $F(this.element); 
+    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
+  },
+  delayedListener: function(event) {
+    if(this.lastValue == $F(this.element)) return;
+    if(this.timer) clearTimeout(this.timer);
+    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
+    this.lastValue = $F(this.element);
+  },
+  onTimerEvent: function() {
+    this.timer = null;
+    this.callback(this.element, $F(this.element));
+  }
+};
\ No newline at end of file
diff --git a/automation/admin/public/javascripts/dragdrop.js b/automation/admin/public/javascripts/dragdrop.js
new file mode 100644
index 0000000..92d1f73
--- /dev/null
+++ b/automation/admin/public/javascripts/dragdrop.js
@@ -0,0 +1,584 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// 
+// See scriptaculous.js for full license.
+
+/*--------------------------------------------------------------------------*/
+
+var Droppables = {
+  drops: [],
+
+  remove: function(element) {
+    this.drops = this.drops.reject(function(d) { return d.element==$(element) });
+  },
+
+  add: function(element) {
+    element = $(element);
+    var options = Object.extend({
+      greedy:     true,
+      hoverclass: null  
+    }, arguments[1] || {});
+
+    // cache containers
+    if(options.containment) {
+      options._containers = [];
+      var containment = options.containment;
+      if((typeof containment == 'object') && 
+        (containment.constructor == Array)) {
+        containment.each( function(c) { options._containers.push($(c)) });
+      } else {
+        options._containers.push($(containment));
+      }
+    }
+    
+    if(options.accept) options.accept = [options.accept].flatten();
+
+    Element.makePositioned(element); // fix IE
+    options.element = element;
+
+    this.drops.push(options);
+  },
+
+  isContained: function(element, drop) {
+    var parentNode = element.parentNode;
+    return drop._containers.detect(function(c) { return parentNode == c });
+  },
+
+  isAffected: function(point, element, drop) {
+    return (
+      (drop.element!=element) &&
+      ((!drop._containers) ||
+        this.isContained(element, drop)) &&
+      ((!drop.accept) ||
+        (Element.classNames(element).detect( 
+          function(v) { return drop.accept.include(v) } ) )) &&
+      Position.within(drop.element, point[0], point[1]) );
+  },
+
+  deactivate: function(drop) {
+    if(drop.hoverclass)
+      Element.removeClassName(drop.element, drop.hoverclass);
+    this.last_active = null;
+  },
+
+  activate: function(drop) {
+    if(drop.hoverclass)
+      Element.addClassName(drop.element, drop.hoverclass);
+    this.last_active = drop;
+  },
+
+  show: function(point, element) {
+    if(!this.drops.length) return;
+    
+    if(this.last_active) this.deactivate(this.last_active);
+    this.drops.each( function(drop) {
+      if(Droppables.isAffected(point, element, drop)) {
+        if(drop.onHover)
+           drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
+        if(drop.greedy) { 
+          Droppables.activate(drop);
+          throw $break;
+        }
+      }
+    });
+  },
+
+  fire: function(event, element) {
+    if(!this.last_active) return;
+    Position.prepare();
+
+    if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
+      if (this.last_active.onDrop) 
+        this.last_active.onDrop(element, this.last_active.element, event);
+  },
+
+  reset: function() {
+    if(this.last_active)
+      this.deactivate(this.last_active);
+  }
+}
+
+var Draggables = {
+  drags: [],
+  observers: [],
+  
+  register: function(draggable) {
+    if(this.drags.length == 0) {
+      this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
+      this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
+      this.eventKeypress  = this.keyPress.bindAsEventListener(this);
+      
+      Event.observe(document, "mouseup", this.eventMouseUp);
+      Event.observe(document, "mousemove", this.eventMouseMove);
+      Event.observe(document, "keypress", this.eventKeypress);
+    }
+    this.drags.push(draggable);
+  },
+  
+  unregister: function(draggable) {
+    this.drags = this.drags.reject(function(d) { return d==draggable });
+    if(this.drags.length == 0) {
+      Event.stopObserving(document, "mouseup", this.eventMouseUp);
+      Event.stopObserving(document, "mousemove", this.eventMouseMove);
+      Event.stopObserving(document, "keypress", this.eventKeypress);
+    }
+  },
+  
+  activate: function(draggable) {
+    window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
+    this.activeDraggable = draggable;
+  },
+  
+  deactivate: function(draggbale) {
+    this.activeDraggable = null;
+  },
+  
+  updateDrag: function(event) {
+    if(!this.activeDraggable) return;
+    var pointer = [Event.pointerX(event), Event.pointerY(event)];
+    // Mozilla-based browsers fire successive mousemove events with
+    // the same coordinates, prevent needless redrawing (moz bug?)
+    if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
+    this._lastPointer = pointer;
+    this.activeDraggable.updateDrag(event, pointer);
+  },
+  
+  endDrag: function(event) {
+    if(!this.activeDraggable) return;
+    this._lastPointer = null;
+    this.activeDraggable.endDrag(event);
+  },
+  
+  keyPress: function(event) {
+    if(this.activeDraggable)
+      this.activeDraggable.keyPress(event);
+  },
+  
+  addObserver: function(observer) {
+    this.observers.push(observer);
+    this._cacheObserverCallbacks();
+  },
+  
+  removeObserver: function(element) {  // element instead of observer fixes mem leaks
+    this.observers = this.observers.reject( function(o) { return o.element==element });
+    this._cacheObserverCallbacks();
+  },
+  
+  notify: function(eventName, draggable, event) {  // 'onStart', 'onEnd', 'onDrag'
+    if(this[eventName+'Count'] > 0)
+      this.observers.each( function(o) {
+        if(o[eventName]) o[eventName](eventName, draggable, event);
+      });
+  },
+  
+  _cacheObserverCallbacks: function() {
+    ['onStart','onEnd','onDrag'].each( function(eventName) {
+      Draggables[eventName+'Count'] = Draggables.observers.select(
+        function(o) { return o[eventName]; }
+      ).length;
+    });
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Draggable = Class.create();
+Draggable.prototype = {
+  initialize: function(element) {
+    var options = Object.extend({
+      handle: false,
+      starteffect: function(element) { 
+        new Effect.Opacity(element, {duration:0.2, from:1.0, to:0.7}); 
+      },
+      reverteffect: function(element, top_offset, left_offset) {
+        var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
+        element._revert = new Effect.MoveBy(element, -top_offset, -left_offset, {duration:dur});
+      },
+      endeffect: function(element) { 
+        new Effect.Opacity(element, {duration:0.2, from:0.7, to:1.0}); 
+      },
+      zindex: 1000,
+      revert: false,
+      snap: false   // false, or xy or [x,y] or function(x,y){ return [x,y] }
+    }, arguments[1] || {});
+
+    this.element = $(element);
+    
+    if(options.handle && (typeof options.handle == 'string'))
+      this.handle = Element.childrenWithClassName(this.element, options.handle)[0];  
+    if(!this.handle) this.handle = $(options.handle);
+    if(!this.handle) this.handle = this.element;
+
+    Element.makePositioned(this.element); // fix IE    
+
+    this.delta    = this.currentDelta();
+    this.options  = options;
+    this.dragging = false;   
+
+    this.eventMouseDown = this.initDrag.bindAsEventListener(this);
+    Event.observe(this.handle, "mousedown", this.eventMouseDown);
+    
+    Draggables.register(this);
+  },
+  
+  destroy: function() {
+    Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
+    Draggables.unregister(this);
+  },
+  
+  currentDelta: function() {
+    return([
+      parseInt(this.element.style.left || '0'),
+      parseInt(this.element.style.top || '0')]);
+  },
+  
+  initDrag: function(event) {
+    if(Event.isLeftClick(event)) {    
+      // abort on form elements, fixes a Firefox issue
+      var src = Event.element(event);
+      if(src.tagName && (
+        src.tagName=='INPUT' ||
+        src.tagName=='SELECT' ||
+        src.tagName=='BUTTON' ||
+        src.tagName=='TEXTAREA')) return;
+        
+      if(this.element._revert) {
+        this.element._revert.cancel();
+        this.element._revert = null;
+      }
+      
+      var pointer = [Event.pointerX(event), Event.pointerY(event)];
+      var pos     = Position.cumulativeOffset(this.element);
+      this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
+      
+      Draggables.activate(this);
+      Event.stop(event);
+    }
+  },
+  
+  startDrag: function(event) {
+    this.dragging = true;
+    
+    if(this.options.zindex) {
+      this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
+      this.element.style.zIndex = this.options.zindex;
+    }
+    
+    if(this.options.ghosting) {
+      this._clone = this.element.cloneNode(true);
+      Position.absolutize(this.element);
+      this.element.parentNode.insertBefore(this._clone, this.element);
+    }
+    
+    Draggables.notify('onStart', this, event);
+    if(this.options.starteffect) this.options.starteffect(this.element);
+  },
+  
+  updateDrag: function(event, pointer) {
+    if(!this.dragging) this.startDrag(event);
+    Position.prepare();
+    Droppables.show(pointer, this.element);
+    Draggables.notify('onDrag', this, event);
+    this.draw(pointer);
+    if(this.options.change) this.options.change(this);
+    
+    // fix AppleWebKit rendering
+    if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+    Event.stop(event);
+  },
+  
+  finishDrag: function(event, success) {
+    this.dragging = false;
+
+    if(this.options.ghosting) {
+      Position.relativize(this.element);
+      Element.remove(this._clone);
+      this._clone = null;
+    }
+
+    if(success) Droppables.fire(event, this.element);
+    Draggables.notify('onEnd', this, event);
+
+    var revert = this.options.revert;
+    if(revert && typeof revert == 'function') revert = revert(this.element);
+    
+    var d = this.currentDelta();
+    if(revert && this.options.reverteffect) {
+      this.options.reverteffect(this.element, 
+        d[1]-this.delta[1], d[0]-this.delta[0]);
+    } else {
+      this.delta = d;
+    }
+
+    if(this.options.zindex)
+      this.element.style.zIndex = this.originalZ;
+
+    if(this.options.endeffect) 
+      this.options.endeffect(this.element);
+
+    Draggables.deactivate(this);
+    Droppables.reset();
+  },
+  
+  keyPress: function(event) {
+    if(!event.keyCode==Event.KEY_ESC) return;
+    this.finishDrag(event, false);
+    Event.stop(event);
+  },
+  
+  endDrag: function(event) {
+    if(!this.dragging) return;
+    this.finishDrag(event, true);
+    Event.stop(event);
+  },
+  
+  draw: function(point) {
+    var pos = Position.cumulativeOffset(this.element);
+    var d = this.currentDelta();
+    pos[0] -= d[0]; pos[1] -= d[1];
+    
+    var p = [0,1].map(function(i){ return (point[i]-pos[i]-this.offset[i]) }.bind(this));
+    
+    if(this.options.snap) {
+      if(typeof this.options.snap == 'function') {
+        p = this.options.snap(p[0],p[1]);
+      } else {
+      if(this.options.snap instanceof Array) {
+        p = p.map( function(v, i) {
+          return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
+      } else {
+        p = p.map( function(v) {
+          return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
+      }
+    }}
+    
+    var style = this.element.style;
+    if((!this.options.constraint) || (this.options.constraint=='horizontal'))
+      style.left = p[0] + "px";
+    if((!this.options.constraint) || (this.options.constraint=='vertical'))
+      style.top  = p[1] + "px";
+    if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var SortableObserver = Class.create();
+SortableObserver.prototype = {
+  initialize: function(element, observer) {
+    this.element   = $(element);
+    this.observer  = observer;
+    this.lastValue = Sortable.serialize(this.element);
+  },
+  
+  onStart: function() {
+    this.lastValue = Sortable.serialize(this.element);
+  },
+  
+  onEnd: function() {
+    Sortable.unmark();
+    if(this.lastValue != Sortable.serialize(this.element))
+      this.observer(this.element)
+  }
+}
+
+var Sortable = {
+  sortables: new Array(),
+  
+  options: function(element){
+    element = $(element);
+    return this.sortables.detect(function(s) { return s.element == element });
+  },
+  
+  destroy: function(element){
+    element = $(element);
+    this.sortables.findAll(function(s) { return s.element == element }).each(function(s){
+      Draggables.removeObserver(s.element);
+      s.droppables.each(function(d){ Droppables.remove(d) });
+      s.draggables.invoke('destroy');
+    });
+    this.sortables = this.sortables.reject(function(s) { return s.element == element });
+  },
+  
+  create: function(element) {
+    element = $(element);
+    var options = Object.extend({ 
+      element:     element,
+      tag:         'li',       // assumes li children, override with tag: 'tagname'
+      dropOnEmpty: false,
+      tree:        false,      // fixme: unimplemented
+      overlap:     'vertical', // one of 'vertical', 'horizontal'
+      constraint:  'vertical', // one of 'vertical', 'horizontal', false
+      containment: element,    // also takes array of elements (or id's); or false
+      handle:      false,      // or a CSS class
+      only:        false,
+      hoverclass:  null,
+      ghosting:    false,
+      format:      null,
+      onChange:    Prototype.emptyFunction,
+      onUpdate:    Prototype.emptyFunction
+    }, arguments[1] || {});
+
+    // clear any old sortable with same element
+    this.destroy(element);
+
+    // build options for the draggables
+    var options_for_draggable = {
+      revert:      true,
+      ghosting:    options.ghosting,
+      constraint:  options.constraint,
+      handle:      options.handle };
+
+    if(options.starteffect)
+      options_for_draggable.starteffect = options.starteffect;
+
+    if(options.reverteffect)
+      options_for_draggable.reverteffect = options.reverteffect;
+    else
+      if(options.ghosting) options_for_draggable.reverteffect = function(element) {
+        element.style.top  = 0;
+        element.style.left = 0;
+      };
+
+    if(options.endeffect)
+      options_for_draggable.endeffect = options.endeffect;
+
+    if(options.zindex)
+      options_for_draggable.zindex = options.zindex;
+
+    // build options for the droppables  
+    var options_for_droppable = {
+      overlap:     options.overlap,
+      containment: options.containment,
+      hoverclass:  options.hoverclass,
+      onHover:     Sortable.onHover,
+      greedy:      !options.dropOnEmpty
+    }
+
+    // fix for gecko engine
+    Element.cleanWhitespace(element); 
+
+    options.draggables = [];
+    options.droppables = [];
+
+    // make it so
+
+    // drop on empty handling
+    if(options.dropOnEmpty) {
+      Droppables.add(element,
+        {containment: options.containment, onHover: Sortable.onEmptyHover, greedy: false});
+      options.droppables.push(element);
+    }
+
+    (this.findElements(element, options) || []).each( function(e) {
+      // handles are per-draggable
+      var handle = options.handle ? 
+        Element.childrenWithClassName(e, options.handle)[0] : e;    
+      options.draggables.push(
+        new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
+      Droppables.add(e, options_for_droppable);
+      options.droppables.push(e);      
+    });
+
+    // keep reference
+    this.sortables.push(options);
+
+    // for onupdate
+    Draggables.addObserver(new SortableObserver(element, options.onUpdate));
+
+  },
+
+  // return all suitable-for-sortable elements in a guaranteed order
+  findElements: function(element, options) {
+    if(!element.hasChildNodes()) return null;
+    var elements = [];
+    $A(element.childNodes).each( function(e) {
+      if(e.tagName && e.tagName.toUpperCase()==options.tag.toUpperCase() &&
+        (!options.only || (Element.hasClassName(e, options.only))))
+          elements.push(e);
+      if(options.tree) {
+        var grandchildren = this.findElements(e, options);
+        if(grandchildren) elements.push(grandchildren);
+      }
+    });
+
+    return (elements.length>0 ? elements.flatten() : null);
+  },
+
+  onHover: function(element, dropon, overlap) {
+    if(overlap>0.5) {
+      Sortable.mark(dropon, 'before');
+      if(dropon.previousSibling != element) {
+        var oldParentNode = element.parentNode;
+        element.style.visibility = "hidden"; // fix gecko rendering
+        dropon.parentNode.insertBefore(element, dropon);
+        if(dropon.parentNode!=oldParentNode) 
+          Sortable.options(oldParentNode).onChange(element);
+        Sortable.options(dropon.parentNode).onChange(element);
+      }
+    } else {
+      Sortable.mark(dropon, 'after');
+      var nextElement = dropon.nextSibling || null;
+      if(nextElement != element) {
+        var oldParentNode = element.parentNode;
+        element.style.visibility = "hidden"; // fix gecko rendering
+        dropon.parentNode.insertBefore(element, nextElement);
+        if(dropon.parentNode!=oldParentNode) 
+          Sortable.options(oldParentNode).onChange(element);
+        Sortable.options(dropon.parentNode).onChange(element);
+      }
+    }
+  },
+
+  onEmptyHover: function(element, dropon) {
+    if(element.parentNode!=dropon) {
+      var oldParentNode = element.parentNode;
+      dropon.appendChild(element);
+      Sortable.options(oldParentNode).onChange(element);
+      Sortable.options(dropon).onChange(element);
+    }
+  },
+
+  unmark: function() {
+    if(Sortable._marker) Element.hide(Sortable._marker);
+  },
+
+  mark: function(dropon, position) {
+    // mark on ghosting only
+    var sortable = Sortable.options(dropon.parentNode);
+    if(sortable && !sortable.ghosting) return; 
+
+    if(!Sortable._marker) {
+      Sortable._marker = $('dropmarker') || document.createElement('DIV');
+      Element.hide(Sortable._marker);
+      Element.addClassName(Sortable._marker, 'dropmarker');
+      Sortable._marker.style.position = 'absolute';
+      document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
+    }    
+    var offsets = Position.cumulativeOffset(dropon);
+    Sortable._marker.style.left = offsets[0] + 'px';
+    Sortable._marker.style.top = offsets[1] + 'px';
+    
+    if(position=='after')
+      if(sortable.overlap == 'horizontal') 
+        Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px';
+      else
+        Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
+    
+    Element.show(Sortable._marker);
+  },
+
+  serialize: function(element) {
+    element = $(element);
+    var sortableOptions = this.options(element);
+    var options = Object.extend({
+      tag:  sortableOptions.tag,
+      only: sortableOptions.only,
+      name: element.id,
+      format: sortableOptions.format || /^[^_]*_(.*)$/
+    }, arguments[1] || {});
+    return $(this.findElements(element, options) || []).map( function(item) {
+      return (encodeURIComponent(options.name) + "[]=" + 
+              encodeURIComponent(item.id.match(options.format) ? item.id.match(options.format)[1] : ''));
+    }).join("&");
+  }
+}
\ No newline at end of file
diff --git a/automation/admin/public/javascripts/effects.js b/automation/admin/public/javascripts/effects.js
new file mode 100644
index 0000000..414398c
--- /dev/null
+++ b/automation/admin/public/javascripts/effects.js
@@ -0,0 +1,854 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// Contributors:
+//  Justin Palmer (http://encytemedia.com/)
+//  Mark Pilgrim (http://diveintomark.org/)
+//  Martin Bialasinki
+// 
+// See scriptaculous.js for full license.  
+
+/* ------------- element ext -------------- */  
+ 
+// converts rgb() and #xxx to #xxxxxx format,  
+// returns self (or first argument) if not convertable  
+String.prototype.parseColor = function() {  
+  var color = '#';  
+  if(this.slice(0,4) == 'rgb(') {  
+    var cols = this.slice(4,this.length-1).split(',');  
+    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);  
+  } else {  
+    if(this.slice(0,1) == '#') {  
+      if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();  
+      if(this.length==7) color = this.toLowerCase();  
+    }  
+  }  
+  return(color.length==7 ? color : (arguments[0] || this));  
+}  
+
+Element.collectTextNodesIgnoreClass = function(element, ignoreclass) {  
+  var children = $(element).childNodes;  
+  var text     = '';  
+  var classtest = new RegExp('^([^ ]+ )*' + ignoreclass+ '( [^ ]+)*$','i');  
+ 
+  for (var i = 0; i < children.length; i++) {  
+    if(children[i].nodeType==3) {  
+      text+=children[i].nodeValue;  
+    } else {  
+      if((!children[i].className.match(classtest)) && children[i].hasChildNodes())  
+        text += Element.collectTextNodesIgnoreClass(children[i], ignoreclass);  
+    }  
+  }  
+ 
+  return text;
+}
+
+Element.setStyle = function(element, style) {
+  element = $(element);
+  for(k in style) element.style[k.camelize()] = style[k];
+}
+
+Element.setContentZoom = function(element, percent) {  
+  Element.setStyle(element, {fontSize: (percent/100) + 'em'});   
+  if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);  
+}
+
+Element.getOpacity = function(element){  
+  var opacity;
+  if (opacity = Element.getStyle(element, 'opacity'))  
+    return parseFloat(opacity);  
+  if (opacity = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/))  
+    if(opacity[1]) return parseFloat(opacity[1]) / 100;  
+  return 1.0;  
+}
+
+Element.setOpacity = function(element, value){  
+  element= $(element);  
+  if (value == 1){
+    Element.setStyle(element, { opacity: 
+      (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 
+      0.999999 : null });
+    if(/MSIE/.test(navigator.userAgent))  
+      Element.setStyle(element, {filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});  
+  } else {  
+    if(value < 0.00001) value = 0;  
+    Element.setStyle(element, {opacity: value});
+    if(/MSIE/.test(navigator.userAgent))  
+     Element.setStyle(element, 
+       { filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') +
+                 'alpha(opacity='+value*100+')' });  
+  }   
+}  
+ 
+Element.getInlineOpacity = function(element){  
+  return $(element).style.opacity || '';
+}  
+
+Element.childrenWithClassName = function(element, className) {  
+  return $A($(element).getElementsByTagName('*')).select(
+    function(c) { return Element.hasClassName(c, className) });
+}
+
+Array.prototype.call = function() {
+  var args = arguments;
+  this.each(function(f){ f.apply(this, args) });
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Effect = {
+  tagifyText: function(element) {
+    var tagifyStyle = 'position:relative';
+    if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ';zoom:1';
+    element = $(element);
+    $A(element.childNodes).each( function(child) {
+      if(child.nodeType==3) {
+        child.nodeValue.toArray().each( function(character) {
+          element.insertBefore(
+            Builder.node('span',{style: tagifyStyle},
+              character == ' ' ? String.fromCharCode(160) : character), 
+              child);
+        });
+        Element.remove(child);
+      }
+    });
+  },
+  multiple: function(element, effect) {
+    var elements;
+    if(((typeof element == 'object') || 
+        (typeof element == 'function')) && 
+       (element.length))
+      elements = element;
+    else
+      elements = $(element).childNodes;
+      
+    var options = Object.extend({
+      speed: 0.1,
+      delay: 0.0
+    }, arguments[2] || {});
+    var masterDelay = options.delay;
+
+    $A(elements).each( function(element, index) {
+      new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
+    });
+  }
+};
+
+var Effect2 = Effect; // deprecated
+
+/* ------------- transitions ------------- */
+
+Effect.Transitions = {}
+
+Effect.Transitions.linear = function(pos) {
+  return pos;
+}
+Effect.Transitions.sinoidal = function(pos) {
+  return (-Math.cos(pos*Math.PI)/2) + 0.5;
+}
+Effect.Transitions.reverse  = function(pos) {
+  return 1-pos;
+}
+Effect.Transitions.flicker = function(pos) {
+  return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
+}
+Effect.Transitions.wobble = function(pos) {
+  return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
+}
+Effect.Transitions.pulse = function(pos) {
+  return (Math.floor(pos*10) % 2 == 0 ? 
+    (pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10)));
+}
+Effect.Transitions.none = function(pos) {
+  return 0;
+}
+Effect.Transitions.full = function(pos) {
+  return 1;
+}
+
+/* ------------- core effects ------------- */
+
+Effect.Queue = {
+  effects:  [],
+  _each: function(iterator) {
+    this.effects._each(iterator);
+  },
+  interval: null,
+  add: function(effect) {
+    var timestamp = new Date().getTime();
+    
+    switch(effect.options.queue) {
+      case 'front':
+        // move unstarted effects after this effect  
+        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
+            e.startOn  += effect.finishOn;
+            e.finishOn += effect.finishOn;
+          });
+        break;
+      case 'end':
+        // start effect after last queued effect has finished
+        timestamp = this.effects.pluck('finishOn').max() || timestamp;
+        break;
+    }
+    
+    effect.startOn  += timestamp;
+    effect.finishOn += timestamp;
+    this.effects.push(effect);
+    if(!this.interval) 
+      this.interval = setInterval(this.loop.bind(this), 40);
+  },
+  remove: function(effect) {
+    this.effects = this.effects.reject(function(e) { return e==effect });
+    if(this.effects.length == 0) {
+      clearInterval(this.interval);
+      this.interval = null;
+    }
+  },
+  loop: function() {
+    var timePos = new Date().getTime();
+    this.effects.invoke('loop', timePos);
+  }
+}
+Object.extend(Effect.Queue, Enumerable);
+
+Effect.Base = function() {};
+Effect.Base.prototype = {
+  position: null,
+  setOptions: function(options) {
+    this.options = Object.extend({
+      transition: Effect.Transitions.sinoidal,
+      duration:   1.0,   // seconds
+      fps:        25.0,  // max. 25fps due to Effect.Queue implementation
+      sync:       false, // true for combining
+      from:       0.0,
+      to:         1.0,
+      delay:      0.0,
+      queue:      'parallel'
+    }, options || {});
+  },
+  start: function(options) {
+    this.setOptions(options || {});
+    this.currentFrame = 0;
+    this.state        = 'idle';
+    this.startOn      = this.options.delay*1000;
+    this.finishOn     = this.startOn + (this.options.duration*1000);
+    this.event('beforeStart');
+    if(!this.options.sync) Effect.Queue.add(this);
+  },
+  loop: function(timePos) {
+    if(timePos >= this.startOn) {
+      if(timePos >= this.finishOn) {
+        this.render(1.0);
+        this.cancel();
+        this.event('beforeFinish');
+        if(this.finish) this.finish(); 
+        this.event('afterFinish');
+        return;  
+      }
+      var pos   = (timePos - this.startOn) / (this.finishOn - this.startOn);
+      var frame = Math.round(pos * this.options.fps * this.options.duration);
+      if(frame > this.currentFrame) {
+        this.render(pos);
+        this.currentFrame = frame;
+      }
+    }
+  },
+  render: function(pos) {
+    if(this.state == 'idle') {
+      this.state = 'running';
+      this.event('beforeSetup');
+      if(this.setup) this.setup();
+      this.event('afterSetup');
+    }
+    if(this.state == 'running') {
+      if(this.options.transition) pos = this.options.transition(pos);
+      pos *= (this.options.to-this.options.from);
+      pos += this.options.from;
+      this.position = pos;
+      this.event('beforeUpdate');
+      if(this.update) this.update(pos);
+      this.event('afterUpdate');
+    }
+  },
+  cancel: function() {
+    if(!this.options.sync) Effect.Queue.remove(this);
+    this.state = 'finished';
+  },
+  event: function(eventName) {
+    if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
+    if(this.options[eventName]) this.options[eventName](this);
+  },
+  inspect: function() {
+    return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options).inspect() + '>';
+  }
+}
+
+Effect.Parallel = Class.create();
+Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
+  initialize: function(effects) {
+    this.effects = effects || [];
+    this.start(arguments[1]);
+  },
+  update: function(position) {
+    this.effects.invoke('render', position);
+  },
+  finish: function(position) {
+    this.effects.each( function(effect) {
+      effect.render(1.0);
+      effect.cancel();
+      effect.event('beforeFinish');
+      if(effect.finish) effect.finish(position);
+      effect.event('afterFinish');
+    });
+  }
+});
+
+Effect.Opacity = Class.create();
+Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
+  initialize: function(element) {
+    this.element = $(element);
+    // make this work on IE on elements without 'layout'
+    if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout))
+      Element.setStyle(this.element, {zoom: 1});
+    var options = Object.extend({
+      from: Element.getOpacity(this.element) || 0.0,
+      to:   1.0
+    }, arguments[1] || {});
+    this.start(options);
+  },
+  update: function(position) {
+    Element.setOpacity(this.element, position);
+  }
+});
+
+Effect.MoveBy = Class.create();
+Object.extend(Object.extend(Effect.MoveBy.prototype, Effect.Base.prototype), {
+  initialize: function(element, toTop, toLeft) {
+    this.element      = $(element);
+    this.toTop        = toTop;
+    this.toLeft       = toLeft;
+    this.start(arguments[3]);
+  },
+  setup: function() {
+    // Bug in Opera: Opera returns the "real" position of a static element or
+    // relative element that does not have top/left explicitly set.
+    // ==> Always set top and left for position relative elements in your stylesheets 
+    // (to 0 if you do not need them) 
+    Element.makePositioned(this.element);
+    this.originalTop  = parseFloat(Element.getStyle(this.element,'top')  || '0');
+    this.originalLeft = parseFloat(Element.getStyle(this.element,'left') || '0');
+  },
+  update: function(position) {
+    Element.setStyle(this.element, {
+      top:  this.toTop  * position + this.originalTop + 'px',
+      left: this.toLeft * position + this.originalLeft + 'px'
+    });
+  }
+});
+
+Effect.Scale = Class.create();
+Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
+  initialize: function(element, percent) {
+    this.element = $(element)
+    var options = Object.extend({
+      scaleX: true,
+      scaleY: true,
+      scaleContent: true,
+      scaleFromCenter: false,
+      scaleMode: 'box',        // 'box' or 'contents' or {} with provided values
+      scaleFrom: 100.0,
+      scaleTo:   percent
+    }, arguments[2] || {});
+    this.start(options);
+  },
+  setup: function() {
+    this.restoreAfterFinish = this.options.restoreAfterFinish || false;
+    this.elementPositioning = Element.getStyle(this.element,'position');
+    
+    this.originalStyle = {};
+    ['top','left','width','height','fontSize'].each( function(k) {
+      this.originalStyle[k] = this.element.style[k];
+    }.bind(this));
+      
+    this.originalTop  = this.element.offsetTop;
+    this.originalLeft = this.element.offsetLeft;
+    
+    var fontSize = Element.getStyle(this.element,'font-size') || '100%';
+    ['em','px','%'].each( function(fontSizeType) {
+      if(fontSize.indexOf(fontSizeType)>0) {
+        this.fontSize     = parseFloat(fontSize);
+        this.fontSizeType = fontSizeType;
+      }
+    }.bind(this));
+    
+    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
+    
+    this.dims = null;
+    if(this.options.scaleMode=='box')
+      this.dims = [this.element.offsetHeight, this.element.offsetWidth];
+    if(/^content/.test(this.options.scaleMode))
+      this.dims = [this.element.scrollHeight, this.element.scrollWidth];
+    if(!this.dims)
+      this.dims = [this.options.scaleMode.originalHeight,
+                   this.options.scaleMode.originalWidth];
+  },
+  update: function(position) {
+    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
+    if(this.options.scaleContent && this.fontSize)
+      Element.setStyle(this.element, {fontSize: this.fontSize * currentScale + this.fontSizeType });
+    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
+  },
+  finish: function(position) {
+    if (this.restoreAfterFinish) Element.setStyle(this.element, this.originalStyle);
+  },
+  setDimensions: function(height, width) {
+    var d = {};
+    if(this.options.scaleX) d.width = width + 'px';
+    if(this.options.scaleY) d.height = height + 'px';
+    if(this.options.scaleFromCenter) {
+      var topd  = (height - this.dims[0])/2;
+      var leftd = (width  - this.dims[1])/2;
+      if(this.elementPositioning == 'absolute') {
+        if(this.options.scaleY) d.top = this.originalTop-topd + 'px';
+        if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
+      } else {
+        if(this.options.scaleY) d.top = -topd + 'px';
+        if(this.options.scaleX) d.left = -leftd + 'px';
+      }
+    }
+    Element.setStyle(this.element, d);
+  }
+});
+
+Effect.Highlight = Class.create();
+Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
+  initialize: function(element) {
+    this.element = $(element);
+    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
+    this.start(options);
+  },
+  setup: function() {
+    // Prevent executing on elements not in the layout flow
+    if(Element.getStyle(this.element, 'display')=='none') { this.cancel(); return; }
+    // Disable background image during the effect
+    this.oldStyle = {
+      backgroundImage: Element.getStyle(this.element, 'background-image') };
+    Element.setStyle(this.element, {backgroundImage: 'none'});
+    if(!this.options.endcolor)
+      this.options.endcolor = Element.getStyle(this.element, 'background-color').parseColor('#ffffff');
+    if(!this.options.restorecolor)
+      this.options.restorecolor = Element.getStyle(this.element, 'background-color');
+    // init color calculations
+    this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
+    this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
+  },
+  update: function(position) {
+    Element.setStyle(this.element,{backgroundColor: $R(0,2).inject('#',function(m,v,i){
+      return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
+  },
+  finish: function() {
+    Element.setStyle(this.element, Object.extend(this.oldStyle, {
+      backgroundColor: this.options.restorecolor
+    }));
+  }
+});
+
+Effect.ScrollTo = Class.create();
+Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
+  initialize: function(element) {
+    this.element = $(element);
+    this.start(arguments[1] || {});
+  },
+  setup: function() {
+    Position.prepare();
+    var offsets = Position.cumulativeOffset(this.element);
+    if(this.options.offset) offsets[1] += this.options.offset;
+    var max = window.innerHeight ? 
+      window.height - window.innerHeight :
+      document.body.scrollHeight - 
+        (document.documentElement.clientHeight ? 
+          document.documentElement.clientHeight : document.body.clientHeight);
+    this.scrollStart = Position.deltaY;
+    this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
+  },
+  update: function(position) {
+    Position.prepare();
+    window.scrollTo(Position.deltaX, 
+      this.scrollStart + (position*this.delta));
+  }
+});
+
+/* ------------- combination effects ------------- */
+
+Effect.Fade = function(element) {
+  var oldOpacity = Element.getInlineOpacity(element);
+  var options = Object.extend({
+  from: Element.getOpacity(element) || 1.0,
+  to:   0.0,
+  afterFinishInternal: function(effect) { with(Element) { 
+    if(effect.options.to!=0) return;
+    hide(effect.element);
+    setStyle(effect.element, {opacity: oldOpacity}); }}
+  }, arguments[1] || {});
+  return new Effect.Opacity(element,options);
+}
+
+Effect.Appear = function(element) {
+  var options = Object.extend({
+  from: (Element.getStyle(element, 'display') == 'none' ? 0.0 : Element.getOpacity(element) || 0.0),
+  to:   1.0,
+  beforeSetup: function(effect) { with(Element) {
+    setOpacity(effect.element, effect.options.from);
+    show(effect.element); }}
+  }, arguments[1] || {});
+  return new Effect.Opacity(element,options);
+}
+
+Effect.Puff = function(element) {
+  element = $(element);
+  var oldStyle = { opacity: Element.getInlineOpacity(element), position: Element.getStyle(element, 'position') };
+  return new Effect.Parallel(
+   [ new Effect.Scale(element, 200, 
+      { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), 
+     new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], 
+     Object.extend({ duration: 1.0, 
+      beforeSetupInternal: function(effect) { with(Element) {
+        setStyle(effect.effects[0].element, {position: 'absolute'}); }},
+      afterFinishInternal: function(effect) { with(Element) {
+         hide(effect.effects[0].element);
+         setStyle(effect.effects[0].element, oldStyle); }}
+     }, arguments[1] || {})
+   );
+}
+
+Effect.BlindUp = function(element) {
+  element = $(element);
+  Element.makeClipping(element);
+  return new Effect.Scale(element, 0, 
+    Object.extend({ scaleContent: false, 
+      scaleX: false, 
+      restoreAfterFinish: true,
+      afterFinishInternal: function(effect) { with(Element) {
+        [hide, undoClipping].call(effect.element); }} 
+    }, arguments[1] || {})
+  );
+}
+
+Effect.BlindDown = function(element) {
+  element = $(element);
+  var oldHeight = Element.getStyle(element, 'height');
+  var elementDimensions = Element.getDimensions(element);
+  return new Effect.Scale(element, 100, 
+    Object.extend({ scaleContent: false, 
+      scaleX: false,
+      scaleFrom: 0,
+      scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+      restoreAfterFinish: true,
+      afterSetup: function(effect) { with(Element) {
+        makeClipping(effect.element);
+        setStyle(effect.element, {height: '0px'});
+        show(effect.element); 
+      }},  
+      afterFinishInternal: function(effect) { with(Element) {
+        undoClipping(effect.element);
+        setStyle(effect.element, {height: oldHeight});
+      }}
+    }, arguments[1] || {})
+  );
+}
+
+Effect.SwitchOff = function(element) {
+  element = $(element);
+  var oldOpacity = Element.getInlineOpacity(element);
+  return new Effect.Appear(element, { 
+    duration: 0.4,
+    from: 0,
+    transition: Effect.Transitions.flicker,
+    afterFinishInternal: function(effect) {
+      new Effect.Scale(effect.element, 1, { 
+        duration: 0.3, scaleFromCenter: true,
+        scaleX: false, scaleContent: false, restoreAfterFinish: true,
+        beforeSetup: function(effect) { with(Element) {
+          [makePositioned,makeClipping].call(effect.element);
+        }},
+        afterFinishInternal: function(effect) { with(Element) {
+          [hide,undoClipping,undoPositioned].call(effect.element);
+          setStyle(effect.element, {opacity: oldOpacity});
+        }}
+      })
+    }
+  });
+}
+
+Effect.DropOut = function(element) {
+  element = $(element);
+  var oldStyle = {
+    top: Element.getStyle(element, 'top'),
+    left: Element.getStyle(element, 'left'),
+    opacity: Element.getInlineOpacity(element) };
+  return new Effect.Parallel(
+    [ new Effect.MoveBy(element, 100, 0, { sync: true }), 
+      new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
+    Object.extend(
+      { duration: 0.5,
+        beforeSetup: function(effect) { with(Element) {
+          makePositioned(effect.effects[0].element); }},
+        afterFinishInternal: function(effect) { with(Element) {
+          [hide, undoPositioned].call(effect.effects[0].element);
+          setStyle(effect.effects[0].element, oldStyle); }} 
+      }, arguments[1] || {}));
+}
+
+Effect.Shake = function(element) {
+  element = $(element);
+  var oldStyle = {
+    top: Element.getStyle(element, 'top'),
+    left: Element.getStyle(element, 'left') };
+  return new Effect.MoveBy(element, 0, 20, 
+    { duration: 0.05, afterFinishInternal: function(effect) {
+  new Effect.MoveBy(effect.element, 0, -40, 
+    { duration: 0.1, afterFinishInternal: function(effect) {
+  new Effect.MoveBy(effect.element, 0, 40, 
+    { duration: 0.1, afterFinishInternal: function(effect) {
+  new Effect.MoveBy(effect.element, 0, -40, 
+    { duration: 0.1, afterFinishInternal: function(effect) {
+  new Effect.MoveBy(effect.element, 0, 40, 
+    { duration: 0.1, afterFinishInternal: function(effect) {
+  new Effect.MoveBy(effect.element, 0, -20, 
+    { duration: 0.05, afterFinishInternal: function(effect) { with(Element) {
+        undoPositioned(effect.element);
+        setStyle(effect.element, oldStyle);
+  }}}) }}) }}) }}) }}) }});
+}
+
+Effect.SlideDown = function(element) {
+  element = $(element);
+  Element.cleanWhitespace(element);
+  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
+  var oldInnerBottom = Element.getStyle(element.firstChild, 'bottom');
+  var elementDimensions = Element.getDimensions(element);
+  return new Effect.Scale(element, 100, Object.extend({ 
+    scaleContent: false, 
+    scaleX: false, 
+    scaleFrom: 0,
+    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+    restoreAfterFinish: true,
+    afterSetup: function(effect) { with(Element) {
+      makePositioned(effect.element);
+      makePositioned(effect.element.firstChild);
+      if(window.opera) setStyle(effect.element, {top: ''});
+      makeClipping(effect.element);
+      setStyle(effect.element, {height: '0px'});
+      show(element); }},
+    afterUpdateInternal: function(effect) { with(Element) {
+      setStyle(effect.element.firstChild, {bottom:
+        (effect.dims[0] - effect.element.clientHeight) + 'px' }); }},
+    afterFinishInternal: function(effect) { with(Element) {
+      undoClipping(effect.element); 
+      undoPositioned(effect.element.firstChild);
+      undoPositioned(effect.element);
+      setStyle(effect.element.firstChild, {bottom: oldInnerBottom}); }}
+    }, arguments[1] || {})
+  );
+}
+  
+Effect.SlideUp = function(element) {
+  element = $(element);
+  Element.cleanWhitespace(element);
+  var oldInnerBottom = Element.getStyle(element.firstChild, 'bottom');
+  return new Effect.Scale(element, 0, 
+   Object.extend({ scaleContent: false, 
+    scaleX: false, 
+    scaleMode: 'box',
+    scaleFrom: 100,
+    restoreAfterFinish: true,
+    beforeStartInternal: function(effect) { with(Element) {
+      makePositioned(effect.element);
+      makePositioned(effect.element.firstChild);
+      if(window.opera) setStyle(effect.element, {top: ''});
+      makeClipping(effect.element);
+      show(element); }},  
+    afterUpdateInternal: function(effect) { with(Element) {
+      setStyle(effect.element.firstChild, {bottom:
+        (effect.dims[0] - effect.element.clientHeight) + 'px' }); }},
+    afterFinishInternal: function(effect) { with(Element) {
+        [hide, undoClipping].call(effect.element); 
+        undoPositioned(effect.element.firstChild);
+        undoPositioned(effect.element);
+        setStyle(effect.element.firstChild, {bottom: oldInnerBottom}); }}
+   }, arguments[1] || {})
+  );
+}
+
+// Bug in opera makes the TD containing this element expand for a instance after finish 
+Effect.Squish = function(element) {
+  return new Effect.Scale(element, window.opera ? 1 : 0, 
+    { restoreAfterFinish: true,
+      beforeSetup: function(effect) { with(Element) {
+        makeClipping(effect.element); }},  
+      afterFinishInternal: function(effect) { with(Element) {
+        hide(effect.element); 
+        undoClipping(effect.element); }}
+  });
+}
+
+Effect.Grow = function(element) {
+  element = $(element);
+  var options = Object.extend({
+    direction: 'center',
+    moveTransistion: Effect.Transitions.sinoidal,
+    scaleTransition: Effect.Transitions.sinoidal,
+    opacityTransition: Effect.Transitions.full
+  }, arguments[1] || {});
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    height: element.style.height,
+    width: element.style.width,
+    opacity: Element.getInlineOpacity(element) };
+
+  var dims = Element.getDimensions(element);    
+  var initialMoveX, initialMoveY;
+  var moveX, moveY;
+  
+  switch (options.direction) {
+    case 'top-left':
+      initialMoveX = initialMoveY = moveX = moveY = 0; 
+      break;
+    case 'top-right':
+      initialMoveX = dims.width;
+      initialMoveY = moveY = 0;
+      moveX = -dims.width;
+      break;
+    case 'bottom-left':
+      initialMoveX = moveX = 0;
+      initialMoveY = dims.height;
+      moveY = -dims.height;
+      break;
+    case 'bottom-right':
+      initialMoveX = dims.width;
+      initialMoveY = dims.height;
+      moveX = -dims.width;
+      moveY = -dims.height;
+      break;
+    case 'center':
+      initialMoveX = dims.width / 2;
+      initialMoveY = dims.height / 2;
+      moveX = -dims.width / 2;
+      moveY = -dims.height / 2;
+      break;
+  }
+  
+  return new Effect.MoveBy(element, initialMoveY, initialMoveX, { 
+    duration: 0.01, 
+    beforeSetup: function(effect) { with(Element) {
+      hide(effect.element);
+      makeClipping(effect.element);
+      makePositioned(effect.element);
+    }},
+    afterFinishInternal: function(effect) {
+      new Effect.Parallel(
+        [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
+          new Effect.MoveBy(effect.element, moveY, moveX, { sync: true, transition: options.moveTransition }),
+          new Effect.Scale(effect.element, 100, {
+            scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, 
+            sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
+        ], Object.extend({
+             beforeSetup: function(effect) { with(Element) {
+               setStyle(effect.effects[0].element, {height: '0px'});
+               show(effect.effects[0].element); }},
+             afterFinishInternal: function(effect) { with(Element) {
+               [undoClipping, undoPositioned].call(effect.effects[0].element); 
+               setStyle(effect.effects[0].element, oldStyle); }}
+           }, options)
+      )
+    }
+  });
+}
+
+Effect.Shrink = function(element) {
+  element = $(element);
+  var options = Object.extend({
+    direction: 'center',
+    moveTransistion: Effect.Transitions.sinoidal,
+    scaleTransition: Effect.Transitions.sinoidal,
+    opacityTransition: Effect.Transitions.none
+  }, arguments[1] || {});
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    height: element.style.height,
+    width: element.style.width,
+    opacity: Element.getInlineOpacity(element) };
+
+  var dims = Element.getDimensions(element);
+  var moveX, moveY;
+  
+  switch (options.direction) {
+    case 'top-left':
+      moveX = moveY = 0;
+      break;
+    case 'top-right':
+      moveX = dims.width;
+      moveY = 0;
+      break;
+    case 'bottom-left':
+      moveX = 0;
+      moveY = dims.height;
+      break;
+    case 'bottom-right':
+      moveX = dims.width;
+      moveY = dims.height;
+      break;
+    case 'center':  
+      moveX = dims.width / 2;
+      moveY = dims.height / 2;
+      break;
+  }
+  
+  return new Effect.Parallel(
+    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
+      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
+      new Effect.MoveBy(element, moveY, moveX, { sync: true, transition: options.moveTransition })
+    ], Object.extend({            
+         beforeStartInternal: function(effect) { with(Element) {
+           [makePositioned, makeClipping].call(effect.effects[0].element) }},
+         afterFinishInternal: function(effect) { with(Element) {
+           [hide, undoClipping, undoPositioned].call(effect.effects[0].element);
+           setStyle(effect.effects[0].element, oldStyle); }}
+       }, options)
+  );
+}
+
+Effect.Pulsate = function(element) {
+  element = $(element);
+  var options    = arguments[1] || {};
+  var oldOpacity = Element.getInlineOpacity(element);
+  var transition = options.transition || Effect.Transitions.sinoidal;
+  var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) };
+  reverser.bind(transition);
+  return new Effect.Opacity(element, 
+    Object.extend(Object.extend({  duration: 3.0, from: 0,
+      afterFinishInternal: function(effect) { Element.setStyle(effect.element, {opacity: oldOpacity}); }
+    }, options), {transition: reverser}));
+}
+
+Effect.Fold = function(element) {
+  element = $(element);
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    width: element.style.width,
+    height: element.style.height };
+  Element.makeClipping(element);
+  return new Effect.Scale(element, 5, Object.extend({   
+    scaleContent: false,
+    scaleX: false,
+    afterFinishInternal: function(effect) {
+    new Effect.Scale(element, 1, { 
+      scaleContent: false, 
+      scaleY: false,
+      afterFinishInternal: function(effect) { with(Element) {
+        [hide, undoClipping].call(effect.element); 
+        setStyle(effect.element, oldStyle);
+      }} });
+  }}, arguments[1] || {}));
+}
diff --git a/automation/admin/public/javascripts/prototype.js b/automation/admin/public/javascripts/prototype.js
new file mode 100644
index 0000000..e9ccd3c
--- /dev/null
+++ b/automation/admin/public/javascripts/prototype.js
@@ -0,0 +1,1785 @@
+/*  Prototype JavaScript framework, version 1.4.0
+ *  (c) 2005 Sam Stephenson <sam at conio.net>
+ *
+ *  THIS FILE IS AUTOMATICALLY GENERATED. When sending patches, please diff
+ *  against the source tree, available from the Prototype darcs repository.
+ *
+ *  Prototype is freely distributable under the terms of an MIT-style license.
+ *
+ *  For details, see the Prototype web site: http://prototype.conio.net/
+ *
+/*--------------------------------------------------------------------------*/
+
+var Prototype = {
+  Version: '1.4.0',
+  ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
+
+  emptyFunction: function() {},
+  K: function(x) {return x}
+}
+
+var Class = {
+  create: function() {
+    return function() {
+      this.initialize.apply(this, arguments);
+    }
+  }
+}
+
+var Abstract = new Object();
+
+Object.extend = function(destination, source) {
+  for (property in source) {
+    destination[property] = source[property];
+  }
+  return destination;
+}
+
+Object.inspect = function(object) {
+  try {
+    if (object == undefined) return 'undefined';
+    if (object == null) return 'null';
+    return object.inspect ? object.inspect() : object.toString();
+  } catch (e) {
+    if (e instanceof RangeError) return '...';
+    throw e;
+  }
+}
+
+Function.prototype.bind = function() {
+  var __method = this, args = $A(arguments), object = args.shift();
+  return function() {
+    return __method.apply(object, args.concat($A(arguments)));
+  }
+}
+
+Function.prototype.bindAsEventListener = function(object) {
+  var __method = this;
+  return function(event) {
+    return __method.call(object, event || window.event);
+  }
+}
+
+Object.extend(Number.prototype, {
+  toColorPart: function() {
+    var digits = this.toString(16);
+    if (this < 16) return '0' + digits;
+    return digits;
+  },
+
+  succ: function() {
+    return this + 1;
+  },
+
+  times: function(iterator) {
+    $R(0, this, true).each(iterator);
+    return this;
+  }
+});
+
+var Try = {
+  these: function() {
+    var returnValue;
+
+    for (var i = 0; i < arguments.length; i++) {
+      var lambda = arguments[i];
+      try {
+        returnValue = lambda();
+        break;
+      } catch (e) {}
+    }
+
+    return returnValue;
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create();
+PeriodicalExecuter.prototype = {
+  initialize: function(callback, frequency) {
+    this.callback = callback;
+    this.frequency = frequency;
+    this.currentlyExecuting = false;
+
+    this.registerCallback();
+  },
+
+  registerCallback: function() {
+    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+
+  onTimerEvent: function() {
+    if (!this.currentlyExecuting) {
+      try {
+        this.currentlyExecuting = true;
+        this.callback();
+      } finally {
+        this.currentlyExecuting = false;
+      }
+    }
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+function $() {
+  var elements = new Array();
+
+  for (var i = 0; i < arguments.length; i++) {
+    var element = arguments[i];
+    if (typeof element == 'string')
+      element = document.getElementById(element);
+
+    if (arguments.length == 1)
+      return element;
+
+    elements.push(element);
+  }
+
+  return elements;
+}
+Object.extend(String.prototype, {
+  stripTags: function() {
+    return this.replace(/<\/?[^>]+>/gi, '');
+  },
+
+  stripScripts: function() {
+    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+  },
+
+  extractScripts: function() {
+    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+    return (this.match(matchAll) || []).map(function(scriptTag) {
+      return (scriptTag.match(matchOne) || ['', ''])[1];
+    });
+  },
+
+  evalScripts: function() {
+    return this.extractScripts().map(eval);
+  },
+
+  escapeHTML: function() {
+    var div = document.createElement('div');
+    var text = document.createTextNode(this);
+    div.appendChild(text);
+    return div.innerHTML;
+  },
+
+  unescapeHTML: function() {
+    var div = document.createElement('div');
+    div.innerHTML = this.stripTags();
+    return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
+  },
+
+  toQueryParams: function() {
+    var pairs = this.match(/^\??(.*)$/)[1].split('&');
+    return pairs.inject({}, function(params, pairString) {
+      var pair = pairString.split('=');
+      params[pair[0]] = pair[1];
+      return params;
+    });
+  },
+
+  toArray: function() {
+    return this.split('');
+  },
+
+  camelize: function() {
+    var oStringList = this.split('-');
+    if (oStringList.length == 1) return oStringList[0];
+
+    var camelizedString = this.indexOf('-') == 0
+      ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
+      : oStringList[0];
+
+    for (var i = 1, len = oStringList.length; i < len; i++) {
+      var s = oStringList[i];
+      camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
+    }
+
+    return camelizedString;
+  },
+
+  inspect: function() {
+    return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'";
+  }
+});
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+var $break    = new Object();
+var $continue = new Object();
+
+var Enumerable = {
+  each: function(iterator) {
+    var index = 0;
+    try {
+      this._each(function(value) {
+        try {
+          iterator(value, index++);
+        } catch (e) {
+          if (e != $continue) throw e;
+        }
+      });
+    } catch (e) {
+      if (e != $break) throw e;
+    }
+  },
+
+  all: function(iterator) {
+    var result = true;
+    this.each(function(value, index) {
+      result = result && !!(iterator || Prototype.K)(value, index);
+      if (!result) throw $break;
+    });
+    return result;
+  },
+
+  any: function(iterator) {
+    var result = true;
+    this.each(function(value, index) {
+      if (result = !!(iterator || Prototype.K)(value, index))
+        throw $break;
+    });
+    return result;
+  },
+
+  collect: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      results.push(iterator(value, index));
+    });
+    return results;
+  },
+
+  detect: function (iterator) {
+    var result;
+    this.each(function(value, index) {
+      if (iterator(value, index)) {
+        result = value;
+        throw $break;
+      }
+    });
+    return result;
+  },
+
+  findAll: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      if (iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  grep: function(pattern, iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      var stringValue = value.toString();
+      if (stringValue.match(pattern))
+        results.push((iterator || Prototype.K)(value, index));
+    })
+    return results;
+  },
+
+  include: function(object) {
+    var found = false;
+    this.each(function(value) {
+      if (value == object) {
+        found = true;
+        throw $break;
+      }
+    });
+    return found;
+  },
+
+  inject: function(memo, iterator) {
+    this.each(function(value, index) {
+      memo = iterator(memo, value, index);
+    });
+    return memo;
+  },
+
+  invoke: function(method) {
+    var args = $A(arguments).slice(1);
+    return this.collect(function(value) {
+      return value[method].apply(value, args);
+    });
+  },
+
+  max: function(iterator) {
+    var result;
+    this.each(function(value, index) {
+      value = (iterator || Prototype.K)(value, index);
+      if (value >= (result || value))
+        result = value;
+    });
+    return result;
+  },
+
+  min: function(iterator) {
+    var result;
+    this.each(function(value, index) {
+      value = (iterator || Prototype.K)(value, index);
+      if (value <= (result || value))
+        result = value;
+    });
+    return result;
+  },
+
+  partition: function(iterator) {
+    var trues = [], falses = [];
+    this.each(function(value, index) {
+      ((iterator || Prototype.K)(value, index) ?
+        trues : falses).push(value);
+    });
+    return [trues, falses];
+  },
+
+  pluck: function(property) {
+    var results = [];
+    this.each(function(value, index) {
+      results.push(value[property]);
+    });
+    return results;
+  },
+
+  reject: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      if (!iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  sortBy: function(iterator) {
+    return this.collect(function(value, index) {
+      return {value: value, criteria: iterator(value, index)};
+    }).sort(function(left, right) {
+      var a = left.criteria, b = right.criteria;
+      return a < b ? -1 : a > b ? 1 : 0;
+    }).pluck('value');
+  },
+
+  toArray: function() {
+    return this.collect(Prototype.K);
+  },
+
+  zip: function() {
+    var iterator = Prototype.K, args = $A(arguments);
+    if (typeof args.last() == 'function')
+      iterator = args.pop();
+
+    var collections = [this].concat(args).map($A);
+    return this.map(function(value, index) {
+      iterator(value = collections.pluck(index));
+      return value;
+    });
+  },
+
+  inspect: function() {
+    return '#<Enumerable:' + this.toArray().inspect() + '>';
+  }
+}
+
+Object.extend(Enumerable, {
+  map:     Enumerable.collect,
+  find:    Enumerable.detect,
+  select:  Enumerable.findAll,
+  member:  Enumerable.include,
+  entries: Enumerable.toArray
+});
+var $A = Array.from = function(iterable) {
+  if (!iterable) return [];
+  if (iterable.toArray) {
+    return iterable.toArray();
+  } else {
+    var results = [];
+    for (var i = 0; i < iterable.length; i++)
+      results.push(iterable[i]);
+    return results;
+  }
+}
+
+Object.extend(Array.prototype, Enumerable);
+
+Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+  _each: function(iterator) {
+    for (var i = 0; i < this.length; i++)
+      iterator(this[i]);
+  },
+
+  clear: function() {
+    this.length = 0;
+    return this;
+  },
+
+  first: function() {
+    return this[0];
+  },
+
+  last: function() {
+    return this[this.length - 1];
+  },
+
+  compact: function() {
+    return this.select(function(value) {
+      return value != undefined || value != null;
+    });
+  },
+
+  flatten: function() {
+    return this.inject([], function(array, value) {
+      return array.concat(value.constructor == Array ?
+        value.flatten() : [value]);
+    });
+  },
+
+  without: function() {
+    var values = $A(arguments);
+    return this.select(function(value) {
+      return !values.include(value);
+    });
+  },
+
+  indexOf: function(object) {
+    for (var i = 0; i < this.length; i++)
+      if (this[i] == object) return i;
+    return -1;
+  },
+
+  reverse: function(inline) {
+    return (inline !== false ? this : this.toArray())._reverse();
+  },
+
+  shift: function() {
+    var result = this[0];
+    for (var i = 0; i < this.length - 1; i++)
+      this[i] = this[i + 1];
+    this.length--;
+    return result;
+  },
+
+  inspect: function() {
+    return '[' + this.map(Object.inspect).join(', ') + ']';
+  }
+});
+var Hash = {
+  _each: function(iterator) {
+    for (key in this) {
+      var value = this[key];
+      if (typeof value == 'function') continue;
+
+      var pair = [key, value];
+      pair.key = key;
+      pair.value = value;
+      iterator(pair);
+    }
+  },
+
+  keys: function() {
+    return this.pluck('key');
+  },
+
+  values: function() {
+    return this.pluck('value');
+  },
+
+  merge: function(hash) {
+    return $H(hash).inject($H(this), function(mergedHash, pair) {
+      mergedHash[pair.key] = pair.value;
+      return mergedHash;
+    });
+  },
+
+  toQueryString: function() {
+    return this.map(function(pair) {
+      return pair.map(encodeURIComponent).join('=');
+    }).join('&');
+  },
+
+  inspect: function() {
+    return '#<Hash:{' + this.map(function(pair) {
+      return pair.map(Object.inspect).join(': ');
+    }).join(', ') + '}>';
+  }
+}
+
+function $H(object) {
+  var hash = Object.extend({}, object || {});
+  Object.extend(hash, Enumerable);
+  Object.extend(hash, Hash);
+  return hash;
+}
+ObjectRange = Class.create();
+Object.extend(ObjectRange.prototype, Enumerable);
+Object.extend(ObjectRange.prototype, {
+  initialize: function(start, end, exclusive) {
+    this.start = start;
+    this.end = end;
+    this.exclusive = exclusive;
+  },
+
+  _each: function(iterator) {
+    var value = this.start;
+    do {
+      iterator(value);
+      value = value.succ();
+    } while (this.include(value));
+  },
+
+  include: function(value) {
+    if (value < this.start)
+      return false;
+    if (this.exclusive)
+      return value < this.end;
+    return value <= this.end;
+  }
+});
+
+var $R = function(start, end, exclusive) {
+  return new ObjectRange(start, end, exclusive);
+}
+
+var Ajax = {
+  getTransport: function() {
+    return Try.these(
+      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+      function() {return new ActiveXObject('Microsoft.XMLHTTP')},
+      function() {return new XMLHttpRequest()}
+    ) || false;
+  },
+
+  activeRequestCount: 0
+}
+
+Ajax.Responders = {
+  responders: [],
+
+  _each: function(iterator) {
+    this.responders._each(iterator);
+  },
+
+  register: function(responderToAdd) {
+    if (!this.include(responderToAdd))
+      this.responders.push(responderToAdd);
+  },
+
+  unregister: function(responderToRemove) {
+    this.responders = this.responders.without(responderToRemove);
+  },
+
+  dispatch: function(callback, request, transport, json) {
+    this.each(function(responder) {
+      if (responder[callback] && typeof responder[callback] == 'function') {
+        try {
+          responder[callback].apply(responder, [request, transport, json]);
+        } catch (e) {}
+      }
+    });
+  }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+  onCreate: function() {
+    Ajax.activeRequestCount++;
+  },
+
+  onComplete: function() {
+    Ajax.activeRequestCount--;
+  }
+});
+
+Ajax.Base = function() {};
+Ajax.Base.prototype = {
+  setOptions: function(options) {
+    this.options = {
+      method:       'post',
+      asynchronous: true,
+      parameters:   ''
+    }
+    Object.extend(this.options, options || {});
+  },
+
+  responseIsSuccess: function() {
+    return this.transport.status == undefined
+        || this.transport.status == 0
+        || (this.transport.status >= 200 && this.transport.status < 300);
+  },
+
+  responseIsFailure: function() {
+    return !this.responseIsSuccess();
+  }
+}
+
+Ajax.Request = Class.create();
+Ajax.Request.Events =
+  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
+  initialize: function(url, options) {
+    this.transport = Ajax.getTransport();
+    this.setOptions(options);
+    this.request(url);
+  },
+
+  request: function(url) {
+    var parameters = this.options.parameters || '';
+    if (parameters.length > 0) parameters += '&_=';
+
+    try {
+      this.url = url;
+      if (this.options.method == 'get' && parameters.length > 0)
+        this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
+
+      Ajax.Responders.dispatch('onCreate', this, this.transport);
+
+      this.transport.open(this.options.method, this.url,
+        this.options.asynchronous);
+
+      if (this.options.asynchronous) {
+        this.transport.onreadystatechange = this.onStateChange.bind(this);
+        setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
+      }
+
+      this.setRequestHeaders();
+
+      var body = this.options.postBody ? this.options.postBody : parameters;
+      this.transport.send(this.options.method == 'post' ? body : null);
+
+    } catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  setRequestHeaders: function() {
+    var requestHeaders =
+      ['X-Requested-With', 'XMLHttpRequest',
+       'X-Prototype-Version', Prototype.Version];
+
+    if (this.options.method == 'post') {
+      requestHeaders.push('Content-type',
+        'application/x-www-form-urlencoded');
+
+      /* Force "Connection: close" for Mozilla browsers to work around
+       * a bug where XMLHttpReqeuest sends an incorrect Content-length
+       * header. See Mozilla Bugzilla #246651.
+       */
+      if (this.transport.overrideMimeType)
+        requestHeaders.push('Connection', 'close');
+    }
+
+    if (this.options.requestHeaders)
+      requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
+
+    for (var i = 0; i < requestHeaders.length; i += 2)
+      this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
+  },
+
+  onStateChange: function() {
+    var readyState = this.transport.readyState;
+    if (readyState != 1)
+      this.respondToReadyState(this.transport.readyState);
+  },
+
+  header: function(name) {
+    try {
+      return this.transport.getResponseHeader(name);
+    } catch (e) {}
+  },
+
+  evalJSON: function() {
+    try {
+      return eval(this.header('X-JSON'));
+    } catch (e) {}
+  },
+
+  evalResponse: function() {
+    try {
+      return eval(this.transport.responseText);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  respondToReadyState: function(readyState) {
+    var event = Ajax.Request.Events[readyState];
+    var transport = this.transport, json = this.evalJSON();
+
+    if (event == 'Complete') {
+      try {
+        (this.options['on' + this.transport.status]
+         || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
+         || Prototype.emptyFunction)(transport, json);
+      } catch (e) {
+        this.dispatchException(e);
+      }
+
+      if ((this.header('Content-type') || '').match(/^text\/javascript/i))
+        this.evalResponse();
+    }
+
+    try {
+      (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
+      Ajax.Responders.dispatch('on' + event, this, transport, json);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+
+    /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
+    if (event == 'Complete')
+      this.transport.onreadystatechange = Prototype.emptyFunction;
+  },
+
+  dispatchException: function(exception) {
+    (this.options.onException || Prototype.emptyFunction)(this, exception);
+    Ajax.Responders.dispatch('onException', this, exception);
+  }
+});
+
+Ajax.Updater = Class.create();
+
+Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
+  initialize: function(container, url, options) {
+    this.containers = {
+      success: container.success ? $(container.success) : $(container),
+      failure: container.failure ? $(container.failure) :
+        (container.success ? null : $(container))
+    }
+
+    this.transport = Ajax.getTransport();
+    this.setOptions(options);
+
+    var onComplete = this.options.onComplete || Prototype.emptyFunction;
+    this.options.onComplete = (function(transport, object) {
+      this.updateContent();
+      onComplete(transport, object);
+    }).bind(this);
+
+    this.request(url);
+  },
+
+  updateContent: function() {
+    var receiver = this.responseIsSuccess() ?
+      this.containers.success : this.containers.failure;
+    var response = this.transport.responseText;
+
+    if (!this.options.evalScripts)
+      response = response.stripScripts();
+
+    if (receiver) {
+      if (this.options.insertion) {
+        new this.options.insertion(receiver, response);
+      } else {
+        Element.update(receiver, response);
+      }
+    }
+
+    if (this.responseIsSuccess()) {
+      if (this.onComplete)
+        setTimeout(this.onComplete.bind(this), 10);
+    }
+  }
+});
+
+Ajax.PeriodicalUpdater = Class.create();
+Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
+  initialize: function(container, url, options) {
+    this.setOptions(options);
+    this.onComplete = this.options.onComplete;
+
+    this.frequency = (this.options.frequency || 2);
+    this.decay = (this.options.decay || 1);
+
+    this.updater = {};
+    this.container = container;
+    this.url = url;
+
+    this.start();
+  },
+
+  start: function() {
+    this.options.onComplete = this.updateComplete.bind(this);
+    this.onTimerEvent();
+  },
+
+  stop: function() {
+    this.updater.onComplete = undefined;
+    clearTimeout(this.timer);
+    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+  },
+
+  updateComplete: function(request) {
+    if (this.options.decay) {
+      this.decay = (request.responseText == this.lastText ?
+        this.decay * this.options.decay : 1);
+
+      this.lastText = request.responseText;
+    }
+    this.timer = setTimeout(this.onTimerEvent.bind(this),
+      this.decay * this.frequency * 1000);
+  },
+
+  onTimerEvent: function() {
+    this.updater = new Ajax.Updater(this.container, this.url, this.options);
+  }
+});
+document.getElementsByClassName = function(className, parentElement) {
+  var children = ($(parentElement) || document.body).getElementsByTagName('*');
+  return $A(children).inject([], function(elements, child) {
+    if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
+      elements.push(child);
+    return elements;
+  });
+}
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Element) {
+  var Element = new Object();
+}
+
+Object.extend(Element, {
+  visible: function(element) {
+    return $(element).style.display != 'none';
+  },
+
+  toggle: function() {
+    for (var i = 0; i < arguments.length; i++) {
+      var element = $(arguments[i]);
+      Element[Element.visible(element) ? 'hide' : 'show'](element);
+    }
+  },
+
+  hide: function() {
+    for (var i = 0; i < arguments.length; i++) {
+      var element = $(arguments[i]);
+      element.style.display = 'none';
+    }
+  },
+
+  show: function() {
+    for (var i = 0; i < arguments.length; i++) {
+      var element = $(arguments[i]);
+      element.style.display = '';
+    }
+  },
+
+  remove: function(element) {
+    element = $(element);
+    element.parentNode.removeChild(element);
+  },
+
+  update: function(element, html) {
+    $(element).innerHTML = html.stripScripts();
+    setTimeout(function() {html.evalScripts()}, 10);
+  },
+
+  getHeight: function(element) {
+    element = $(element);
+    return element.offsetHeight;
+  },
+
+  classNames: function(element) {
+    return new Element.ClassNames(element);
+  },
+
+  hasClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    return Element.classNames(element).include(className);
+  },
+
+  addClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    return Element.classNames(element).add(className);
+  },
+
+  removeClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    return Element.classNames(element).remove(className);
+  },
+
+  // removes whitespace-only text node children
+  cleanWhitespace: function(element) {
+    element = $(element);
+    for (var i = 0; i < element.childNodes.length; i++) {
+      var node = element.childNodes[i];
+      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+        Element.remove(node);
+    }
+  },
+
+  empty: function(element) {
+    return $(element).innerHTML.match(/^\s*$/);
+  },
+
+  scrollTo: function(element) {
+    element = $(element);
+    var x = element.x ? element.x : element.offsetLeft,
+        y = element.y ? element.y : element.offsetTop;
+    window.scrollTo(x, y);
+  },
+
+  getStyle: function(element, style) {
+    element = $(element);
+    var value = element.style[style.camelize()];
+    if (!value) {
+      if (document.defaultView && document.defaultView.getComputedStyle) {
+        var css = document.defaultView.getComputedStyle(element, null);
+        value = css ? css.getPropertyValue(style) : null;
+      } else if (element.currentStyle) {
+        value = element.currentStyle[style.camelize()];
+      }
+    }
+
+    if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
+      if (Element.getStyle(element, 'position') == 'static') value = 'auto';
+
+    return value == 'auto' ? null : value;
+  },
+
+  setStyle: function(element, style) {
+    element = $(element);
+    for (name in style)
+      element.style[name.camelize()] = style[name];
+  },
+
+  getDimensions: function(element) {
+    element = $(element);
+    if (Element.getStyle(element, 'display') != 'none')
+      return {width: element.offsetWidth, height: element.offsetHeight};
+
+    // All *Width and *Height properties give 0 on elements with display none,
+    // so enable the element temporarily
+    var els = element.style;
+    var originalVisibility = els.visibility;
+    var originalPosition = els.position;
+    els.visibility = 'hidden';
+    els.position = 'absolute';
+    els.display = '';
+    var originalWidth = element.clientWidth;
+    var originalHeight = element.clientHeight;
+    els.display = 'none';
+    els.position = originalPosition;
+    els.visibility = originalVisibility;
+    return {width: originalWidth, height: originalHeight};
+  },
+
+  makePositioned: function(element) {
+    element = $(element);
+    var pos = Element.getStyle(element, 'position');
+    if (pos == 'static' || !pos) {
+      element._madePositioned = true;
+      element.style.position = 'relative';
+      // Opera returns the offset relative to the positioning context, when an
+      // element is position relative but top and left have not been defined
+      if (window.opera) {
+        element.style.top = 0;
+        element.style.left = 0;
+      }
+    }
+  },
+
+  undoPositioned: function(element) {
+    element = $(element);
+    if (element._madePositioned) {
+      element._madePositioned = undefined;
+      element.style.position =
+        element.style.top =
+        element.style.left =
+        element.style.bottom =
+        element.style.right = '';
+    }
+  },
+
+  makeClipping: function(element) {
+    element = $(element);
+    if (element._overflow) return;
+    element._overflow = element.style.overflow;
+    if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
+      element.style.overflow = 'hidden';
+  },
+
+  undoClipping: function(element) {
+    element = $(element);
+    if (element._overflow) return;
+    element.style.overflow = element._overflow;
+    element._overflow = undefined;
+  }
+});
+
+var Toggle = new Object();
+Toggle.display = Element.toggle;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.Insertion = function(adjacency) {
+  this.adjacency = adjacency;
+}
+
+Abstract.Insertion.prototype = {
+  initialize: function(element, content) {
+    this.element = $(element);
+    this.content = content.stripScripts();
+
+    if (this.adjacency && this.element.insertAdjacentHTML) {
+      try {
+        this.element.insertAdjacentHTML(this.adjacency, this.content);
+      } catch (e) {
+        if (this.element.tagName.toLowerCase() == 'tbody') {
+          this.insertContent(this.contentFromAnonymousTable());
+        } else {
+          throw e;
+        }
+      }
+    } else {
+      this.range = this.element.ownerDocument.createRange();
+      if (this.initializeRange) this.initializeRange();
+      this.insertContent([this.range.createContextualFragment(this.content)]);
+    }
+
+    setTimeout(function() {content.evalScripts()}, 10);
+  },
+
+  contentFromAnonymousTable: function() {
+    var div = document.createElement('div');
+    div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
+    return $A(div.childNodes[0].childNodes[0].childNodes);
+  }
+}
+
+var Insertion = new Object();
+
+Insertion.Before = Class.create();
+Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
+  initializeRange: function() {
+    this.range.setStartBefore(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.parentNode.insertBefore(fragment, this.element);
+    }).bind(this));
+  }
+});
+
+Insertion.Top = Class.create();
+Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
+  initializeRange: function() {
+    this.range.selectNodeContents(this.element);
+    this.range.collapse(true);
+  },
+
+  insertContent: function(fragments) {
+    fragments.reverse(false).each((function(fragment) {
+      this.element.insertBefore(fragment, this.element.firstChild);
+    }).bind(this));
+  }
+});
+
+Insertion.Bottom = Class.create();
+Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
+  initializeRange: function() {
+    this.range.selectNodeContents(this.element);
+    this.range.collapse(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.appendChild(fragment);
+    }).bind(this));
+  }
+});
+
+Insertion.After = Class.create();
+Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
+  initializeRange: function() {
+    this.range.setStartAfter(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.parentNode.insertBefore(fragment,
+        this.element.nextSibling);
+    }).bind(this));
+  }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+  initialize: function(element) {
+    this.element = $(element);
+  },
+
+  _each: function(iterator) {
+    this.element.className.split(/\s+/).select(function(name) {
+      return name.length > 0;
+    })._each(iterator);
+  },
+
+  set: function(className) {
+    this.element.className = className;
+  },
+
+  add: function(classNameToAdd) {
+    if (this.include(classNameToAdd)) return;
+    this.set(this.toArray().concat(classNameToAdd).join(' '));
+  },
+
+  remove: function(classNameToRemove) {
+    if (!this.include(classNameToRemove)) return;
+    this.set(this.select(function(className) {
+      return className != classNameToRemove;
+    }).join(' '));
+  },
+
+  toString: function() {
+    return this.toArray().join(' ');
+  }
+}
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+var Field = {
+  clear: function() {
+    for (var i = 0; i < arguments.length; i++)
+      $(arguments[i]).value = '';
+  },
+
+  focus: function(element) {
+    $(element).focus();
+  },
+
+  present: function() {
+    for (var i = 0; i < arguments.length; i++)
+      if ($(arguments[i]).value == '') return false;
+    return true;
+  },
+
+  select: function(element) {
+    $(element).select();
+  },
+
+  activate: function(element) {
+    element = $(element);
+    element.focus();
+    if (element.select)
+      element.select();
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Form = {
+  serialize: function(form) {
+    var elements = Form.getElements($(form));
+    var queryComponents = new Array();
+
+    for (var i = 0; i < elements.length; i++) {
+      var queryComponent = Form.Element.serialize(elements[i]);
+      if (queryComponent)
+        queryComponents.push(queryComponent);
+    }
+
+    return queryComponents.join('&');
+  },
+
+  getElements: function(form) {
+    form = $(form);
+    var elements = new Array();
+
+    for (tagName in Form.Element.Serializers) {
+      var tagElements = form.getElementsByTagName(tagName);
+      for (var j = 0; j < tagElements.length; j++)
+        elements.push(tagElements[j]);
+    }
+    return elements;
+  },
+
+  getInputs: function(form, typeName, name) {
+    form = $(form);
+    var inputs = form.getElementsByTagName('input');
+
+    if (!typeName && !name)
+      return inputs;
+
+    var matchingInputs = new Array();
+    for (var i = 0; i < inputs.length; i++) {
+      var input = inputs[i];
+      if ((typeName && input.type != typeName) ||
+          (name && input.name != name))
+        continue;
+      matchingInputs.push(input);
+    }
+
+    return matchingInputs;
+  },
+
+  disable: function(form) {
+    var elements = Form.getElements(form);
+    for (var i = 0; i < elements.length; i++) {
+      var element = elements[i];
+      element.blur();
+      element.disabled = 'true';
+    }
+  },
+
+  enable: function(form) {
+    var elements = Form.getElements(form);
+    for (var i = 0; i < elements.length; i++) {
+      var element = elements[i];
+      element.disabled = '';
+    }
+  },
+
+  findFirstElement: function(form) {
+    return Form.getElements(form).find(function(element) {
+      return element.type != 'hidden' && !element.disabled &&
+        ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+    });
+  },
+
+  focusFirstElement: function(form) {
+    Field.activate(Form.findFirstElement(form));
+  },
+
+  reset: function(form) {
+    $(form).reset();
+  }
+}
+
+Form.Element = {
+  serialize: function(element) {
+    element = $(element);
+    var method = element.tagName.toLowerCase();
+    var parameter = Form.Element.Serializers[method](element);
+
+    if (parameter) {
+      var key = encodeURIComponent(parameter[0]);
+      if (key.length == 0) return;
+
+      if (parameter[1].constructor != Array)
+        parameter[1] = [parameter[1]];
+
+      return parameter[1].map(function(value) {
+        return key + '=' + encodeURIComponent(value);
+      }).join('&');
+    }
+  },
+
+  getValue: function(element) {
+    element = $(element);
+    var method = element.tagName.toLowerCase();
+    var parameter = Form.Element.Serializers[method](element);
+
+    if (parameter)
+      return parameter[1];
+  }
+}
+
+Form.Element.Serializers = {
+  input: function(element) {
+    switch (element.type.toLowerCase()) {
+      case 'submit':
+      case 'hidden':
+      case 'password':
+      case 'text':
+        return Form.Element.Serializers.textarea(element);
+      case 'checkbox':
+      case 'radio':
+        return Form.Element.Serializers.inputSelector(element);
+    }
+    return false;
+  },
+
+  inputSelector: function(element) {
+    if (element.checked)
+      return [element.name, element.value];
+  },
+
+  textarea: function(element) {
+    return [element.name, element.value];
+  },
+
+  select: function(element) {
+    return Form.Element.Serializers[element.type == 'select-one' ?
+      'selectOne' : 'selectMany'](element);
+  },
+
+  selectOne: function(element) {
+    var value = '', opt, index = element.selectedIndex;
+    if (index >= 0) {
+      opt = element.options[index];
+      value = opt.value;
+      if (!value && !('value' in opt))
+        value = opt.text;
+    }
+    return [element.name, value];
+  },
+
+  selectMany: function(element) {
+    var value = new Array();
+    for (var i = 0; i < element.length; i++) {
+      var opt = element.options[i];
+      if (opt.selected) {
+        var optValue = opt.value;
+        if (!optValue && !('value' in opt))
+          optValue = opt.text;
+        value.push(optValue);
+      }
+    }
+    return [element.name, value];
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var $F = Form.Element.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = function() {}
+Abstract.TimedObserver.prototype = {
+  initialize: function(element, frequency, callback) {
+    this.frequency = frequency;
+    this.element   = $(element);
+    this.callback  = callback;
+
+    this.lastValue = this.getValue();
+    this.registerCallback();
+  },
+
+  registerCallback: function() {
+    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+
+  onTimerEvent: function() {
+    var value = this.getValue();
+    if (this.lastValue != value) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  }
+}
+
+Form.Element.Observer = Class.create();
+Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.Observer = Class.create();
+Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = function() {}
+Abstract.EventObserver.prototype = {
+  initialize: function(element, callback) {
+    this.element  = $(element);
+    this.callback = callback;
+
+    this.lastValue = this.getValue();
+    if (this.element.tagName.toLowerCase() == 'form')
+      this.registerFormCallbacks();
+    else
+      this.registerCallback(this.element);
+  },
+
+  onElementEvent: function() {
+    var value = this.getValue();
+    if (this.lastValue != value) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  },
+
+  registerFormCallbacks: function() {
+    var elements = Form.getElements(this.element);
+    for (var i = 0; i < elements.length; i++)
+      this.registerCallback(elements[i]);
+  },
+
+  registerCallback: function(element) {
+    if (element.type) {
+      switch (element.type.toLowerCase()) {
+        case 'checkbox':
+        case 'radio':
+          Event.observe(element, 'click', this.onElementEvent.bind(this));
+          break;
+        case 'password':
+        case 'text':
+        case 'textarea':
+        case 'select-one':
+        case 'select-multiple':
+          Event.observe(element, 'change', this.onElementEvent.bind(this));
+          break;
+      }
+    }
+  }
+}
+
+Form.Element.EventObserver = Class.create();
+Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.EventObserver = Class.create();
+Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+if (!window.Event) {
+  var Event = new Object();
+}
+
+Object.extend(Event, {
+  KEY_BACKSPACE: 8,
+  KEY_TAB:       9,
+  KEY_RETURN:   13,
+  KEY_ESC:      27,
+  KEY_LEFT:     37,
+  KEY_UP:       38,
+  KEY_RIGHT:    39,
+  KEY_DOWN:     40,
+  KEY_DELETE:   46,
+
+  element: function(event) {
+    return event.target || event.srcElement;
+  },
+
+  isLeftClick: function(event) {
+    return (((event.which) && (event.which == 1)) ||
+            ((event.button) && (event.button == 1)));
+  },
+
+  pointerX: function(event) {
+    return event.pageX || (event.clientX +
+      (document.documentElement.scrollLeft || document.body.scrollLeft));
+  },
+
+  pointerY: function(event) {
+    return event.pageY || (event.clientY +
+      (document.documentElement.scrollTop || document.body.scrollTop));
+  },
+
+  stop: function(event) {
+    if (event.preventDefault) {
+      event.preventDefault();
+      event.stopPropagation();
+    } else {
+      event.returnValue = false;
+      event.cancelBubble = true;
+    }
+  },
+
+  // find the first node with the given tagName, starting from the
+  // node the event was triggered on; traverses the DOM upwards
+  findElement: function(event, tagName) {
+    var element = Event.element(event);
+    while (element.parentNode && (!element.tagName ||
+        (element.tagName.toUpperCase() != tagName.toUpperCase())))
+      element = element.parentNode;
+    return element;
+  },
+
+  observers: false,
+
+  _observeAndCache: function(element, name, observer, useCapture) {
+    if (!this.observers) this.observers = [];
+    if (element.addEventListener) {
+      this.observers.push([element, name, observer, useCapture]);
+      element.addEventListener(name, observer, useCapture);
+    } else if (element.attachEvent) {
+      this.observers.push([element, name, observer, useCapture]);
+      element.attachEvent('on' + name, observer);
+    }
+  },
+
+  unloadCache: function() {
+    if (!Event.observers) return;
+    for (var i = 0; i < Event.observers.length; i++) {
+      Event.stopObserving.apply(this, Event.observers[i]);
+      Event.observers[i][0] = null;
+    }
+    Event.observers = false;
+  },
+
+  observe: function(element, name, observer, useCapture) {
+    var element = $(element);
+    useCapture = useCapture || false;
+
+    if (name == 'keypress' &&
+        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+        || element.attachEvent))
+      name = 'keydown';
+
+    this._observeAndCache(element, name, observer, useCapture);
+  },
+
+  stopObserving: function(element, name, observer, useCapture) {
+    var element = $(element);
+    useCapture = useCapture || false;
+
+    if (name == 'keypress' &&
+        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+        || element.detachEvent))
+      name = 'keydown';
+
+    if (element.removeEventListener) {
+      element.removeEventListener(name, observer, useCapture);
+    } else if (element.detachEvent) {
+      element.detachEvent('on' + name, observer);
+    }
+  }
+});
+
+/* prevent memory leaks in IE */
+Event.observe(window, 'unload', Event.unloadCache, false);
+var Position = {
+  // set to true if needed, warning: firefox performance problems
+  // NOT neeeded for page scrolling, only if draggable contained in
+  // scrollable elements
+  includeScrollOffsets: false,
+
+  // must be called before calling withinIncludingScrolloffset, every time the
+  // page is scrolled
+  prepare: function() {
+    this.deltaX =  window.pageXOffset
+                || document.documentElement.scrollLeft
+                || document.body.scrollLeft
+                || 0;
+    this.deltaY =  window.pageYOffset
+                || document.documentElement.scrollTop
+                || document.body.scrollTop
+                || 0;
+  },
+
+  realOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.scrollTop  || 0;
+      valueL += element.scrollLeft || 0;
+      element = element.parentNode;
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  cumulativeOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  positionedOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+      if (element) {
+        p = Element.getStyle(element, 'position');
+        if (p == 'relative' || p == 'absolute') break;
+      }
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  offsetParent: function(element) {
+    if (element.offsetParent) return element.offsetParent;
+    if (element == document.body) return element;
+
+    while ((element = element.parentNode) && element != document.body)
+      if (Element.getStyle(element, 'position') != 'static')
+        return element;
+
+    return document.body;
+  },
+
+  // caches x/y coordinate pair to use with overlap
+  within: function(element, x, y) {
+    if (this.includeScrollOffsets)
+      return this.withinIncludingScrolloffsets(element, x, y);
+    this.xcomp = x;
+    this.ycomp = y;
+    this.offset = this.cumulativeOffset(element);
+
+    return (y >= this.offset[1] &&
+            y <  this.offset[1] + element.offsetHeight &&
+            x >= this.offset[0] &&
+            x <  this.offset[0] + element.offsetWidth);
+  },
+
+  withinIncludingScrolloffsets: function(element, x, y) {
+    var offsetcache = this.realOffset(element);
+
+    this.xcomp = x + offsetcache[0] - this.deltaX;
+    this.ycomp = y + offsetcache[1] - this.deltaY;
+    this.offset = this.cumulativeOffset(element);
+
+    return (this.ycomp >= this.offset[1] &&
+            this.ycomp <  this.offset[1] + element.offsetHeight &&
+            this.xcomp >= this.offset[0] &&
+            this.xcomp <  this.offset[0] + element.offsetWidth);
+  },
+
+  // within must be called directly before
+  overlap: function(mode, element) {
+    if (!mode) return 0;
+    if (mode == 'vertical')
+      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+        element.offsetHeight;
+    if (mode == 'horizontal')
+      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+        element.offsetWidth;
+  },
+
+  clone: function(source, target) {
+    source = $(source);
+    target = $(target);
+    target.style.position = 'absolute';
+    var offsets = this.cumulativeOffset(source);
+    target.style.top    = offsets[1] + 'px';
+    target.style.left   = offsets[0] + 'px';
+    target.style.width  = source.offsetWidth + 'px';
+    target.style.height = source.offsetHeight + 'px';
+  },
+
+  page: function(forElement) {
+    var valueT = 0, valueL = 0;
+
+    var element = forElement;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+
+      // Safari fix
+      if (element.offsetParent==document.body)
+        if (Element.getStyle(element,'position')=='absolute') break;
+
+    } while (element = element.offsetParent);
+
+    element = forElement;
+    do {
+      valueT -= element.scrollTop  || 0;
+      valueL -= element.scrollLeft || 0;
+    } while (element = element.parentNode);
+
+    return [valueL, valueT];
+  },
+
+  clone: function(source, target) {
+    var options = Object.extend({
+      setLeft:    true,
+      setTop:     true,
+      setWidth:   true,
+      setHeight:  true,
+      offsetTop:  0,
+      offsetLeft: 0
+    }, arguments[2] || {})
+
+    // find page position of source
+    source = $(source);
+    var p = Position.page(source);
+
+    // find coordinate system to use
+    target = $(target);
+    var delta = [0, 0];
+    var parent = null;
+    // delta [0,0] will do fine with position: fixed elements,
+    // position:absolute needs offsetParent deltas
+    if (Element.getStyle(target,'position') == 'absolute') {
+      parent = Position.offsetParent(target);
+      delta = Position.page(parent);
+    }
+
+    // correct by body offsets (fixes Safari)
+    if (parent == document.body) {
+      delta[0] -= document.body.offsetLeft;
+      delta[1] -= document.body.offsetTop;
+    }
+
+    // set position
+    if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
+    if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
+    if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
+    if(options.setHeight) target.style.height = source.offsetHeight + 'px';
+  },
+
+  absolutize: function(element) {
+    element = $(element);
+    if (element.style.position == 'absolute') return;
+    Position.prepare();
+
+    var offsets = Position.positionedOffset(element);
+    var top     = offsets[1];
+    var left    = offsets[0];
+    var width   = element.clientWidth;
+    var height  = element.clientHeight;
+
+    element._originalLeft   = left - parseFloat(element.style.left  || 0);
+    element._originalTop    = top  - parseFloat(element.style.top || 0);
+    element._originalWidth  = element.style.width;
+    element._originalHeight = element.style.height;
+
+    element.style.position = 'absolute';
+    element.style.top    = top + 'px';;
+    element.style.left   = left + 'px';;
+    element.style.width  = width + 'px';;
+    element.style.height = height + 'px';;
+  },
+
+  relativize: function(element) {
+    element = $(element);
+    if (element.style.position == 'relative') return;
+    Position.prepare();
+
+    element.style.position = 'relative';
+    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
+    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.height = element._originalHeight;
+    element.style.width  = element._originalWidth;
+  }
+}
+
+// Safari returns margins on body which is incorrect if the child is absolutely
+// positioned.  For performance reasons, redefine Position.cumulativeOffset for
+// KHTML/WebKit only.
+if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
+  Position.cumulativeOffset = function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      if (element.offsetParent == document.body)
+        if (Element.getStyle(element, 'position') == 'absolute') break;
+
+      element = element.offsetParent;
+    } while (element);
+
+    return [valueL, valueT];
+  }
+}
\ No newline at end of file
diff --git a/automation/admin/public/robots.txt b/automation/admin/public/robots.txt
new file mode 100644
index 0000000..4ab9e89
--- /dev/null
+++ b/automation/admin/public/robots.txt
@@ -0,0 +1 @@
+# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
\ No newline at end of file
diff --git a/automation/admin/public/stylesheets/admin.css b/automation/admin/public/stylesheets/admin.css
new file mode 100644
index 0000000..c1244bd
--- /dev/null
+++ b/automation/admin/public/stylesheets/admin.css
@@ -0,0 +1,106 @@
+body {
+  font-family: verdana, ariel, helvetica, sans-serif;
+  line-height: 120%;
+  background-color: #b7dcff;
+}
+
+.title { 
+  font-size: 120%;
+}
+
+td { 
+  background-color: #c9edff;
+  padding: 4px;
+  font-size: 80%;
+}
+
+th { 
+  background-color: #eeffff;
+  padding: 4px;
+  font-size: 80%;
+}
+
+.actionlist {
+  background-color: #ffffee;
+}
+
+.footer { 
+  background-color: #ffffee;
+  width: 30em;
+  text-align: center;
+  padding: 4px;
+}
+
+.fieldWithErrors {
+  padding: 0px;
+  background-color: red;
+  display: table;
+}
+
+#errorExplanation { 
+  margin-left: auto; 
+  margin-right: auto;
+}
+
+#errorExplanation h2 { 
+  font-size: 110%;
+}
+
+/* Set up the sidebar */
+div.link-list {
+  width:11em;
+  position:absolute;
+  top:0;
+  bottom:0;
+  font-size:100%;
+  padding-left:0.5em;
+  padding-right:0.5em;
+  padding-top:0.5em;
+  margin-left:0;
+  margin-right:0;
+  background-color: #c9edff;
+}
+
+div.menu-item {
+  margin-top: 2px;
+  margin-bottom: 2px;
+  padding-left: 4px;
+  padding-right: 4px;
+  border-style: solid;
+  border-width: 1px;
+  border-color: #ffffff;
+}
+
+div.submenu {
+  background-color: #ddefff;
+  margin-top: 2px;
+  margin-bottom: 2px;
+}
+
+div.submenutitle {
+  text-align: center;
+}
+
+#main {
+  margin-left:11em;
+  padding-left:1em;
+}
+
+#sidebar {
+  left:0;
+  text-align: left;
+  line-height: 150%;
+}
+
+.notice {
+  font-size: 90%;
+  color: blue;
+}
+
+:link {
+  color: #3333aa;
+}
+
+:visited {
+  color: #7700aa;
+}
\ No newline at end of file
diff --git a/automation/admin/script/about b/automation/admin/script/about
new file mode 100755
index 0000000..7b07d46
--- /dev/null
+++ b/automation/admin/script/about
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/about'
\ No newline at end of file
diff --git a/automation/admin/script/benchmarker b/automation/admin/script/benchmarker
new file mode 100755
index 0000000..f5c6d7c
--- /dev/null
+++ b/automation/admin/script/benchmarker
@@ -0,0 +1,19 @@
+#!/usr/local/bin/ruby
+
+if ARGV.empty?
+  puts "Usage: benchmarker times 'Person.expensive_way' 'Person.another_expensive_way' ..."
+  exit 
+end
+
+require File.dirname(__FILE__) + '/../config/environment'
+require 'benchmark'
+include Benchmark
+
+# Don't include compilation in the benchmark
+ARGV[1..-1].each { |expression| eval(expression) }
+
+bm(6) do |x|
+  ARGV[1..-1].each_with_index do |expression, idx|
+    x.report("##{idx + 1}") { ARGV[0].to_i.times { eval(expression) } }
+  end
+end 
diff --git a/automation/admin/script/breakpointer b/automation/admin/script/breakpointer
new file mode 100755
index 0000000..64af76e
--- /dev/null
+++ b/automation/admin/script/breakpointer
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/breakpointer'
\ No newline at end of file
diff --git a/automation/admin/script/console b/automation/admin/script/console
new file mode 100755
index 0000000..42f28f7
--- /dev/null
+++ b/automation/admin/script/console
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/console'
\ No newline at end of file
diff --git a/automation/admin/script/destroy b/automation/admin/script/destroy
new file mode 100755
index 0000000..fa0e6fc
--- /dev/null
+++ b/automation/admin/script/destroy
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/destroy'
\ No newline at end of file
diff --git a/automation/admin/script/generate b/automation/admin/script/generate
new file mode 100755
index 0000000..ef976e0
--- /dev/null
+++ b/automation/admin/script/generate
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/generate'
\ No newline at end of file
diff --git a/automation/admin/script/performance/benchmarker b/automation/admin/script/performance/benchmarker
new file mode 100755
index 0000000..c842d35
--- /dev/null
+++ b/automation/admin/script/performance/benchmarker
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../../config/boot'
+require 'commands/performance/benchmarker'
diff --git a/automation/admin/script/performance/profiler b/automation/admin/script/performance/profiler
new file mode 100755
index 0000000..d855ac8
--- /dev/null
+++ b/automation/admin/script/performance/profiler
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../../config/boot'
+require 'commands/performance/profiler'
diff --git a/automation/admin/script/plugin b/automation/admin/script/plugin
new file mode 100755
index 0000000..26ca64c
--- /dev/null
+++ b/automation/admin/script/plugin
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/plugin'
\ No newline at end of file
diff --git a/automation/admin/script/process/reaper b/automation/admin/script/process/reaper
new file mode 100755
index 0000000..c77f045
--- /dev/null
+++ b/automation/admin/script/process/reaper
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../../config/boot'
+require 'commands/process/reaper'
diff --git a/automation/admin/script/process/spawner b/automation/admin/script/process/spawner
new file mode 100755
index 0000000..7118f39
--- /dev/null
+++ b/automation/admin/script/process/spawner
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../../config/boot'
+require 'commands/process/spawner'
diff --git a/automation/admin/script/process/spinner b/automation/admin/script/process/spinner
new file mode 100755
index 0000000..6816b32
--- /dev/null
+++ b/automation/admin/script/process/spinner
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../../config/boot'
+require 'commands/process/spinner'
diff --git a/automation/admin/script/profiler b/automation/admin/script/profiler
new file mode 100755
index 0000000..d8927a1
--- /dev/null
+++ b/automation/admin/script/profiler
@@ -0,0 +1,34 @@
+#!/usr/local/bin/ruby
+if ARGV.empty?
+  $stderr.puts "Usage: profiler 'Person.expensive_method(10)' [times]"
+  exit(1)
+end
+
+# Keep the expensive require out of the profile.
+$stderr.puts 'Loading Rails...'
+require File.dirname(__FILE__) + '/../config/environment'
+
+# Define a method to profile.
+if ARGV[1] and ARGV[1].to_i > 1
+  eval "def profile_me() #{ARGV[1]}.times { #{ARGV[0]} } end"
+else
+  eval "def profile_me() #{ARGV[0]} end"
+end
+
+# Use the ruby-prof extension if available.  Fall back to stdlib profiler.
+begin
+  require 'prof'
+  $stderr.puts 'Using the ruby-prof extension.'
+  Prof.clock_mode = Prof::GETTIMEOFDAY
+  Prof.start
+  profile_me
+  results = Prof.stop
+  require 'rubyprof_ext'
+  Prof.print_profile(results, $stderr)
+rescue LoadError
+  $stderr.puts 'Using the standard Ruby profiler.'
+  Profiler__.start_profile
+  profile_me
+  Profiler__.stop_profile
+  Profiler__.print_profile($stderr)
+end
diff --git a/automation/admin/script/runner b/automation/admin/script/runner
new file mode 100755
index 0000000..ccc30f9
--- /dev/null
+++ b/automation/admin/script/runner
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/runner'
\ No newline at end of file
diff --git a/automation/admin/script/server b/automation/admin/script/server
new file mode 100755
index 0000000..dfabcb8
--- /dev/null
+++ b/automation/admin/script/server
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/server'
\ No newline at end of file
diff --git a/automation/admin/test/fixtures/array_designs.yml b/automation/admin/test/fixtures/array_designs.yml
new file mode 100644
index 0000000..39c0490
--- /dev/null
+++ b/automation/admin/test/fixtures/array_designs.yml
@@ -0,0 +1,9 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first:
+  id: 1
+  miamexpress_subid: 1001
+  accession: A-MEXP-1
+another:
+  id: 2
+  miamexpress_subid: 1002
+  accession: A-MEXP-2
diff --git a/automation/admin/test/fixtures/categories.yml b/automation/admin/test/fixtures/categories.yml
new file mode 100644
index 0000000..c605e24
--- /dev/null
+++ b/automation/admin/test/fixtures/categories.yml
@@ -0,0 +1,9 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first_category:
+  id: 1
+  ontology_term: Age
+  display_label: Age
+another_category:
+  id: 2
+  ontology_term: CellLine
+  display_label: Cell line
diff --git a/automation/admin/test/fixtures/data_files.yml b/automation/admin/test/fixtures/data_files.yml
new file mode 100644
index 0000000..8794d28
--- /dev/null
+++ b/automation/admin/test/fixtures/data_files.yml
@@ -0,0 +1,5 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first:
+  id: 1
+another:
+  id: 2
diff --git a/automation/admin/test/fixtures/designs.yml b/automation/admin/test/fixtures/designs.yml
new file mode 100644
index 0000000..24d9203
--- /dev/null
+++ b/automation/admin/test/fixtures/designs.yml
@@ -0,0 +1,13 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first:
+  id: 1
+  display_label:     Cell type comparison
+  ontology_category: ExperimentalDesignType
+  ontology_value:    cell_type_comparison_design
+  design_type:       biological
+another:
+  id: 2
+  display_label:     Dye swap
+  ontology_category: MethodologicalDesignType
+  ontology_value:    dye_swap_design
+  design_type:       methodological
diff --git a/automation/admin/test/fixtures/events.yml b/automation/admin/test/fixtures/events.yml
new file mode 100644
index 0000000..d1c4a63
--- /dev/null
+++ b/automation/admin/test/fixtures/events.yml
@@ -0,0 +1,9 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first:
+  id: 1
+  event_type: MAGEvalidator
+  experiment_id: 2
+another:
+  id: 2
+  event_type: MAGEloader
+  experiment_id: 2
diff --git a/automation/admin/test/fixtures/experiments.yml b/automation/admin/test/fixtures/experiments.yml
new file mode 100644
index 0000000..4345b53
--- /dev/null
+++ b/automation/admin/test/fixtures/experiments.yml
@@ -0,0 +1,18 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first:     # N.B. no accession for the first, otherwise test deprecation fails.
+  id: 1
+  experiment_type: Tab2MAGE
+  is_affymetrix: 0
+  in_curation: 0
+another:
+  id: 2
+  accession: E-TABM-1
+  experiment_type: Tab2MAGE
+  is_affymetrix: 1
+  in_curation: 1
+yetanother:
+  id: 3
+  accession: E-MEXP-1
+  experiment_type: MIAMExpress
+  is_affymetrix: 0
+  in_curation: 1
diff --git a/automation/admin/test/fixtures/factors.yml b/automation/admin/test/fixtures/factors.yml
new file mode 100644
index 0000000..6aef4f5
--- /dev/null
+++ b/automation/admin/test/fixtures/factors.yml
@@ -0,0 +1,7 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first:
+  id: 1
+  name: cell_type
+another:
+  id: 2
+  name: compound
diff --git a/automation/admin/test/fixtures/materials.yml b/automation/admin/test/fixtures/materials.yml
new file mode 100644
index 0000000..38af1ac
--- /dev/null
+++ b/automation/admin/test/fixtures/materials.yml
@@ -0,0 +1,11 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first:
+  id: 1
+  display_label:     Whole organism
+  ontology_category: MaterialType
+  ontology_value:    whole_organism
+another:
+  id: 2
+  display_label:     Organism part
+  ontology_category: MaterialType
+  ontology_value:    organism_part
diff --git a/automation/admin/test/fixtures/organisms.yml b/automation/admin/test/fixtures/organisms.yml
new file mode 100644
index 0000000..16528e0
--- /dev/null
+++ b/automation/admin/test/fixtures/organisms.yml
@@ -0,0 +1,13 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first_organism:
+  id: 1
+  accession:  562
+  common_name: E. coli
+  scientific_name: Escherichia coli
+  taxon_id: 1
+another_organism:
+  id: 2
+  accession:  4932
+  common_name: Baker's yeast
+  scientific_name: Saccharomyces cerevisiae
+  taxon_id: 2
\ No newline at end of file
diff --git a/automation/admin/test/fixtures/permissions.yml b/automation/admin/test/fixtures/permissions.yml
new file mode 100644
index 0000000..acaa96a
--- /dev/null
+++ b/automation/admin/test/fixtures/permissions.yml
@@ -0,0 +1,17 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+adminperms:
+  id: 1
+  name: '.*/.*'
+  info: 'Access All Areas'
+  is_deleted: 0
+exptperms:
+  id: 2
+  name: 'experiments/.*'
+  info: 'Full experiment access'
+  is_deleted: 0
+arrayperms:
+  id: 3
+  name: 'array_designs/.*'
+  info: 'Full array design access'
+  is_deleted: 0
+
diff --git a/automation/admin/test/fixtures/permissions_roles.yml b/automation/admin/test/fixtures/permissions_roles.yml
new file mode 100644
index 0000000..c80f72f
--- /dev/null
+++ b/automation/admin/test/fixtures/permissions_roles.yml
@@ -0,0 +1,11 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+
+adminrole:
+  role_id: 1
+  permission_id: 1
+exptrole:
+  role_id: 1
+  permission_id: 2
+arrayrole:
+  role_id: 1
+  permission_id: 3
diff --git a/automation/admin/test/fixtures/protocols.yml b/automation/admin/test/fixtures/protocols.yml
new file mode 100644
index 0000000..97ae777
--- /dev/null
+++ b/automation/admin/test/fixtures/protocols.yml
@@ -0,0 +1,11 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first:
+  id: 1
+  accession: P-TABM-1
+  user_accession: my_acc
+  expt_accession: E-TABM-1
+another:
+  id: 2
+  accession: P-TABM-2
+  user_accession: my_other_acc
+  expt_accession: E-TABM-2
diff --git a/automation/admin/test/fixtures/quantitation_types.yml b/automation/admin/test/fixtures/quantitation_types.yml
new file mode 100644
index 0000000..ce9ff7d
--- /dev/null
+++ b/automation/admin/test/fixtures/quantitation_types.yml
@@ -0,0 +1,7 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first:
+  id: 1
+  name: CHPSignal
+another:
+  id: 2
+  name: CHPDetection
diff --git a/automation/admin/test/fixtures/roles.yml b/automation/admin/test/fixtures/roles.yml
new file mode 100644
index 0000000..a6999e2
--- /dev/null
+++ b/automation/admin/test/fixtures/roles.yml
@@ -0,0 +1,11 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first:
+  id: 1
+  name: 'admin'
+  info: 'Superuser'
+  is_deleted: 0
+another:
+  id: 2
+  name: 'curator'
+  info: 'Curator'
+  is_deleted: 0
\ No newline at end of file
diff --git a/automation/admin/test/fixtures/roles_users.yml b/automation/admin/test/fixtures/roles_users.yml
new file mode 100644
index 0000000..f62f7d9
--- /dev/null
+++ b/automation/admin/test/fixtures/roles_users.yml
@@ -0,0 +1,5 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+
+bobrole:
+  role_id: 1
+  user_id: 1000001
diff --git a/automation/admin/test/fixtures/spreadsheets.yml b/automation/admin/test/fixtures/spreadsheets.yml
new file mode 100644
index 0000000..8794d28
--- /dev/null
+++ b/automation/admin/test/fixtures/spreadsheets.yml
@@ -0,0 +1,5 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first:
+  id: 1
+another:
+  id: 2
diff --git a/automation/admin/test/fixtures/taxons.yml b/automation/admin/test/fixtures/taxons.yml
new file mode 100644
index 0000000..0bd0e7a
--- /dev/null
+++ b/automation/admin/test/fixtures/taxons.yml
@@ -0,0 +1,11 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first_taxon:
+  id: 1
+  accession:  2
+  common_name: Eubacteria
+  scientific_name: Bacteria
+another_taxon:
+  id: 2
+  accession:  4890
+  common_name: Sac Fungi
+  scientific_name: Ascomycota
diff --git a/automation/admin/test/fixtures/users.yml b/automation/admin/test/fixtures/users.yml
new file mode 100644
index 0000000..5330c67
--- /dev/null
+++ b/automation/admin/test/fixtures/users.yml
@@ -0,0 +1,16 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+
+bob:
+  id: 1000001
+  login: bob
+  password: test # test
+  
+existingbob:
+  id: 1000002
+  login: existingbob
+  password: test # test
+  
+longbob:
+  id: 1000003
+  login: longbob
+  password: longtest # longtest
\ No newline at end of file
diff --git a/automation/admin/test/functional/account_controller_test.rb b/automation/admin/test/functional/account_controller_test.rb
new file mode 100644
index 0000000..f1d1fe6
--- /dev/null
+++ b/automation/admin/test/functional/account_controller_test.rb
@@ -0,0 +1,81 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'account_controller'
+
+# Raise errors beyond the default web-based presentation
+class AccountController; def rescue_action(e) raise e end; end
+
+class AccountControllerTest < Test::Unit::TestCase
+  
+  fixtures :permissions, :roles, :users, :roles_users, :permissions_roles
+  
+  def setup
+    @controller = AccountController.new
+    @request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
+    @request.host = "localhost"
+  end
+  
+  def test_auth_bob
+    @request.session['return-to'] = "/account/welcome"
+
+    post :login, :user_login => "bob", :user_password => "test"
+    assert(@response.has_session_object?(:user))
+
+    assert_equal @bob, @response.session[:user]
+    
+    assert(@response.redirect_url_match?(/\/account\/welcome$/))
+  end
+  
+  def test_signup
+    @request.session['return-to'] = "/account/welcome"
+
+    post :signup, :user => { :login                 => "newbob",
+                             :password              => "newpassword",
+                             :password_confirmation => "newpassword" }
+    assert(@response.has_session_object?(:user))
+    
+    assert(@response.redirect_url_match?(/\/account\/welcome$/))
+  end
+
+  def test_bad_signup
+    @request.session['return-to'] = "/account/welcome"
+
+    post :signup, :user => { :login                 => "newbob",
+                             :password              => "newpassword",
+                             :password_confirmation => "wrong" }
+
+    assert(@response.template_objects['user'].errors.invalid?(:password))
+    assert_response(:success)
+    
+    post :signup, :user => { :login                 => "yo",
+                             :password              => "newpassword",
+                             :password_confirmation => "newpassword" }
+    assert(@response.template_objects['user'].errors.invalid?(:login))
+    assert_response(:success)
+
+    post :signup, :user => { :login                 => "yo",
+                             :password              => "newpassword",
+                             :password_confirmation => "wrong" }
+    assert(@response.template_objects['user'].errors.invalid?(:login))
+    assert(@response.template_objects['user'].errors.invalid?(:password))
+    assert_response(:success)
+  end
+
+  def test_invalid_login
+    post :login, :user_login => "bob", :user_password => "not_correct"
+     
+    assert(! @response.has_session_object?("user"))
+    assert(@response.has_template_object?("message"))
+    assert(@response.has_template_object?("login"))
+  end
+  
+  def test_login_logoff
+
+    post :login, :user_login => "bob", :user_password => "test"
+    assert(@response.has_session_object?(:user))
+
+    get :logout
+    assert(! @response.has_session_object?(:user))
+
+  end
+  
+end
diff --git a/automation/admin/test/functional/array_designs_controller_test.rb b/automation/admin/test/functional/array_designs_controller_test.rb
new file mode 100644
index 0000000..d5a922c
--- /dev/null
+++ b/automation/admin/test/functional/array_designs_controller_test.rb
@@ -0,0 +1,88 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'array_designs_controller'
+
+# Re-raise errors caught by the controller.
+class ArrayDesignsController; def rescue_action(e) raise e end; end
+
+class ArrayDesignsControllerTest < Test::Unit::TestCase
+  fixtures :array_designs
+  fixtures :permissions, :roles, :users, :roles_users, :permissions_roles
+
+  def setup
+    @controller = ArrayDesignsController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+    @request.session[:user] = @bob
+  end
+
+  def test_index
+    get :index
+    assert_response :success
+    assert_template 'list'
+  end
+
+  def test_list
+    get :list
+
+    assert_response :success
+    assert_template 'list'
+
+    assert_not_nil assigns(:array_designs)
+  end
+
+  def test_show
+    get :show, :id => 1
+
+    assert_response :success
+    assert_template 'show'
+
+    assert_not_nil assigns(:array_design)
+    assert assigns(:array_design).valid?
+  end
+
+  def test_new
+    get :new
+
+    assert_response :success
+    assert_template 'new'
+
+    assert_not_nil assigns(:array_design)
+  end
+
+  def test_create
+    num_array_designs = ArrayDesign.count
+
+    post :create, :array_design => {:id => 3, :miamexpress_subid => 1003, :accession => "A-MEXP-3"}
+
+    assert_response :redirect
+    assert_redirected_to :action => 'list'
+
+    assert_equal num_array_designs + 1, ArrayDesign.count
+  end
+
+  def test_edit
+    get :edit, :id => 1
+
+    assert_response :success
+    assert_template 'edit'
+
+    assert_not_nil assigns(:array_design)
+    assert assigns(:array_design).valid?
+  end
+
+  def test_update
+    post :update, :id => 1
+    assert_response :redirect
+    assert_redirected_to :action => 'list_all'
+  end
+
+  def test_deprecate
+    assert_not_nil ArrayDesign.find(1)
+
+    post :deprecate, :id => 1
+    assert_response :redirect
+    assert_redirected_to :action => 'list'
+
+    assert_equal(1, ArrayDesign.find(1).is_deleted)
+  end
+end
diff --git a/automation/admin/test/functional/category_controller_test.rb b/automation/admin/test/functional/category_controller_test.rb
new file mode 100644
index 0000000..2b6558d
--- /dev/null
+++ b/automation/admin/test/functional/category_controller_test.rb
@@ -0,0 +1,21 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'category_controller'
+
+# Re-raise errors caught by the controller.
+class CategoryController; def rescue_action(e) raise e end; end
+
+class CategoryControllerTest < Test::Unit::TestCase
+
+  fixtures :categories
+
+  def setup
+    @controller = CategoryController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+  end
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
diff --git a/automation/admin/test/functional/design_controller_test.rb b/automation/admin/test/functional/design_controller_test.rb
new file mode 100644
index 0000000..91eb1de
--- /dev/null
+++ b/automation/admin/test/functional/design_controller_test.rb
@@ -0,0 +1,21 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'design_controller'
+
+# Re-raise errors caught by the controller.
+class DesignController; def rescue_action(e) raise e end; end
+
+class DesignControllerTest < Test::Unit::TestCase
+
+  fixtures :designs
+
+  def setup
+    @controller = DesignController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+  end
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
diff --git a/automation/admin/test/functional/event_controller_test.rb b/automation/admin/test/functional/event_controller_test.rb
new file mode 100644
index 0000000..71b596b
--- /dev/null
+++ b/automation/admin/test/functional/event_controller_test.rb
@@ -0,0 +1,21 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'event_controller'
+
+# Re-raise errors caught by the controller.
+class EventController; def rescue_action(e) raise e end; end
+
+class EventControllerTest < Test::Unit::TestCase
+
+  fixtures :experiments, :events
+
+  def setup
+    @controller = EventController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+  end
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
diff --git a/automation/admin/test/functional/experiments_controller_test.rb b/automation/admin/test/functional/experiments_controller_test.rb
new file mode 100644
index 0000000..8415da8
--- /dev/null
+++ b/automation/admin/test/functional/experiments_controller_test.rb
@@ -0,0 +1,94 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'experiments_controller'
+
+# Re-raise errors caught by the controller.
+class ExperimentsController; def rescue_action(e) raise e end; end
+
+class ExperimentsControllerTest < Test::Unit::TestCase
+  fixtures :experiments
+  fixtures :permissions, :roles, :users, :roles_users, :permissions_roles
+
+  def setup
+    @controller = ExperimentsController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+    @request.session[:user] = @bob
+  end
+
+  def test_index
+    get :index
+    assert_response :success
+    assert_template 'list'
+  end
+
+  def test_list
+    get :list
+
+    assert_response :success
+    assert_template 'list'
+
+    assert_not_nil assigns(:experiments)
+  end
+
+  def test_show
+    get :show, :id => 1
+
+    assert_response :success
+    assert_template 'show'
+
+    assert_not_nil assigns(:experiment)
+    assert assigns(:experiment).valid?
+  end
+
+  def test_new
+    get :new
+
+    assert_response :success
+    assert_template 'new'
+
+    assert_not_nil assigns(:experiment)
+  end
+
+  def test_create
+    num_experiments = Experiment.count
+
+    post :create, :experiment => {:id => 4, :accession => 'E-GEOD-1',
+                                  :experiment_type => 'GEO',
+                                  :is_affymetrix => 0,
+                                  :in_curation => 0}
+
+    assert_response :redirect
+    assert_redirected_to :action => 'list'
+
+    assert_equal num_experiments + 1, Experiment.count
+  end
+
+  def test_edit
+    get :edit, :id => 1
+
+    # Experiments are redirected to their tab2mage or miamexp
+    # subclass. FIXME we should really add tests for these subclasses
+    # at some point.
+    assert_response :redirect
+    assert_redirected_to :controller => 'tab2mages', :action => 'edit'
+
+    assert_not_nil assigns(:experiment)
+    assert assigns(:experiment).valid?
+  end
+
+  def test_update
+    post :update, :id => 2
+    assert_response :redirect
+    assert_redirected_to :action => 'list'
+  end
+
+  def test_deprecate
+    assert_not_nil Experiment.find(1)
+
+    post :deprecate, :id => 1
+    assert_response :redirect
+    assert_redirected_to :action => 'list'
+
+    assert_equal(1, Experiment.find(1).is_deleted)
+  end
+end
diff --git a/automation/admin/test/functional/factor_controller_test.rb b/automation/admin/test/functional/factor_controller_test.rb
new file mode 100644
index 0000000..0a775bd
--- /dev/null
+++ b/automation/admin/test/functional/factor_controller_test.rb
@@ -0,0 +1,21 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'factor_controller'
+
+# Re-raise errors caught by the controller.
+class FactorController; def rescue_action(e) raise e end; end
+
+class FactorControllerTest < Test::Unit::TestCase
+
+  fixtures :factors
+
+  def setup
+    @controller = FactorController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+  end
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
diff --git a/automation/admin/test/functional/material_controller_test.rb b/automation/admin/test/functional/material_controller_test.rb
new file mode 100644
index 0000000..d8f8cb7
--- /dev/null
+++ b/automation/admin/test/functional/material_controller_test.rb
@@ -0,0 +1,21 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'material_controller'
+
+# Re-raise errors caught by the controller.
+class MaterialController; def rescue_action(e) raise e end; end
+
+class MaterialControllerTest < Test::Unit::TestCase
+
+  fixtures :materials
+
+  def setup
+    @controller = MaterialController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+  end
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
diff --git a/automation/admin/test/functional/miamexps_controller_test.rb b/automation/admin/test/functional/miamexps_controller_test.rb
new file mode 100644
index 0000000..56d7d48
--- /dev/null
+++ b/automation/admin/test/functional/miamexps_controller_test.rb
@@ -0,0 +1,18 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'miamexps_controller'
+
+# Re-raise errors caught by the controller.
+class MiamexpsController; def rescue_action(e) raise e end; end
+
+class MiamexpsControllerTest < Test::Unit::TestCase
+  def setup
+    @controller = MiamexpsController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+  end
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
diff --git a/automation/admin/test/functional/organism_controller_test.rb b/automation/admin/test/functional/organism_controller_test.rb
new file mode 100644
index 0000000..dfe86b6
--- /dev/null
+++ b/automation/admin/test/functional/organism_controller_test.rb
@@ -0,0 +1,21 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'organism_controller'
+
+# Re-raise errors caught by the controller.
+class OrganismController; def rescue_action(e) raise e end; end
+
+class OrganismControllerTest < Test::Unit::TestCase
+
+  fixtures :taxons, :organisms
+
+  def setup
+    @controller = OrganismController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+  end
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
diff --git a/automation/admin/test/functional/protocols_controller_test.rb b/automation/admin/test/functional/protocols_controller_test.rb
new file mode 100644
index 0000000..b5b1967
--- /dev/null
+++ b/automation/admin/test/functional/protocols_controller_test.rb
@@ -0,0 +1,88 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'protocols_controller'
+
+# Re-raise errors caught by the controller.
+class ProtocolsController; def rescue_action(e) raise e end; end
+
+class ProtocolsControllerTest < Test::Unit::TestCase
+  fixtures :protocols
+  fixtures :permissions, :roles, :users, :roles_users, :permissions_roles
+
+  def setup
+    @controller = ProtocolsController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+    @request.session[:user] = @bob
+  end
+
+  def test_index
+    get :index
+    assert_response :success
+    assert_template 'list'
+  end
+
+  def test_list
+    get :list
+
+    assert_response :success
+    assert_template 'list'
+
+    assert_not_nil assigns(:protocols)
+  end
+
+  def test_show
+    get :show, :id => 1
+
+    assert_response :success
+    assert_template 'show'
+
+    assert_not_nil assigns(:protocol)
+    assert assigns(:protocol).valid?
+  end
+
+  def test_new
+    get :new
+
+    assert_response :success
+    assert_template 'new'
+
+    assert_not_nil assigns(:protocol)
+  end
+
+  def test_create
+    num_protocols = Protocol.count
+
+    post :create, :protocol => {:id => 3, :accession => 3, :user_accession => 3, :expt_accession => 3}
+
+    assert_response :redirect
+    assert_redirected_to :action => 'list'
+
+    assert_equal num_protocols + 1, Protocol.count
+  end
+
+  def test_edit
+    get :edit, :id => 1
+
+    assert_response :success
+    assert_template 'edit'
+
+    assert_not_nil assigns(:protocol)
+    assert assigns(:protocol).valid?
+  end
+
+  def test_update
+    post :update, :id => 1
+    assert_response :redirect
+    assert_redirected_to :action => 'list'
+  end
+
+  def test_deprecate
+    assert_not_nil Protocol.find(1)
+
+    post :deprecate, :id => 1
+    assert_response :redirect
+    assert_redirected_to :action => 'list'
+
+    assert_equal(1, Protocol.find(1).is_deleted)
+  end
+end
diff --git a/automation/admin/test/functional/quantitation_type_controller_test.rb b/automation/admin/test/functional/quantitation_type_controller_test.rb
new file mode 100644
index 0000000..aff34e8
--- /dev/null
+++ b/automation/admin/test/functional/quantitation_type_controller_test.rb
@@ -0,0 +1,21 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'quantitation_type_controller'
+
+# Re-raise errors caught by the controller.
+class QuantitationTypeController; def rescue_action(e) raise e end; end
+
+class QuantitationTypeControllerTest < Test::Unit::TestCase
+  
+  fixtures :quantitation_types
+
+  def setup
+    @controller = QuantitationTypeController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+  end
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
diff --git a/automation/admin/test/functional/tab2mages_controller_test.rb b/automation/admin/test/functional/tab2mages_controller_test.rb
new file mode 100644
index 0000000..6403635
--- /dev/null
+++ b/automation/admin/test/functional/tab2mages_controller_test.rb
@@ -0,0 +1,18 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'tab2mages_controller'
+
+# Re-raise errors caught by the controller.
+class Tab2magesController; def rescue_action(e) raise e end; end
+
+class Tab2magesControllerTest < Test::Unit::TestCase
+  def setup
+    @controller = Tab2magesController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+  end
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
diff --git a/automation/admin/test/functional/taxon_controller_test.rb b/automation/admin/test/functional/taxon_controller_test.rb
new file mode 100644
index 0000000..0e4f5c3
--- /dev/null
+++ b/automation/admin/test/functional/taxon_controller_test.rb
@@ -0,0 +1,18 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'taxon_controller'
+
+# Re-raise errors caught by the controller.
+class TaxonController; def rescue_action(e) raise e end; end
+
+class TaxonControllerTest < Test::Unit::TestCase
+  def setup
+    @controller = TaxonController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+  end
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
diff --git a/automation/admin/test/functional/users_controller_test.rb b/automation/admin/test/functional/users_controller_test.rb
new file mode 100644
index 0000000..57a3fc8
--- /dev/null
+++ b/automation/admin/test/functional/users_controller_test.rb
@@ -0,0 +1,18 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'users_controller'
+
+# Re-raise errors caught by the controller.
+class UsersController; def rescue_action(e) raise e end; end
+
+class UsersControllerTest < Test::Unit::TestCase
+  def setup
+    @controller = UsersController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+  end
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
diff --git a/automation/admin/test/test_helper.rb b/automation/admin/test/test_helper.rb
new file mode 100644
index 0000000..40989bb
--- /dev/null
+++ b/automation/admin/test/test_helper.rb
@@ -0,0 +1,28 @@
+ENV["RAILS_ENV"] = "test"
+require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
+require 'test_help'
+
+class Test::Unit::TestCase
+  # Transactional fixtures accelerate your tests by wrapping each test method
+  # in a transaction that's rolled back on completion.  This ensures that the
+  # test database remains unchanged so your fixtures don't have to be reloaded
+  # between every test method.  Fewer database queries means faster tests.
+  #
+  # Read Mike Clark's excellent walkthrough at
+  #   http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting
+  #
+  # Every Active Record database supports transactions except MyISAM tables
+  # in MySQL.  Turn off transactional fixtures in this case; however, if you
+  # don't care one way or the other, switching from MyISAM to InnoDB tables
+  # is recommended.
+  self.use_transactional_fixtures = true
+
+  # Instantiated fixtures are slow, but give you @david where otherwise you
+  # would need people(:david).  If you don't want to migrate your existing
+  # test cases which use the @david style and don't mind the speed hit (each
+  # instantiated fixtures translates to a database query per test method),
+  # then set this back to true.
+  self.use_instantiated_fixtures  = true
+
+  # Add more helper methods to be used by all tests here...
+end
diff --git a/automation/admin/test/unit/array_design_test.rb b/automation/admin/test/unit/array_design_test.rb
new file mode 100644
index 0000000..c238037
--- /dev/null
+++ b/automation/admin/test/unit/array_design_test.rb
@@ -0,0 +1,14 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class ArrayDesignTest < Test::Unit::TestCase
+  fixtures :array_designs
+
+  def setup
+    @array_design = ArrayDesign.find(1)
+  end
+
+  # Replace this with your real tests.
+  def test_truth
+    assert_kind_of ArrayDesign, @array_design
+  end
+end
diff --git a/automation/admin/test/unit/category_test.rb b/automation/admin/test/unit/category_test.rb
new file mode 100644
index 0000000..f58ea61
--- /dev/null
+++ b/automation/admin/test/unit/category_test.rb
@@ -0,0 +1,14 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class CategoryTest < Test::Unit::TestCase
+  fixtures :categories
+
+  def setup
+    @category = Category.find(1)
+  end
+
+  # Replace this with your real tests.
+  def test_truth
+    assert_kind_of Category,  @category
+  end
+end
diff --git a/automation/admin/test/unit/data_file_test.rb b/automation/admin/test/unit/data_file_test.rb
new file mode 100644
index 0000000..261a725
--- /dev/null
+++ b/automation/admin/test/unit/data_file_test.rb
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class DataFileTest < Test::Unit::TestCase
+  fixtures :data_files
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
diff --git a/automation/admin/test/unit/design_test.rb b/automation/admin/test/unit/design_test.rb
new file mode 100644
index 0000000..03256e3
--- /dev/null
+++ b/automation/admin/test/unit/design_test.rb
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class DesignTest < Test::Unit::TestCase
+  fixtures :designs
+
+  # Replace this with your real tests.
+  def test_truth
+    assert_kind_of Design, designs(:first)
+  end
+end
diff --git a/automation/admin/test/unit/event_test.rb b/automation/admin/test/unit/event_test.rb
new file mode 100644
index 0000000..d8925d8
--- /dev/null
+++ b/automation/admin/test/unit/event_test.rb
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class EventTest < Test::Unit::TestCase
+  fixtures :experiments, :events
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
diff --git a/automation/admin/test/unit/experiment_test.rb b/automation/admin/test/unit/experiment_test.rb
new file mode 100644
index 0000000..ae340c4
--- /dev/null
+++ b/automation/admin/test/unit/experiment_test.rb
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class ExperimentTest < Test::Unit::TestCase
+  fixtures :experiments
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
diff --git a/automation/admin/test/unit/factor_test.rb b/automation/admin/test/unit/factor_test.rb
new file mode 100644
index 0000000..632cae1
--- /dev/null
+++ b/automation/admin/test/unit/factor_test.rb
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class FactorTest < Test::Unit::TestCase
+  fixtures :factors
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
diff --git a/automation/admin/test/unit/material_test.rb b/automation/admin/test/unit/material_test.rb
new file mode 100644
index 0000000..e4cf600
--- /dev/null
+++ b/automation/admin/test/unit/material_test.rb
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class MaterialTest < Test::Unit::TestCase
+  fixtures :materials
+
+  # Replace this with your real tests.
+  def test_truth
+    assert_kind_of Material, materials(:first)
+  end
+end
diff --git a/automation/admin/test/unit/organism_test.rb b/automation/admin/test/unit/organism_test.rb
new file mode 100644
index 0000000..7086ba5
--- /dev/null
+++ b/automation/admin/test/unit/organism_test.rb
@@ -0,0 +1,14 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class OrganismTest < Test::Unit::TestCase
+  fixtures :taxons, :organisms
+
+  def setup
+    @organism = Organism.find(1)
+  end
+
+  # Replace this with your real tests.
+  def test_truth
+    assert_kind_of Organism,  @organism
+  end
+end
diff --git a/automation/admin/test/unit/permission_test.rb b/automation/admin/test/unit/permission_test.rb
new file mode 100644
index 0000000..cab1ace
--- /dev/null
+++ b/automation/admin/test/unit/permission_test.rb
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class PermissionTest < Test::Unit::TestCase
+  fixtures :permissions
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
diff --git a/automation/admin/test/unit/protocol_test.rb b/automation/admin/test/unit/protocol_test.rb
new file mode 100644
index 0000000..b44d2da
--- /dev/null
+++ b/automation/admin/test/unit/protocol_test.rb
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class ProtocolTest < Test::Unit::TestCase
+  fixtures :protocols
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
diff --git a/automation/admin/test/unit/quantitation_type_test.rb b/automation/admin/test/unit/quantitation_type_test.rb
new file mode 100644
index 0000000..48edaa8
--- /dev/null
+++ b/automation/admin/test/unit/quantitation_type_test.rb
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class QuantitationTypeTest < Test::Unit::TestCase
+  fixtures :quantitation_types
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
diff --git a/automation/admin/test/unit/role_test.rb b/automation/admin/test/unit/role_test.rb
new file mode 100644
index 0000000..05d6652
--- /dev/null
+++ b/automation/admin/test/unit/role_test.rb
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class RoleTest < Test::Unit::TestCase
+  fixtures :roles
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
diff --git a/automation/admin/test/unit/spreadsheet_test.rb b/automation/admin/test/unit/spreadsheet_test.rb
new file mode 100644
index 0000000..465ea8a
--- /dev/null
+++ b/automation/admin/test/unit/spreadsheet_test.rb
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class SpreadsheetTest < Test::Unit::TestCase
+  fixtures :spreadsheets
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
diff --git a/automation/admin/test/unit/taxon_test.rb b/automation/admin/test/unit/taxon_test.rb
new file mode 100644
index 0000000..a8c311a
--- /dev/null
+++ b/automation/admin/test/unit/taxon_test.rb
@@ -0,0 +1,14 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class TaxonTest < Test::Unit::TestCase
+  fixtures :taxons
+
+  def setup
+    @taxon = Taxon.find(1)
+  end
+
+  # Replace this with your real tests.
+  def test_truth
+    assert_kind_of Taxon,  @taxon
+  end
+end
diff --git a/automation/admin/test/unit/user_test.rb b/automation/admin/test/unit/user_test.rb
new file mode 100644
index 0000000..0310e97
--- /dev/null
+++ b/automation/admin/test/unit/user_test.rb
@@ -0,0 +1,101 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class UserTest < Test::Unit::TestCase
+  self.use_instantiated_fixtures  = true
+  
+  fixtures :users
+    
+  def test_auth  
+    assert_equal  @bob, User.authenticate("bob", "test")    
+    assert_nil    User.authenticate("nonbob", "test")
+
+  end
+
+
+  def test_passwordchange
+        
+    @longbob.change_password("nonbobpasswd")
+    assert_equal @longbob, User.authenticate("longbob", "nonbobpasswd")
+    assert_nil   User.authenticate("longbob", "longtest")
+    @longbob.change_password("longtest")
+    assert_equal @longbob, User.authenticate("longbob", "longtest")
+    assert_nil   User.authenticate("longbob", "nonbobpasswd")
+        
+  end
+  
+  def test_disallowed_passwords
+
+    u = User.new( :is_deleted => 0 )    
+    u.login = "nonbob"
+
+    u.password = u.password_confirmation = "tiny"
+    assert !u.save     
+    assert u.errors.invalid?('password')
+
+    u.password = u.password_confirmation = "hugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehuge"
+    assert !u.save     
+    assert u.errors.invalid?('password')
+        
+    u.password = u.password_confirmation = ""
+    assert !u.save    
+    assert u.errors.invalid?('password')
+        
+    u.password = u.password_confirmation = "bobs_secure_password"
+    assert u.save     
+    assert u.errors.empty?
+        
+  end
+  
+  def test_bad_logins
+
+    u = User.new( :is_deleted => 0 )
+    u.password = u.password_confirmation = "bobs_secure_password"
+
+    u.login = "x"
+    assert !u.save     
+    assert u.errors.invalid?('login')
+    
+    u.login = "hugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhug"
+    assert !u.save     
+    assert u.errors.invalid?('login')
+
+    u.login = ""
+    assert !u.save
+    assert u.errors.invalid?('login')
+
+    u.login = "okbob"
+    assert u.save  
+    assert u.errors.empty?
+      
+  end
+
+
+  def test_collision
+    u = User.new
+    u.login      = "existingbob"
+    u.password = u.password_confirmation = "bobs_secure_password"
+    assert !u.save
+  end
+
+
+  def test_create
+    u = User.new( :is_deleted => 0 )
+    u.login      = "nonexistingbob"
+    u.password = u.password_confirmation = "bobs_secure_password"
+      
+    assert u.save  
+    
+  end
+  
+#  def test_sha1
+#    u = User.new
+#    u.login      = "nonexistingbob"
+#    u.password = u.password_confirmation = "bobs_secure_password"
+#    assert u.save
+#        
+#    assert_equal '98740ff87bade6d895010bceebbd9f718e7856bb', u.password
+#    
+#  end
+
+  
+end
diff --git a/automation/autosubs_checkd.pl b/automation/autosubs_checkd.pl
new file mode 100755
index 0000000..9585f06
--- /dev/null
+++ b/automation/autosubs_checkd.pl
@@ -0,0 +1,26 @@
+#!/usr/bin/env perl
+#
+# Script to automate expt_check.pl running for newly-submitted tab2mage experiments
+#
+# $Id: autosubs_checkd.pl 1853 2007-12-13 17:53:43Z tfrayner $
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use ArrayExpress::AutoSubmission::Daemon::T2MChecker;
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+# Create the daemon object and start checking.
+my $daemon = ArrayExpress::AutoSubmission::Daemon::T2MChecker->new({
+    polling_interval  => 5,
+    checker_threshold => ($CONFIG->get_ERROR_INNOCENT()
+ 		        | $CONFIG->get_ERROR_MIAME()),
+    experiment_type   => 'Tab2MAGE',
+    accession_prefix  => 'E-TABM-',
+    autosubs_admin    => $CONFIG->get_AUTOSUBS_ADMIN(),
+});
+
+$daemon->run();
diff --git a/automation/autosubs_exportd.pl b/automation/autosubs_exportd.pl
new file mode 100755
index 0000000..9167e05
--- /dev/null
+++ b/automation/autosubs_exportd.pl
@@ -0,0 +1,26 @@
+#!/usr/bin/env perl
+#
+# Script to automate MAGE-ML export for Tab2MAGE experiments which
+# have passed automated checking.
+#
+# $Id: autosubs_exportd.pl 1853 2007-12-13 17:53:43Z tfrayner $
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use ArrayExpress::AutoSubmission::Daemon::T2MExporter;
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+# Create the daemon object and start exporting.
+my $daemon = ArrayExpress::AutoSubmission::Daemon::T2MExporter->new({
+    polling_interval  => 5,
+    experiment_type   => 'Tab2MAGE',
+    autosubs_admin    => $CONFIG->get_AUTOSUBS_ADMIN(),
+    pipeline_subdir   => 'TABM',
+    accession_prefix  => 'E-TABM-',
+});
+
+$daemon->run();
diff --git a/automation/autosubs_httpd.pl b/automation/autosubs_httpd.pl
new file mode 100755
index 0000000..10493a3
--- /dev/null
+++ b/automation/autosubs_httpd.pl
@@ -0,0 +1,298 @@
+#!/usr/bin/env perl
+#
+# Web server daemon script to provide views onto automated submissions
+# processing.
+#
+# Tim Rayner 2006 EBI Microarray Informatics Team
+#
+# $Id: autosubs_httpd.pl 1853 2007-12-13 17:53:43Z tfrayner $
+#
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use HTTP::Daemon;
+use HTTP::Status;
+use HTTP::Response;
+use Proc::Daemon;
+use CGI;
+use Getopt::Long;
+
+require ArrayExpress::AutoSubmission::DB::Protocol;
+require ArrayExpress::AutoSubmission::DB::Experiment;
+require ArrayExpress::AutoSubmission::DB::ArrayDesign;
+
+########
+# SUBS #
+########
+
+sub create_table {
+
+    my ( $methods, $records, $sort_key ) = @_;
+
+    my $cgi = CGI->new;
+
+    my $content = q{};
+
+    $content .= $cgi->start_html(
+        -title => 'Curator Submissions Tracking',
+        -style => { src => '/style.css' }
+    );
+    $content .= $cgi->h1('Curator Submissions Tracking (deprecated interface)');
+
+    $content .= $cgi->ul(
+        $cgi->li(
+            $cgi->a( { href => '/mx_array.html' }, "MIAMExpress Arrays" )
+        ),
+        $cgi->li(
+            $cgi->a(
+                { href => '/mx_experiment.html' },
+                "MIAMExpress Experiments"
+            )
+	),
+        $cgi->li(
+            $cgi->a(
+                { href => '/t2m_experiment.html' },
+                "Tab2MAGE Experiments"
+            )
+	),
+	$cgi->li(
+            $cgi->a( { href => '/t2m_protocol.html' }, "Tab2MAGE Protocols" )
+	)
+    );
+
+    if ( $methods && scalar @$methods ) {
+        my $header_data = q{};
+        foreach my $method (@$methods) {
+            my $formatted_heading = ucfirst($method);
+            $formatted_heading =~ s/_(\w)/q{ } . ucfirst($1)/ge;
+            $header_data .= $cgi->th($formatted_heading);
+        }
+        my $table_header = $cgi->Tr($header_data);
+
+        if ( $records && scalar @$records ) {
+
+	    my @sorted =
+		map { $_->[0] }
+		reverse sort { $a->[1] <=> $b->[1] }
+		map { [ $_, $_->$sort_key ] }
+		@$records;
+
+            # Print out the whole table
+            my @table_rows;
+            foreach my $object (@sorted) {
+                my $row_data = q{};
+                foreach my $method (@$methods) {
+                    my $cell = $object->$method;
+
+                    # Colour up the status column values to highlight them.
+                    if ( $method =~ /\bstatus\b/i ) {
+                        my $color =
+                              ( $cell =~ /\bfail(?:ed|ure|ing)?\b/i ) ? q{red}
+                            : ( $cell =~ /\bpass(?:ed|ing)?\b/i )   ? q{green}
+                            : ( $cell =~ /\bwarn(?:ed|ings?)?\b/i ) ? q{blue}
+                            : q{};
+                        $cell = $color
+                            ? qq{<font color="$color">$cell</font>}
+                            : $cell;
+                    }
+                    $row_data .= $cgi->td($cell);
+                }
+                push( @table_rows, $cgi->Tr($row_data) );
+            }
+            $content
+                .= $cgi->table( { border => 1 }, $table_header, @table_rows );
+        }
+
+        else {
+
+            # Headings only
+            $content .= $cgi->table( { border => 1 }, $table_header );
+        }
+    }
+
+    $content .= $cgi->end_html;
+
+    return $content;
+}
+
+sub mx_array_list {
+    my @records = ArrayExpress::AutoSubmission::DB::ArrayDesign->search(
+	is_deleted => 0,
+    );
+    my @headings =
+      qw(
+      miamexpress_subid
+      accession
+      name
+      miamexpress_login
+      status
+      data_warehouse_ready
+      date_last_processed
+      comment
+    );
+    return ( 'text/html',
+        create_table( \@headings, \@records, 'miamexpress_subid' ) );
+}
+
+sub mx_experiment_list {
+    my @records = ArrayExpress::AutoSubmission::DB::Experiment->search(
+	experiment_type => 'MIAMExpress',
+	is_deleted      => 0,
+    );
+    my @headings =
+	qw(
+	   miamexpress_subid
+	   accession
+	   name
+	   miamexpress_login
+	   status
+	   data_warehouse_ready
+	   date_last_processed
+	   checker_score
+	   software
+	   comment
+	   curator
+       );
+    return ( 'text/html',
+        create_table( \@headings, \@records, 'miamexpress_subid' ) );
+}
+
+sub t2m_experiment_list {
+    my @records = ArrayExpress::AutoSubmission::DB::Experiment->search(
+	experiment_type => 'Tab2MAGE',
+	is_deleted      => 0,
+    );
+    my @headings =
+	qw(
+	   accession
+	   name
+	   user_id
+	   status
+	   data_warehouse_ready
+	   date_last_processed
+	   checker_score
+	   software
+	   in_curation
+	   comment
+	   curator
+       );
+    return ( 'text/html',
+        create_table( \@headings, \@records, 'id' ) );
+}
+
+sub t2m_protocol_list {
+    my @records = ArrayExpress::AutoSubmission::DB::Protocol->search(
+	is_deleted => 0,
+    );
+    my @headings =
+	qw(
+	   accession
+	   user_accession
+	   expt_accession
+	   name
+	   date_last_processed
+	   comment
+       );
+    return ( 'text/html',
+        create_table( \@headings, \@records, 'accession' ) );
+}
+
+########
+# MAIN #
+########
+
+my $DEBUG;
+GetOptions( "D|debug" => \$DEBUG );
+
+# Run in the background.
+unless ($DEBUG) {
+    print STDOUT "Starting HTTP Daemon...\n";
+    Proc::Daemon::Init;
+}
+
+# Set up the http daemon process.
+my $port = 8086;
+my $httpd = HTTP::Daemon->new( LocalPort => $port )
+    or die("Error: cannot bind to port $port: $!");
+print STDOUT ( "Started HTTP at " . $httpd->url() . "\n" ) if $DEBUG;
+
+# Ignore child process exits. This should cut down on zombie
+# processes.
+$SIG{CHLD} = 'IGNORE';
+
+# Hold the stylesheet in memory.
+my $stylesheet = join( "\n", <DATA> );
+
+# Supported page links should be enumerated here.
+my %dispatch = (
+    '/mx_array.html'      => \&mx_array_list,
+    '/mx_experiment.html' => \&mx_experiment_list,
+    '/t2m_protocol.html'  => \&t2m_protocol_list,
+    '/t2m_experiment.html'  => \&t2m_experiment_list,
+    '/style.css'          => sub { return ( 'text/css', $stylesheet ) },
+);
+
+# Get HTTP requests and respond to them in a forked process.
+while ( my $client = $httpd->accept() ) {
+    my $pid = fork();
+    if ( !$pid ) {
+
+        #### Child process ####
+        while ( my $request = $client->get_request() ) {
+
+            my $response = HTTP::Response->new(RC_OK);
+
+            if ( $request->method() eq 'GET' ) {
+                if ( my $url = $request->url()->path() ) {
+
+                    # See if the request is in the dispatch table.
+                    my ( $type, $content );
+                    if ( my $code_ref = $dispatch{$url} ) {
+                        ( $type, $content ) = $code_ref->();
+                    }
+
+                    # If everything worked, serve up the desired content.
+                    if ( $type && $content ) {
+                        $response->header( 'Content-Type' => $type );
+                        $response->content($content);
+                    }
+
+                    # Otherwise, fall back to a basic menu.
+                    else {
+                        $response->header( 'Content-Type' => 'text/html' );
+                        $response->content( create_table() );
+                    }
+                }
+            }
+            $client->send_response($response);
+
+            # Log the request.
+            print STDOUT (
+                $request->method() . "\n" . $request->url()->path() . "\n" )
+                if $DEBUG;
+        }
+
+        # Clean up.
+        $client->close();
+        undef($client);
+        exit();
+        #### End of Child process ####
+    }
+    #### Parent process ####
+}
+
+__DATA__
+body {
+  font-family: ariel, helvetica, sans-serif;
+  line-height: 120%;
+  background-color: #ccddff;
+}
+
+td, th {
+  background-color: #ddeeff;
+}
+
diff --git a/automation/calculate_celqc.pl b/automation/calculate_celqc.pl
new file mode 100755
index 0000000..d014fc5
--- /dev/null
+++ b/automation/calculate_celqc.pl
@@ -0,0 +1,52 @@
+#!/usr/bin/env perl
+#
+# Script to automate CEL QC checks run across the whole AE database,
+# processed in parallel using the LSF batch job submission system.
+#
+# $Id: calculate_celqc.pl 1960 2008-02-21 12:03:19Z tfrayner $
+
+use strict;
+use warnings;
+
+# NOTE: no "use lib" placeholder for this script, because it uses a
+# PATH specific to the LSF cluster which is maintained in the shell
+# wrapper for this script.
+
+use ArrayExpress::Tracking::QCJobManager;
+use ArrayExpress::Curator::Database qw(get_ae_dbh);
+
+# Create AE statement handle, start JobManager.
+my $dbh = get_ae_dbh();
+
+my $sth = $dbh->prepare(<<'QUERY', { ora_auto_lob => 0 });
+select di.identifier as identifier,
+ di.id as dbid,
+ bdc.dataformat as dataformat,
+ bdc.netcdf as lob
+from tt_identifiable i,
+ tt_experiment e,
+ tt_bioassaydata_t_experimen de,
+ tt_bioassaydata d,
+ tt_identifiable di,
+ tt_biodatacube bdc,
+ pl_label l
+where i.id=e.id
+and i.id=l.mainobj_id
+and e.id=de.t_experiment_id
+and de.bioassaydata_id=d.id
+and d.id=di.id
+and d.biodatavalues_id=bdc.id
+and bdc.dataformat in ('CELv3','CELv4')
+and i.identifier!='E-TABM-185'
+QUERY
+
+$sth->execute() or die($sth->errstr());
+
+# num_procs is the maximum number of concurrent processes.
+my $manager = ArrayExpress::Tracking::QCJobManager->new({
+    ae_sth      => $sth,
+    ae_dbh      => $dbh,
+    num_procs   => 20,
+});
+
+$manager->run();
diff --git a/automation/calculate_datahashes.pl b/automation/calculate_datahashes.pl
new file mode 100755
index 0000000..cad2b13
--- /dev/null
+++ b/automation/calculate_datahashes.pl
@@ -0,0 +1,289 @@
+#!/usr/bin/env perl
+#
+# Script to populate the tracking database with MD5 hashes of data
+# BLOBs loaded into ArrayExpress (tt_biodatacube.netcdf) for the
+# purposes of quality control. This script is designed to be run as a
+# cron job, around once per day.
+#
+# $Id: calculate_datahashes.pl 2069 2008-06-04 14:33:52Z tfrayner $
+
+use strict;
+use warnings;
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use Digest::MD5;
+use Getopt::Long;
+
+use ArrayExpress::AutoSubmission::DB::LoadedData;
+use ArrayExpress::AutoSubmission::DB::DataFormat;
+use ArrayExpress::AutoSubmission::DB::Experiment;
+use ArrayExpress::Curator::Database qw(get_ae_dbh);
+use ArrayExpress::Curator::Common qw(date_now);
+
+########
+# SUBS #
+########
+
+sub delete_unused_data {
+
+    my ( $ae_dbh ) = @_;
+
+    print qq{Scanning for recently deleted data objects...\n};
+
+    my $records = ArrayExpress::AutoSubmission::DB::LoadedData->search(
+	is_deleted => 0,
+    );
+
+    # FIXME modify this to confirm that there's a "valid" experiment
+    # accession; i.e. not E-XXXX-n_bad or whatever. Will probably need
+    # caching a big AE query.
+    my $ae_sth = $ae_dbh->prepare(<<'QUERY');
+select identifier
+from tt_identifiable
+where identifier=?
+QUERY
+
+    while ( my $data = $records->next() ) {
+	$ae_sth->execute( $data->identifier() ) or die( $ae_sth->errstr() );
+	my $results = $ae_sth->fetchall_arrayref();
+	unless ( scalar @{ $results } ) {
+	    printf( q{Deleting obsolete record "%s"...}, $data->identifier() );
+	    $data->set('is_deleted' => 1);
+	    $data->update();
+	    print qq{ done.\n};
+	}
+    }
+
+    $ae_sth->finish();
+
+    return;
+}
+
+sub get_old_hashed_data {
+
+    print( q{Caching experiment accessions and data identifiers...} );
+
+    my $hashed_data = {};
+
+    my $dbh = ArrayExpress::AutoSubmission::DB->db_Main();
+
+    my $sth = $dbh->prepare(<<'QUERY');
+select e.accession, d.identifier
+from experiments e, loaded_data d, experiments_loaded_data ed
+where e.id=ed.experiment_id
+and d.id=ed.loaded_data_id
+and d.is_deleted=0
+QUERY
+
+    $sth->execute() or die( $sth->errstr() );
+
+    while ( my $row = $sth->fetchrow_hashref( 'NAME_lc' ) ) {
+	$hashed_data->{ $row->{'accession'} }{ $row->{'identifier'} }++;
+    }
+
+    $sth->finish();
+
+    print( qq{ done.\n} );
+
+    return $hashed_data;
+}
+
+{
+
+    my $cached_query;
+
+sub needs_hashing {
+
+    my ( $row ) = @_;
+
+    # Quick look to see if the data has been already hashed.
+    $cached_query ||= get_old_hashed_data();
+    return if $cached_query->{ $row->{'accession'} }{ $row->{'identifier'} };
+
+    # More detailed look to check linkage etc.
+    my $experiment = ArrayExpress::AutoSubmission::DB::Experiment->find_or_create({
+	accession  => $row->{'accession'},
+	is_deleted => 0,
+    });
+    my %attached = map { $_->identifier() => 1 } $experiment->loaded_data();
+    if ( $attached{ $row->{'identifier'} } ) {
+
+	# Data file has been processed and is atteched.
+	return;
+    }
+
+    my @data = ArrayExpress::AutoSubmission::DB::LoadedData->search(
+	identifier => $row->{'identifier'},
+	is_deleted => 0,
+    );
+    if ( scalar @data ) {
+	foreach my $datum ( @data ) {    # Should be only one, but just in case...
+	    print qq{Attaching hash "$row->{identifier}"}
+		. qq{ to experiment "$row->{accession}"...\n};
+	    $experiment->add_to_loaded_data_instances({
+		loaded_data_id => $datum,
+	    });
+	}
+	return;
+    }
+
+    return $experiment;
+}
+
+}
+
+sub hash_row_data {
+
+    my ( $row, $experiment, $ae_dbh ) = @_;
+
+    print( qq{Hashing data for "$row->{identifier}"...} );
+
+    my $md5         = Digest::MD5->new();
+
+    my $lob_locator = $row->{'lob'};
+    my $length      = $ae_dbh->ora_lob_length( $lob_locator );
+    my $chunksize   = 65536;
+
+    # Read in the LOB and add it to a Digest::MD5 object.
+    for ( my $i = 1; $i <= $length; $i += $chunksize ) {
+	my $chunk = $ae_dbh->ora_lob_read( $lob_locator, $i, $chunksize );
+	$md5->add( $chunk );
+    }
+
+    # Create records in the SQLite DB for this data file.
+    my $format = ArrayExpress::AutoSubmission::DB::DataFormat->find_or_create({
+	name => $row->{'dataformat'},
+    });
+    my $datafile = ArrayExpress::AutoSubmission::DB::LoadedData->create({
+	identifier     => $row->{'identifier'},
+	data_format_id => $format,
+	md5_hash       => $md5->hexdigest(),
+	is_deleted     => 0,
+	needs_metrics_calculation => 1,
+	date_hashed    => date_now(),
+    });
+    $datafile->add_to_loaded_data_instances({
+	experiment_id => $experiment,
+    });
+
+    print( qq{ done.\n} );
+
+    return;
+}
+
+sub import_data_hashes {
+
+    my ( $dataformat, $ae_dbh ) = @_;
+
+    my $ae_sth = $ae_dbh->prepare(<<'QUERY', { ora_auto_lob => 0 });
+select i.identifier as accession,
+ di.identifier as identifier,
+ bdc.dataformat as dataformat,
+ bdc.netcdf as lob
+from tt_identifiable i,
+ tt_experiment e,
+ tt_bioassaydata_t_experimen de,
+ tt_bioassaydata d,
+ tt_identifiable di,
+ tt_biodatacube bdc,
+ pl_label l
+where i.id=e.id
+and i.id=l.mainobj_id
+and e.id=de.t_experiment_id
+and de.bioassaydata_id=d.id
+and d.id=di.id
+and d.biodatavalues_id=bdc.id
+and bdc.dataformat=?
+and i.identifier!='E-TABM-185'
+QUERY
+
+    $ae_sth->execute( $dataformat ) or die( $ae_sth->errstr() );
+
+    ROW:
+    while ( my $row = $ae_sth->fetchrow_hashref( 'NAME_lc' ) ) {
+
+	# Skip obviously bad accessions.
+	next ROW unless ( $row->{'accession'} =~ m/\A E-[A-Z]{4}-\d+[a-zA-Z]* \z/xms );
+
+	# Skip data files we've already processed.
+
+	# First, check this experiment to see if the file has already
+	# been processed and atteched.
+	my $experiment = needs_hashing( $row );
+	next ROW unless ( $experiment );
+
+	hash_row_data( $row, $experiment, $ae_dbh );
+    }
+
+    $ae_sth->finish();
+
+    return;
+}
+
+sub parse_args {
+
+    my ( $dataformat, $updatemode );
+
+    GetOptions(
+	"f|format=s" => \$dataformat,
+	"u|update"   => \$updatemode,
+    );
+
+    unless ( defined $dataformat || defined $updatemode ) {
+
+	print <<"USAGE";
+    Usage: $0 -f <data format>
+
+           This hashes all new data of the specified format.
+
+       or: $0 -u
+
+           This hashes all new data of the formats already
+           loaded into the tracking database. This will also
+           delete data hashes from the tracking database where
+           they have been deleted from ArrayExpress.
+
+    Example: $0 -f CELv3
+
+USAGE
+
+	exit 255;
+    }
+
+    # Quick sanity check - some things aren't really supported yet.
+    if ( $dataformat
+	     && $dataformat =~ /\A \b (tab|whitespace) [ ]* (delimited)? \z/ixms ) {
+	die("\n    I'm sorry Dave, I can't do that.\n\n  (Unsupported format: $dataformat)\n\n");
+    }
+
+    return ( $dataformat, $updatemode );
+}
+
+########
+# MAIN #
+########
+
+# Autoflush STDOUT.
+$| = 1;
+
+my ( $dataformat, $updatemode ) = parse_args();
+
+my $ae_dbh = get_ae_dbh();
+
+# Run the hashing function.
+if ( defined $dataformat ) {
+    import_data_hashes( $dataformat, $ae_dbh );
+}
+elsif ( defined $updatemode ) {
+
+    # Clean the table to remove data objects which no longer exist in AE.
+    delete_unused_data( $ae_dbh );
+
+    my $format = ArrayExpress::AutoSubmission::DB::DataFormat->retrieve_all();
+    while ( defined( my $df = $format->next() ) ) {
+	printf ( qq{Looking for new data in the "%s" format...\n}, $df->name() );
+	import_data_hashes( $df->name(), $ae_dbh );
+    }
+}
diff --git a/automation/cel_qcstats.pl b/automation/cel_qcstats.pl
new file mode 100755
index 0000000..a902203
--- /dev/null
+++ b/automation/cel_qcstats.pl
@@ -0,0 +1,181 @@
+#!/usr/bin/perl --
+#
+# Script to generate QC stats for Affy CEL files. Note that the CelQC
+# module requires the R executable to be on your path; you may also
+# need to set $R_LIBS to point to a suitable library directory, since
+# Affy annotation libraries will be automatically downloaded by R as
+# appropriate.
+#
+# N.B. the first line of this script needs to point to the perl interpreter.
+#
+# $Id: cel_qcstats.pl 2087 2008-06-25 10:57:21Z tfrayner $
+
+use strict;
+use warnings;
+
+# NOTE: no "use lib" placeholder for this script, because it uses a
+# PATH specific to the LSF cluster which is maintained in the shell
+# wrapper for this script.
+
+use Getopt::Long;
+use Benchmark;
+
+use ArrayExpress::Tracking::CelQC;
+use ArrayExpress::Curator::Common qw(date_now);
+
+########
+# SUBS #
+########
+
+sub parse_args {
+
+    my ( $identifier, $celfile, $quiet, $delete_cel, $want_help );
+
+    GetOptions(
+	"i|identifier=s" => \$identifier,
+	"f|file=s"       => \$celfile,
+	"q|quiet"        => \$quiet,
+	"d|delete"       => \$delete_cel,
+	"h|help"         => \$want_help,
+    );
+
+    if ( $want_help || ! defined $celfile ) {
+	print <<"USAGE";
+
+    Usage: $0 -f <cel file>
+
+    Options: -i  BioAssayData identifier, used to update the record in
+                   the tracking database.
+
+             -q  Suppress output, e.g. as part of a cron job.
+
+             -d  Delete CEL file upon completion (also used as part of a cron job).
+
+USAGE
+
+	exit 255;
+    }
+    unless ( -r $celfile ) {
+	die(qq{Error: CEL file "$celfile" does not exist or is unreadable.\n});
+    }
+
+    return ( $identifier, $celfile, $quiet, $delete_cel );
+}
+
+sub create_record {
+
+    my ( $loaded_data, $key, $value ) = @_;
+
+    require ArrayExpress::AutoSubmission::DB::QualityMetric;
+    require ArrayExpress::AutoSubmission::DB::Platform;
+
+    if ( lc( $key ) eq 'platform' ) {
+
+	# Platform is a special case.
+	my $platform = ArrayExpress::AutoSubmission::DB::Platform->find_or_create({
+	    name => $value,
+	});
+	$loaded_data->set('platform_id', $platform);
+	$loaded_data->update();
+    }
+    else {
+
+	# Everything else goes in quality_metrics. At the moment this
+	# is constrained to decimal type.
+	my $qm = ArrayExpress::AutoSubmission::DB::QualityMetric->find_or_create({
+	    type => $key,
+	});
+
+	my $qm_instances = $loaded_data->quality_metric_instances(
+	    quality_metric_id => $qm,
+	);
+	return if ( scalar $qm_instances );
+
+	$loaded_data->add_to_quality_metrics({
+	    loaded_data_id    => $loaded_data,
+	    quality_metric_id => $qm,
+	    value             => $value,
+	    date_calculated   => date_now(),
+	});
+    }
+
+    return;
+}
+
+########
+# MAIN #
+########
+
+my ( $identifier, $celfile, $quiet, $delete_cel ) = parse_args();
+
+my $loaded_data;
+if ( $identifier ) {
+
+    require ArrayExpress::AutoSubmission::DB::LoadedData;
+
+    my @data = ArrayExpress::AutoSubmission::DB::LoadedData->search(
+	identifier => $identifier,
+	is_deleted => 0,
+    );
+    unless ( scalar @data ) {
+	die(qq{Error: identifier "$identifier" not found in tracking DB});
+    }
+
+    $loaded_data = $data[0];
+
+    unless ( $loaded_data->needs_metrics_calculation() ) {
+	die(qq{Loaded data "$identifier" needs no metrics calculation.\n});
+    }
+}
+
+my $starttime = new Benchmark;
+
+my $qc = ArrayExpress::Tracking::CelQC->new({
+    input  => $celfile,
+    quiet  => $quiet,
+});
+
+# This may fail, in which case it will croak().
+my $result;
+eval { $result = $qc->run_metrics() };
+if ( $@ ) {
+
+    # Failure.
+    print("Error calculating metrics: $@");
+}
+else {
+
+    # Success.
+    my $endtime = new Benchmark;
+    my $timediff = timediff( $endtime, $starttime );
+    print STDOUT ( "\nTotal run time = ", timestr($timediff), "\n\n", );
+
+    if ( $loaded_data ) {
+
+	# Write the results to the database.
+	RESULT:
+	while ( my ( $key, $value ) = each %{ $result } ) {
+	    create_record( $loaded_data, $key, $value );
+	}
+    }
+    else {
+
+	foreach my $key (sort keys %$result) {
+	    print STDOUT (join("\t", $key, $result->{$key}), "\n");
+	}
+    }
+}
+
+if ( $loaded_data ) {
+
+    # Set our loaded_data as having had metrics calculation (or at
+    # least an attempt therof).
+    $loaded_data->set('needs_metrics_calculation', 0);
+    $loaded_data->update();
+}
+
+# Delete the cel file if we've been told to.
+if ( $delete_cel ) {
+    unlink($celfile) or die("Error deleting CEL file: $!");
+}
+
diff --git a/automation/database_overseer.pl b/automation/database_overseer.pl
new file mode 100755
index 0000000..b9f482a
--- /dev/null
+++ b/automation/database_overseer.pl
@@ -0,0 +1,170 @@
+#!/usr/bin/env perl
+#
+# Script to perform periodic checks and maintenance on the
+# autosubmissions database. Tasks include:
+#
+# 1. Query database for all Tab2MAGE experiments which have data
+# uploaded but appear to be abandoned, email submitter and ask them to
+# complete.
+#
+# [more to be added as necessary]
+#
+# $Id: database_overseer.pl 1960 2008-02-21 12:03:19Z tfrayner $
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use MIME::Lite;
+use Date::Manip qw(ParseDate DateCalc Delta_Format);
+use Getopt::Long;
+require ArrayExpress::AutoSubmission::DB::Experiment;
+use ArrayExpress::Curator::Common qw(date_now);
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+sub email_reminder {
+
+    my $submission = shift;
+
+    my $user     = $submission->user_id()->name();
+    my $type     = $submission->experiment_type();
+    my $expt     = $submission->name();
+    my $mailbody = (<<"MAILBODY");
+Dear $user,
+
+We are emailing you to query the status of your ongoing $type
+data submission, "$expt".
+
+Our records indicate that it has now been longer than a week since you
+last edited your experiment, and that you have successfully uploaded
+both a spreadsheet and accompanying data files. If your submission is
+now complete, please return to it and click on the final "submit" button
+to indicate to us that it is ready for processing. Until this is done,
+we will assume that your submission is not yet finished to your
+satisfaction.
+
+Best regards,
+
+The ArrayExpress Curation Team
+MAILBODY
+
+    my $mail = MIME::Lite->new(
+        From     => $CONFIG->get_AUTOSUBS_CURATOR_EMAIL(),
+        To       => $submission->user_id()->email(),
+        Subject  => "$type submission follow-up: $expt",
+        Encoding => 'quoted-printable',
+        Data     => $mailbody,
+        Type     => 'text/plain',
+    );
+
+    # Use the CONFIG SMTP server, if set.
+    if ( my $server = $CONFIG->get_AUTOSUBS_SMTP_SERVER() ) {
+	$mail->send('smtp', $server)
+	    or die("Error sending reminder email: $!");
+    }
+    else {
+	$mail->send()
+	    or die("Error sending reminder email: $!");
+    }
+
+    return;
+
+}
+
+########
+# MAIN #
+########
+
+my $help_wanted;
+GetOptions("h|help" => \$help_wanted,);
+
+if ($help_wanted) {
+
+    print (<<"USAGE");
+
+    Usage: $0
+
+This script is designed to be run as a cron job; it performs various periodic
+maintenance tasks on the autosubmissions database.
+
+USAGE
+
+    exit 255;
+}
+
+# Check for submissions that are (a) over a week since last edited,
+# (b) have both data files and spreadsheet uploaded, (c) are not
+# in_curation, and (d) have no date_last_processed or
+# date_last_processed is over a month ago. Email submitter to check
+# they haven't forgotten something, and set date_last_processed to
+# now.
+
+my $expt_iterator
+    = ArrayExpress::AutoSubmission::DB::Experiment->search(
+	is_deleted      => 0,
+	in_curation     => 0,
+	experiment_type => 'Tab2MAGE',
+    );
+
+SUBMISSION:
+while ( my $submission = $expt_iterator->next() ) {
+
+    # Skip clearly unfinished submissions.
+    unless ( scalar $submission->spreadsheets(is_deleted => 0)
+        && scalar $submission->data_files(is_deleted => 0) ) {
+        next SUBMISSION;
+    }
+
+    # Skip test submissions, and subs without users.
+    my $user = $submission->user_id();
+    if ( ! $user || ( $user->login() eq 'test' ) ) {
+	next SUBMISSION;
+    }
+
+    my $then = ParseDate( $submission->date_last_edited() );
+    next SUBMISSION unless $then;
+
+    my $now = ParseDate( date_now() );
+    die("Error parsing date today!") unless $now;
+
+    my $delta = DateCalc( $then, $now, 1 );
+
+    # Time elapsed in weeks.
+    my $elapsed_weeks = Delta_Format( $delta, 'approx', 2, '%wt' );
+
+    # Over a week since last edit.
+    if ( $elapsed_weeks > 1 ) {
+        my $last = ParseDate( $submission->date_last_processed() );
+
+        my $elapsed_months;
+        if ($last) {
+            my $procdelta   = DateCalc( $last, $now, 1 );
+            $elapsed_months = Delta_Format( $procdelta, 'approx', 2, '%Mt' );
+        }
+
+        # Either never processed, or over a month since last done.
+        if ( ! $last || ( $elapsed_months > 1 ) ) {
+
+	    # Make a note for cron feedback to admin.
+	    print STDOUT (
+		"Emailing ",
+		$user->login(),
+		" concerning submission ",
+		$submission->name(),
+		"\n",
+	    );
+
+            # Send the email, note the date.
+            email_reminder($submission);
+	    my $date = date_now();
+            $submission->set(
+		'date_last_processed' => $date,
+		'comment'             => "Emailed reminder on $date\n\n"
+		                            . ($submission->comment() || q{}),
+	    );
+            $submission->update();
+        }
+    }
+}
diff --git a/automation/magetab.cgi b/automation/magetab.cgi
new file mode 100755
index 0000000..f1e2dbe
--- /dev/null
+++ b/automation/magetab.cgi
@@ -0,0 +1,76 @@
+#!/usr/bin/env perl -wT
+#
+# Example instance script for tab2mage web submissions.
+#
+# $Id: submit.cgi 1595 2007-06-08 13:24:25Z tfrayner $
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use ArrayExpress::AutoSubmission::WebForm;
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+my $webapp = ArrayExpress::AutoSubmission::WebForm->new(
+    PARAMS => {
+	experiment_type  => 'MAGE-TAB',
+	spreadsheet_type => 'MAGE-TAB',
+	cgi_base         => '/cgi-bin/microarray/magetab.cgi',
+	stylesheet       => '/microarray/tab2mage.css',
+	intro_html_template    => 'magetab_intro.html',
+	upload_html_template   => 'magetab_upload.html',
+	template_html_template => 'magetab_template.html',
+	sidebar_icons   => [
+	    {
+		image       => '/microarray/aelogo.png',
+		destination => 'http://www.ebi.ac.uk/arrayexpress/',
+		alt         => 'ArrayExpress',
+	    },
+	    {
+		image       => '/microarray/MAGETAB_logo_small.png',
+		destination => 'http://tab2mage.sourceforge.net/docs/magetab_subs.html',
+		alt         => 'MAGE-TAB',
+		width       => 100,
+	    },
+	],
+	sidebar_links   => [
+	    {
+		text        => 'Submitting MAGE-TAB to ArrayExpress',
+		destination => 'http://tab2mage.sourceforge.net/docs/magetab_subs.html',
+	    },
+	    {
+		text        => 'MAGE-TAB overview',
+		destination => 'http://tab2mage.sourceforge.net/docs/magetab_docs.html',
+	    },
+	    {
+		text        => 'IDF detailed notes',
+		destination => 'http://tab2mage.sourceforge.net/docs/idf.html',
+	    },
+	    {
+		text        => 'SDRF detailed notes',
+		destination => 'http://tab2mage.sourceforge.net/docs/sdrf.html',
+	    },
+	    {
+		text        => 'MAGE-TAB specification',
+		destination => 'http://www.mged.org/mage-tab/',
+	    },
+	    {
+		text        => 'Microarray data submissions',
+		destination => 'http://www.ebi.ac.uk/microarray/submissions.html',
+	    },
+	    {
+		text        => 'Query ArrayExpress',
+		destination => 'http://www.ebi.ac.uk/arrayexpress/',
+	    },
+	    {
+		text        => 'Microarray group',
+		destination => 'http://www.ebi.ac.uk/microarray/',
+	    },
+	],
+    }
+);
+
+# Start the CGI script.
+$webapp->run();
diff --git a/automation/magetab_checkd.pl b/automation/magetab_checkd.pl
new file mode 100755
index 0000000..9d33dc6
--- /dev/null
+++ b/automation/magetab_checkd.pl
@@ -0,0 +1,26 @@
+#!/usr/bin/env perl
+#
+# Script to automate expt_check.pl running for newly-submitted MAGE-TAB experiments
+#
+# $Id: magetab_checkd.pl 1853 2007-12-13 17:53:43Z tfrayner $
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use ArrayExpress::AutoSubmission::Daemon::MAGETABChecker;
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+# Create the daemon object and start checking.
+my $daemon = ArrayExpress::AutoSubmission::Daemon::MAGETABChecker->new({
+    polling_interval  => 5,
+    checker_threshold => ($CONFIG->get_ERROR_INNOCENT()
+ 		        | $CONFIG->get_ERROR_MIAME()),
+    experiment_type   => 'MAGE-TAB',
+    accession_prefix  => 'E-MTAB-',
+    autosubs_admin    => $CONFIG->get_AUTOSUBS_ADMIN(),
+});
+
+$daemon->run();
diff --git a/automation/magetab_exportd.pl b/automation/magetab_exportd.pl
new file mode 100755
index 0000000..ffcf160
--- /dev/null
+++ b/automation/magetab_exportd.pl
@@ -0,0 +1,26 @@
+#!/usr/bin/env perl
+#
+# Script to automate MAGE-ML export for MAGE-TAB experiments which
+# have passed automated checking.
+#
+# $Id: magetab_exportd.pl 1853 2007-12-13 17:53:43Z tfrayner $
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use ArrayExpress::AutoSubmission::Daemon::MAGETABExporter;
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+# Create the daemon object and start exporting.
+my $daemon = ArrayExpress::AutoSubmission::Daemon::MAGETABExporter->new({
+    polling_interval  => 5,
+    experiment_type   => 'MAGE-TAB',
+    autosubs_admin    => $CONFIG->get_AUTOSUBS_ADMIN(),
+    pipeline_subdir   => 'MTAB',
+    accession_prefix  => 'E-MTAB-',
+});
+
+$daemon->run();
diff --git a/automation/magetab_insert_sub.pl b/automation/magetab_insert_sub.pl
new file mode 100755
index 0000000..05e0451
--- /dev/null
+++ b/automation/magetab_insert_sub.pl
@@ -0,0 +1,121 @@
+#!/usr/bin/env perl
+#
+# Script to add a new tab2mage submission to the autosubmissions
+# system (used e.g. for FTP uploaded submissions).
+#
+# $Id: magetab_insert_sub.pl 1843 2007-12-09 21:04:53Z tfrayner $
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use Getopt::Long;
+use ArrayExpress::AutoSubmission::DB;
+use ArrayExpress::AutoSubmission::Creator;
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(date_now);
+use ArrayExpress::MAGETAB::IDF;
+
+########
+# MAIN #
+########
+
+my ($idf, $sdrf_arg, $magetab_doc, $login, $accession);
+
+GetOptions(
+    "i|idf=s"         => \$idf,
+    "s|sdrf=s"        => \$sdrf_arg,
+    "m|magetab=s"     => \$magetab_doc,
+    "l|login=s"       => \$login,
+    "A|accession=s"   => \$accession,
+);
+
+unless ( ($idf || $magetab_doc) && $login) {
+
+    print STDERR (<<"USAGE");
+Usage: $0 -i <IDF file> -l <login> <list of data file archives>
+
+ Optional arguments:
+
+    -A <accession number to assign to this submission>
+
+    -s <SDRF file>
+
+If the IDF is insufficiently well formatted for the parser to retrieve
+the SDRF filename(s), then use -s to indicate the SDRF filename.
+
+    -m <Combined IDF+SDRF document>
+
+For combined MAGE-TAB documents such as those created by the template
+generation system, use the -m option to insert.
+
+USAGE
+
+    exit 255;
+}
+
+my @sdrfs;
+if ( defined($sdrf_arg) ) {
+    push @sdrfs, $sdrf_arg;
+}
+
+# Figure out the SDRF name if not given
+if ( $idf && ! scalar @sdrfs ) {
+    my $parser = ArrayExpress::MAGETAB::IDF->new({
+	idf             => $idf,
+	expt_accession  => 'DUMMY',
+	in_relaxed_mode => 1,
+    });
+
+    eval {
+	my $sdrf_ref;
+	(undef, undef, $sdrf_ref) = $parser->parse();
+	@sdrfs = @{ $sdrf_ref };
+    };
+    if ($@) {
+	die(  "Error parsing IDF file to retrieve SDRF filename. "
+	    . "Please try using the -s option. Error was as follows:\n\n" . $@);
+    }
+    else {
+	unless ( scalar @sdrfs ) {
+	    die("Error: No SDRFs found in IDF. Please use the -s option to include SDRFs.\n");
+	}
+    }
+}
+
+my $startfile = ($idf || $magetab_doc);
+
+# Quick sanity check on the file list.
+foreach my $file ($startfile, @sdrfs, @ARGV) {
+    die("Error: file not found: $file\n") unless ( defined($file) && -f $file && -r $file );
+}
+
+# Instantiate our Creator object.
+my $creator = ArrayExpress::AutoSubmission::Creator->new({
+    login           => $login,
+    name            => $startfile,
+    spreadsheet     => $startfile,
+    data_files      => [ @sdrfs, @ARGV ],
+    accession       => $accession,
+    experiment_type => 'MAGE-TAB',
+    comment         => 'Submission inserted manually',
+    clobber         => 0,
+});
+
+# Create the experiment and insert the spreadsheet.
+my $expt = $creator->get_experiment();
+
+# Copy the files to the submissions directory.
+print STDERR ("Copying files...\n");
+$creator->insert_spreadsheet();
+$creator->insert_data_files();
+
+# Now we're all set, release the hounds.
+$expt->set(
+    status          => $CONFIG->get_STATUS_PENDING(),
+    in_curation     => 1,
+    num_submissions => ( $expt->num_submissions() + 1 ),
+);
+$expt->update();
diff --git a/automation/mx_autocheck_daemon.pl b/automation/mx_autocheck_daemon.pl
new file mode 100755
index 0000000..c8f1f6f
--- /dev/null
+++ b/automation/mx_autocheck_daemon.pl
@@ -0,0 +1,30 @@
+#!/usr/bin/env perl
+#
+# Script to automate expt_check.pl running for newly-submitted MX experiments
+#
+# $Id: mx_autocheck_daemon.pl 1728 2007-08-31 16:09:26Z tfrayner $
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use ArrayExpress::AutoSubmission::Daemon::MXChecker;
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+# Create the daemon object and start checking.
+my $daemon = ArrayExpress::AutoSubmission::Daemon::MXChecker->new({
+    polling_interval  => 5,
+    checker_threshold => ($CONFIG->get_ERROR_INNOCENT()
+ 		        | $CONFIG->get_ERROR_MIAME()),
+    experiment_type   => 'MIAMExpress',
+    accession_prefix  => 'E-MEXP-',
+    autosubs_admin    => $CONFIG->get_MX_AUTOSUBS_ADMIN(),
+    mx_dsn            => $CONFIG->get_MX_DSN(),
+    mx_username       => $CONFIG->get_MX_USERNAME(),
+    mx_password       => $CONFIG->get_MX_PASSWORD(),
+    mx_dbparams       => $CONFIG->get_MX_DBPARAMS(),
+});
+
+$daemon->run();
diff --git a/automation/mx_export_daemon.pl b/automation/mx_export_daemon.pl
new file mode 100755
index 0000000..21580c8
--- /dev/null
+++ b/automation/mx_export_daemon.pl
@@ -0,0 +1,26 @@
+#!/usr/bin/env perl
+#
+# Script to automate MAGE-ML export for MIAMExpress experiments which
+# have passed automated checking.
+#
+# $Id: mx_export_daemon.pl 1728 2007-08-31 16:09:26Z tfrayner $
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use ArrayExpress::AutoSubmission::Daemon::MXExporter;
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+# Create the daemon object and start exporting.
+my $daemon = ArrayExpress::AutoSubmission::Daemon::MXExporter->new({
+    polling_interval  => 5,
+    experiment_type   => 'MIAMExpress',
+    accession_prefix  => 'E-MEXP-',
+    mx_export_command => $CONFIG->get_MX_MAGEML_EXPORT_COMMAND(),
+    autosubs_admin    => $CONFIG->get_MX_AUTOSUBS_ADMIN(),
+});
+
+$daemon->run();
diff --git a/automation/mx_reset_experiment.pl b/automation/mx_reset_experiment.pl
new file mode 100755
index 0000000..e59ca02
--- /dev/null
+++ b/automation/mx_reset_experiment.pl
@@ -0,0 +1,235 @@
+#!/usr/bin/env perl
+#
+# Script to reset a MIAMExpress experiment submission to pending, and
+# marking it for checking upon resubmission.
+#
+# $Id: mx_reset_experiment.pl 2013 2008-03-31 17:23:14Z tfrayner $
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use DBI;
+use Getopt::Long;
+use English qw( -no_match_vars );
+use Readonly;
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(date_now mx2tab);
+require ArrayExpress::AutoSubmission::DB::Experiment;
+
+Readonly my $EXPT_TYPE => 'MIAMExpress';
+Readonly my $CURATION  => 1;
+Readonly my $PENDING   => 0;
+
+########
+# SUBS #
+########
+
+sub reset_mx_tsubmis {
+
+    my ( $subid, $new_status ) = @_;
+
+    unless ( $CONFIG->get_MX_DSN() ) {
+        warn(     "Warning: MX_DSN constant not set. "
+                . "No connection made to MIAMExpress database." );
+        return;
+    }
+
+    my $dbh = DBI->connect(
+        $CONFIG->get_MX_DSN(),      $CONFIG->get_MX_USERNAME(),
+        $CONFIG->get_MX_PASSWORD(), $CONFIG->get_MX_DBPARAMS()
+        )
+        or die("Database connection error: $DBI::errstr\n");
+
+    my $sth = $dbh->prepare(<<'QUERY');
+select distinct TSUBMIS_SYSUID, TSUBMIS_PROC_STATUS
+from TSUBMIS, TEXPRMNT
+where TSUBMIS_SYSUID=?
+and TSUBMIS_SYSUID=TEXPRMNT_SUBID
+and TSUBMIS_DEL_STATUS='U'
+and TEXPRMNT_DEL_STATUS='U'
+QUERY
+
+    $sth->execute($subid) or die("$sth->errstr\n");
+    my $results = $sth->fetchall_hashref('TSUBMIS_SYSUID');
+    $sth->finish();
+
+    my $count = scalar( grep { defined $_ } values %$results );
+
+    unless ( $count == 1 ) {
+        die(      "Error: SubID $subid returns invalid "
+                . "number of MIAMExpress experiments: $count\n" );
+    }
+
+    my $old_status = $results->{$subid}{'TSUBMIS_PROC_STATUS'}
+        or warn("Warning: MIAMExpress TSUBMIS_PROC_STATUS is empty.\n");
+
+    $sth = $dbh->prepare(<<'QUERY');
+update TSUBMIS set TSUBMIS_PROC_STATUS=?
+where TSUBMIS_SYSUID=?
+and TSUBMIS_DEL_STATUS='U'
+QUERY
+
+    $sth->execute( $new_status, $subid ) or die("$sth->errstr\n");
+    $sth->finish();
+
+    $dbh->disconnect()
+        or die( "Database disconnection error: " . $dbh->errstr() . "\n" );
+
+    print STDOUT ( "MIAMExpress TSUBMIS table successfully updated "
+            . "($subid: changed from $old_status to $new_status).\n" );
+
+    return;
+}
+
+sub reset_accession_cache {
+
+    my ( $subid, $status, $in_curation, $experiment_type ) = @_;
+
+    # In future this will be a MAGE-TAB experiment that we reset to MIAMExpress.
+    my @experiments
+        = ArrayExpress::AutoSubmission::DB::Experiment->search(
+	    miamexpress_subid => $subid,
+	    is_deleted        => 0,
+	);
+
+    if ( scalar @experiments == 1 ) {
+
+	my $expt = $experiments[0];
+
+	# Sometimes submissions are forced to export; we handle the
+	# requisite MX to MAGE-TAX export here.
+	if ( $status eq $CONFIG->get_STATUS_PASSED()
+		 && $expt->experiment_type() eq 'MIAMExpress' ) {
+	    mx2tab($expt);
+	}
+
+	$experiment_type ||= $expt->experiment_type();
+
+        $expt->set(
+            status              => $status,
+            date_last_processed => date_now(),
+            curator             => getlogin,
+	    in_curation         => $in_curation,
+	    experiment_type     => $experiment_type,
+            comment             => (
+                $expt->status() eq $CONFIG->get_STATUS_CRASHED()
+                ? q{}
+                : $expt->comment()
+            ),
+        );
+	$expt->update();
+        print STDOUT (
+            qq{Accession table successfully updated ($subid set to "$status").\n}
+        );
+    }
+    elsif ( scalar @experiments > 1 ) {
+	warn (
+	    "Warning: Duplicate records found for SubID $subid in"
+		. " experiments table. Please fix and re-run this script.\n"
+	);
+    }
+    else {
+        warn (
+            "Warning: No submission with SubID $subid found in experiments table.\n"
+        );
+    }
+
+    return;
+}
+
+sub parse_args {
+
+    my %args;
+
+    GetOptions(
+	"p|pending" => \$args{pending},
+        "c|check"   => \$args{check},
+        "q|quick"   => \$args{quick},
+        "e|export"  => \$args{export},
+    );
+
+    unless ( ( $args{pending} || $args{check} || $args{quick} || $args{export} ) && @ARGV) {
+        print <<"NOINPUT";
+Usage: $PROGRAM_NAME <option> <list of submission ids>
+
+Options:  -c   set submission for full re-checking
+                 (sets experiment back to "MIAMExpress", checks database annotation and data files).
+
+          -q   set submission for quick re-checking
+                 (checks experiment as indicated by its current type;
+                    typically this will run annotation-only checks on MX MAGE-TAB experiments).
+
+          -e   set submission for MAGE-ML export without re-checking
+                 (experiment is exported according to its current type).
+
+          -p   set submission to pending status for user editing
+                 (sets experiment back to "MIAMExpress").
+
+NOINPUT
+
+        exit 255;
+    }
+
+    return \%args;
+
+}
+
+########
+# MAIN #
+########
+
+my $args = parse_args();
+
+foreach my $subid (@ARGV) {
+
+    if ( $args->{export} ) {
+
+        reset_mx_tsubmis( $subid, 'C' );
+        reset_accession_cache(
+	    $subid,
+	    $CONFIG->get_STATUS_PASSED(),
+	    $CURATION,
+	);
+    }
+    elsif ( $args->{quick} ) {
+
+        reset_mx_tsubmis( $subid, 'C' );
+        reset_accession_cache(
+	    $subid,
+	    $CONFIG->get_STATUS_PENDING(),
+	    $CURATION,
+	);
+    }
+    elsif ( $args->{check} ) {
+
+        reset_mx_tsubmis( $subid, 'C' );
+        reset_accession_cache(
+	    $subid,
+	    $CONFIG->get_STATUS_PENDING(),
+	    $CURATION,
+	    $EXPT_TYPE,
+	);
+    }
+    elsif ( $args->{pending} ) {
+
+        reset_mx_tsubmis( $subid, 'P' );
+        reset_accession_cache(
+	    $subid,
+	    $CONFIG->get_STATUS_PENDING(),
+	    $PENDING,
+	    $EXPT_TYPE,
+	);
+    }
+    else {
+	die("Error: Unrecognised user option.");
+    }
+}
+
+print STDOUT ("Done.\n\n");
+
+exit 0;
+
diff --git a/automation/populate_tracking_info.pl b/automation/populate_tracking_info.pl
new file mode 100755
index 0000000..5608948
--- /dev/null
+++ b/automation/populate_tracking_info.pl
@@ -0,0 +1,599 @@
+#!/usr/bin/env perl
+#
+# Script to populate the tracking database with information from the
+# ArrayExpress repository and data warehouse DB instances. The plan is
+# to incorporate elements of this code into a regular update
+# mechanism, running every 30 minutes or so. We also want a mechanism
+# for wiping all the imported data indiscriminately and repopulating.
+#
+# $Id: populate_tracking_info.pl 1898 2008-01-18 20:13:25Z tfrayner $
+
+use strict;
+use warnings;
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use Getopt::Long;
+use Carp;
+use List::Util qw(first);
+
+require ArrayExpress::AutoSubmission::DB::Experiment;
+require ArrayExpress::AutoSubmission::DB::ArrayDesign;
+require ArrayExpress::AutoSubmission::DB::Organism;
+require ArrayExpress::AutoSubmission::DB::Factor;
+require ArrayExpress::AutoSubmission::DB::QuantitationType;
+require ArrayExpress::AutoSubmission::DB::Event;
+
+require ArrayExpress::Tracking::Event;
+require ArrayExpress::Tracking::QueryHandler;
+
+sub update_toplevel_objects {
+
+    my ( $aedb ) = @_;
+
+    print STDOUT "Inserting new experiment records...\n";
+    my $ae_experiments = $aedb->get_experiments();
+    my @prev_expts = ArrayExpress::AutoSubmission::DB::Experiment->search(
+	is_deleted => 0,
+    );
+    my @prev_expt_accns = map { $_->accession() } @prev_expts;
+
+    EXPT:
+    foreach my $accession ( @$ae_experiments ) {
+
+	# Don't load obviously bad accessions.
+	next EXPT unless ( $accession =~ m/\A E-[A-Z]{4}-\d+[a-zA-Z]* \z/xms );
+
+	unless ( first { defined($_) && $_ eq $accession } @prev_expt_accns ) {
+	    my $expt = ArrayExpress::AutoSubmission::DB::Experiment->find_or_create({
+		accession  => $accession,
+		is_deleted => 0,
+	    });
+	    unless ( $expt->experiment_type() ) {
+		$expt->set(
+		    experiment_type => 'Unknown',
+		);
+		$expt->update();
+	    }
+	}
+    }
+    print STDOUT "Inserting new array design records...\n";
+    my $ae_arrays = $aedb->get_array_designs();
+    my @prev_arrays = ArrayExpress::AutoSubmission::DB::ArrayDesign->search(
+	is_deleted => 0,
+    );
+    my @prev_array_accns = map { $_->accession() } @prev_arrays;
+
+    ARRAY:
+    foreach my $accession ( @$ae_arrays ) {
+
+	# Don't load obviously bad accessions.
+	next ARRAY unless ( $accession =~ m/\A A-[A-Z]{4}-\d+[a-zA-Z]* \z/xms );
+
+	unless ( first { defined($_) && $_ eq $accession } @prev_array_accns ) {
+	    ArrayExpress::AutoSubmission::DB::ArrayDesign->find_or_create({
+		accession  => $accession,
+		is_deleted => 0,
+	    });
+	}
+    }
+
+    return;
+}
+
+sub delete_cached_data {
+
+    my ( $aedb, $todo ) = @_;
+
+    # The aedb query object contains information on what *can* be
+    # repopulated - and therefore deleted here.
+
+    # Delete all events associated with the database instances of interest.
+    if ( $todo->{events} ) {
+	foreach my $instance ( @{ $aedb->get_instances() } ) {
+	    print STDOUT ("Deleting old event data imported from $instance...\n");
+
+	    # FIXME consider trying this without using the iterator
+	    # approach? It might be faster.
+	    ArrayExpress::AutoSubmission::DB::Event->search(
+		target_db => $instance,
+	    )->delete_all();
+	}
+    }
+
+    # Delete metadata associated with experiments, array designs.
+    if ( $todo->{metadata} ) {
+	delete_cached_metadata( $aedb );
+    }
+
+    return;
+}
+
+sub delete_cached_metadata {
+
+    my ( $aedb ) = @_;
+
+    # Don't delete expt species (or anything else) for experiments not
+    # loaded... but then how to mark these as being updateable upon
+    # loading?
+
+    # Experiments.
+    my $ae_experiments = $aedb->get_experiments();
+
+    my $expt_iterator = ArrayExpress::AutoSubmission::DB::Experiment->search(
+	is_deleted => 0,
+    );
+
+    EXPT:
+    while ( my $expt = $expt_iterator->next() ) {
+	next EXPT unless ( first { $_ eq $expt->accession() } @$ae_experiments );
+
+	printf STDOUT ("Deleting metadata for %s...\n", $expt->accession());
+
+	# Setting release_date = undef is particularly important, sinc
+	# it is used to flag objects needing update.
+	$expt->set(
+	    submitter_description   => undef,
+	    curated_name            => undef,
+	    num_samples             => undef,
+	    num_hybridizations      => undef,
+	    has_raw_data            => undef,
+	    has_processed_data      => undef,
+	    release_date            => undef,
+	    is_released             => undef,
+	    ae_miame_score          => undef,
+	    ae_data_warehouse_score => undef,
+	);
+
+	$expt->array_design_instances()->delete_all();
+	$expt->organism_instances()->delete_all();
+	$expt->factor_instances()->delete_all();
+	$expt->quantitation_type_instances()->delete_all();
+	
+	$expt->update();
+    }
+
+    # Array Designs.
+    my $ae_arrays = $aedb->get_array_designs();
+
+    my $array_iterator = ArrayExpress::AutoSubmission::DB::ArrayDesign->search(
+	is_deleted => 0,
+    );
+
+    EXPT:
+    while ( my $array = $array_iterator->next() ) {
+	next EXPT unless ( first { $_ eq $array->accession() } @$ae_arrays );
+
+	printf STDOUT ("Deleting metadata for %s...\n", $array->accession());
+
+	# Setting release_date = undef is particularly important, sinc
+	# it is used to flag objects needing update.
+	$array->set(
+	    release_date            => undef,
+	    is_released             => undef,
+	);
+
+	$array->organism_instances()->delete_all();
+	
+	$array->update();
+    }
+
+    return;
+}
+
+sub update_events {
+
+    my ( $object, $aedb ) = @_;
+
+    # $object can be an experiment or array_design.
+
+    unless ( $object->accession() ) {
+	croak("Error: update_events called with an invalid experiment object (no accession).");
+    }
+
+    # N.B. we *do* process unloaded objects here.
+
+    my $events = $aedb->get_events( $object->accession() );
+
+    foreach my $event ( @{ $events } ) {
+	$object->add_to_events({
+	    event_type       => $event->get_event_type(),
+	    was_successful   => $event->get_success(),
+	    source_db        => $event->get_source_db(),
+	    target_db        => $event->get_target_db(),
+	    start_time       => $event->get_starttime(),
+	    end_time         => $event->get_endtime(),
+	    machine          => $event->get_machine(),
+	    operator         => $event->get_operator(),
+	    log_file         => $event->get_log_file(),
+	    jobregister_dbid => $event->get_jobregister_dbid(),
+	    comment          => $event->get_comment(),
+	    is_deleted       => 0,
+	});
+    }
+
+    return;
+}
+
+sub update_expt_metadata {
+
+    my ( $expt, $aedb ) = @_;
+
+    # FIXME not implemented: ae_data_warehouse_score.
+
+    my $acc = $expt->accession();
+
+    unless ( $acc ) {
+	croak("Error: update_expt_metadata called with an invalid experiment object (no accession).");
+    }
+
+    # Don't process unloaded experiments.
+    return unless ( $aedb->get_is_loaded( $acc ) );
+
+    my $has_metadata = $expt->release_date();
+
+    # Always update the date info, as it will change.
+    $expt->set(
+	release_date => ( $aedb->get_release_date($acc) || 0 ),
+    );
+    $expt->set(
+	is_released => $aedb->get_is_released($acc),
+    );
+    $expt->set(
+	in_data_warehouse => $aedb->get_expt_in_data_warehouse($acc),
+    );
+
+    unless ( $expt->curated_name() ) {
+	$expt->set(
+	    curated_name => $aedb->get_curated_name($acc),
+	);
+    }
+
+    # Skip metadata update for experiments having a release date (this
+    # is a fairly arbitrary shortcut to reduce processing time).
+    unless ( defined $has_metadata ) {
+	update_heavy_expt_queries( $expt, $aedb );
+    }
+
+    # Save any changes.
+    $expt->update();
+
+    return;
+}
+
+sub update_heavy_expt_queries {
+
+    my ( $expt, $aedb ) = @_;
+
+    my $acc = $expt->accession();
+
+    unless ( $acc ) {
+	croak("Error: update_heavy_expt_queries called with an invalid experiment object (no accession).");
+    }
+
+    # Only update the following if the metadatum is not present.
+    unless ( defined ($expt->submitter_description()) ) {
+	$expt->set(
+	    submitter_description => $aedb->get_submitter_description($acc),
+	);
+    }
+    unless ( defined ($expt->num_samples()) ) {
+	$expt->set(
+	    num_samples => $aedb->get_num_samples($acc),
+	);
+    }
+    unless ( defined ($expt->num_hybridizations()) ) {
+	$expt->set(
+	    num_hybridizations => $aedb->get_num_hybridizations($acc),
+	);
+    }
+    unless ( defined ($expt->has_raw_data()) ) {
+	$expt->set(
+	    has_raw_data => $aedb->get_has_raw_data($acc),
+	);
+    }
+    unless ( defined ($expt->has_processed_data()) ) {
+	$expt->set(
+	    has_processed_data => $aedb->get_has_processed_data($acc),
+	);
+    }
+    unless ( defined ($expt->ae_miame_score()) ) {
+	$expt->set(
+	    ae_miame_score => $aedb->get_ae_miame_score($acc),
+	);
+    }
+
+    # FIXME where do we even get this from?
+#    unless ( defined ($expt->ae_data_warehouse_score()) ) {
+#	$expt->set(
+#	    ae_data_warehouse_score => $aedb->get_ae_data_warehouse_score($acc),
+#	);
+#    }
+    unless ( scalar ($expt->organisms()) ) {
+	my $species_list = $aedb->get_expt_species($acc);
+	foreach my $species ( @$species_list ) {
+	    next unless defined ( $species );
+	    my $organism = ArrayExpress::AutoSubmission::DB::Organism->find_or_create({
+		scientific_name => $species,
+		is_deleted      => 0,
+	    });
+	    $expt->add_to_organism_instances({
+		experiment_id => $expt,
+		organism_id   => $organism,
+	    });
+	}
+    }
+    unless ( scalar ($expt->factors()) ) {
+	my $factor_list = $aedb->get_expt_factors($acc);
+	foreach my $factor_name ( @$factor_list ) {
+	    next unless defined ( $factor_name );
+	    my $factor = ArrayExpress::AutoSubmission::DB::Factor->find_or_create({
+		name       => $factor_name,
+		is_deleted => 0,
+	    });
+	    $expt->add_to_factor_instances({
+		experiment_id => $expt,
+		factor_id     => $factor,
+	    });
+	}
+    }
+    unless ( scalar ($expt->quantitation_types()) ) {
+	my $qt_list = $aedb->get_expt_qts($acc);
+	foreach my $qt_name ( @$qt_list ) {
+	    next unless defined ( $qt_name );
+	    my $qt = ArrayExpress::AutoSubmission::DB::QuantitationType->find_or_create({
+		name       => $qt_name,
+		is_deleted => 0,
+	    });
+	    $expt->add_to_quantitation_type_instances({
+		experiment_id        => $expt,
+		quantitation_type_id => $qt,
+	    });
+	}
+    }
+    unless ( scalar ($expt->array_designs()) ) {
+	my $array_list = $aedb->get_expt_arrays($acc);
+	foreach my $array_acc ( @$array_list ) {
+	    next unless defined ( $array_acc );
+	    my $array_design = ArrayExpress::AutoSubmission::DB::ArrayDesign->find_or_create({
+		accession  => $array_acc,
+		is_deleted => 0,
+	    });
+	    $expt->add_to_array_design_instances({
+		experiment_id   => $expt,
+		array_design_id => $array_design,
+	    });
+	}
+    }
+
+    # Save any changes.
+    $expt->update();
+
+    return;
+}
+
+sub update_array_metadata {
+
+    my ( $array, $aedb ) = @_;
+
+    # FIXME not implemented: annotation_source, annotation_version, biomart_table_name.
+
+    my $acc = $array->accession();
+
+    unless ( $acc ) {
+	croak("Error: update_array_metadata called with an invalid array design object (no accession).");
+    }
+
+    # Don't process unloaded array_designs.
+    return unless ( $aedb->get_is_loaded( $acc ) );
+
+    my $has_metadata = $array->release_date();
+
+    $array->set(
+	release_date => ( $aedb->get_release_date($acc) || 0 ),
+    );
+    $array->set(
+	is_released => $aedb->get_is_released($acc),
+    );
+    $array->set(
+	in_data_warehouse => $aedb->get_array_in_data_warehouse($acc),
+    );
+
+    # Skip metadata update for array designs having a release date
+    # (this is a fairly arbitrary shortcut to reduce processing time).
+    unless ( defined $has_metadata ) {
+	update_heavy_array_queries( $array, $aedb );
+    }
+
+    # Save any changes.
+    $array->update();
+
+    return;
+}
+
+sub update_heavy_array_queries {
+
+    my ( $array, $aedb ) = @_;
+
+    my $acc = $array->accession();
+
+    unless ( $acc ) {
+	croak("Error: update_heavy_array_queries called with an invalid array design object (no accession).");
+    }
+
+    unless ( scalar ($array->organisms()) ) {
+	my $species_list = $aedb->get_array_species($acc);
+	foreach my $species ( @$species_list ) {
+	    my $organism = ArrayExpress::AutoSubmission::DB::Organism->find_or_create({
+		scientific_name => $species,
+		is_deleted      => 0,
+	    });
+	    $array->add_to_organism_instances({
+		array_design_id => $array,
+		organism_id     => $organism,
+	    });
+	}
+    }
+
+    # Save any changes.
+    $array->update();
+
+    return;
+}
+
+sub max_job_dbids {
+
+    my ( $aedb ) = @_;
+
+    my %last_jobid;
+    foreach my $instance ( @{ $aedb->get_instances() } ) {
+	$last_jobid{$instance} =
+	    ArrayExpress::AutoSubmission::DB::Event
+		->sql_last_jobid('target_db')->select_val($instance);
+    }
+
+    return \%last_jobid;
+}    
+
+sub update_unfinished_events {
+
+    my ( $aedb ) = @_;
+
+    my $event_iterator = ArrayExpress::AutoSubmission::DB::Event->search(
+	was_successful => undef,
+	end_time       => undef,
+	is_deleted     => 0,
+    );
+
+    while ( my $event = $event_iterator->next() ) {
+	my ( $endtime, $success ) = $aedb->get_updated_event_data(
+	    $event->jobregister_dbid(),
+	    $event->target_db(),
+	);
+
+	if ( defined( $endtime ) || defined( $success ) ) {
+	    printf STDOUT ("Updating event %s...\n", $event->jobregister_dbid());
+	    $event->set(
+		end_time       => $endtime,
+		was_successful => $success,
+	    );
+	    $event->update();
+	}
+    }
+
+    return;
+}
+
+# Separate autosubs DB queries from AE/AEDW queries. Transparently
+# cache data (e.g. jobregister) retrieved from the AE databases.
+
+# Optional autosubs clearing step; don't delete qt, factor or
+# array_design instances, just the links to experiment.
+
+# Iterate over experiments, array designs and query the cached AE data
+# for results.
+
+my ( $repopulating, $event_only, $metadata_only, $want_help );
+
+GetOptions(
+    "R|repopulate"    => \$repopulating,
+    "E|event-only"    => \$event_only,
+    "M|metadata-only" => \$metadata_only,
+    "h|help"          => \$want_help,
+);
+
+if ( $want_help ) {
+
+    print <<"USAGE";
+
+    Usage: populate_tracking_info.pl
+
+    Optional arguments:
+
+	-R    Wipe the old tracking data and repopulate.
+        -E    Only update Event data.
+        -M    Only update Experiment and Array metadata.
+	-h    Print this help text.
+
+USAGE
+
+    exit 255;
+}
+
+# This hash is inspected to figure out which operations need to be
+# performed.
+my %todo = (  # N.B. careful about how you add entries to this hash.
+    events   => (not $metadata_only),
+    metadata => (not $event_only),
+);
+
+# One single ArrayExpress::Tracking::QueryHandler query object used
+# throughout. We want to use the largest jobregister_dbid as
+# last_jobid here so that updates work correctly.
+my $aedb = ArrayExpress::Tracking::QueryHandler->new();
+unless ( $repopulating ) {
+    my $last_jobid = max_job_dbids( $aedb );
+    $aedb->set_last_jobid( $last_jobid );
+}
+
+# The first thing to do is to make sure we have a full list of
+# every experiment and array design in the AE databases.
+update_toplevel_objects( $aedb );
+
+# FIXME we should consider wiping the release_date and is_released
+# data every time we re-run the metadata updates.
+
+# If we want a full repopulation we delete the old cached data here.
+if ( $repopulating ) {
+
+    print("WARNING: You have chosen to delete the following information, and repopulate from the AE databases:\n\n");
+    print("    " . join("; ", grep { $todo{$_} } keys %todo) . "\n\nProceed (Y/N)? ");
+
+    chomp( my $choice = lc <STDIN> );
+
+    unless( $choice eq 'y' ) {
+	print("User terminated script execution.\n");
+	exit 255;
+    }
+
+    delete_cached_data( $aedb, \%todo );
+}
+
+# Update experiments here.
+my $expt_iterator = ArrayExpress::AutoSubmission::DB::Experiment->search(
+    is_deleted => 0,
+);
+
+EXPT:
+while ( my $expt = $expt_iterator->next() ) {
+
+    # Skip experiments without assigned accessions.
+    next EXPT unless $expt->accession();
+
+    printf STDOUT ("Updating experiment %s...\n", $expt->accession());
+
+    update_events( $expt, $aedb )        if $todo{events};
+    update_expt_metadata( $expt, $aedb ) if $todo{metadata};
+}
+
+# Update array designs here.
+my $array_iterator = ArrayExpress::AutoSubmission::DB::ArrayDesign->search(
+    is_deleted => 0,
+);
+
+ARRAY_DESIGN:
+while ( my $array_design = $array_iterator->next() ) {
+
+    # Skip array designs without assigned accessions.
+    next ARRAY_DESIGN unless $array_design->accession();
+
+    printf STDOUT ("Updating array design %s...\n", $array_design->accession());
+
+    update_events( $array_design, $aedb )         if $todo{events};
+    update_array_metadata( $array_design, $aedb ) if $todo{metadata};
+}
+
+# Finally, update any previously-unfinished events.
+update_unfinished_events( $aedb ) if $todo{events};
+
diff --git a/automation/reset_experiment.pl b/automation/reset_experiment.pl
new file mode 100755
index 0000000..be57721
--- /dev/null
+++ b/automation/reset_experiment.pl
@@ -0,0 +1,168 @@
+#!/usr/bin/env perl
+#
+# Script to reset a Tab2MAGE experiment submission to pending, and
+# marking it for checking upon resubmission.
+#
+# $Id: reset_experiment.pl 2014 2008-04-01 15:03:56Z tfrayner $
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use Getopt::Long;
+use English qw( -no_match_vars );
+use Readonly;
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(date_now);
+require ArrayExpress::AutoSubmission::DB::Experiment;
+
+# These values are assumed to be numeric later.
+Readonly my $CURATION => 1;
+Readonly my $PENDING  => 0;
+
+########
+# SUBS #
+########
+
+sub reset_accession_cache {
+
+    my ( $id, $expt_type, $status, $in_curation ) = @_;
+
+    my @experiments
+        = ArrayExpress::AutoSubmission::DB::Experiment->search_like(
+	    id              => $id,
+	    experiment_type => $expt_type,
+	    is_deleted      => 0,
+	);
+
+    if ( scalar @experiments == 1 ) {
+
+	my $experiment = $experiments[0];
+
+	# We can't set MX experiments to pending, so we don't attempt it.
+	if ( $experiment->miamexpress_subid() && $in_curation == $PENDING ) {
+	    die("Error: This script is unable to set MIAMExpress experiments"
+	      . " back to pending. Please use the mx_reset_experiment.pl script.\n");
+	}
+
+        $experiment->set(
+            status              => $status,
+            date_last_processed => date_now(),
+            curator             => getlogin,
+	    in_curation         => $in_curation,
+            comment             => (
+                $experiment->status() eq $CONFIG->get_STATUS_CRASHED()
+                ? q{}
+                : undef
+            ),
+        );
+	$experiment->update();
+        printf STDOUT (
+            qq{Accession table successfully updated (%s_%i set to "%s").\n},
+	    $experiment->experiment_type(),
+	    $experiment->id(),
+	    $experiment->status(),
+        );
+	if ( $in_curation ) {
+	    print STDOUT (qq{Experiment is in curation.\n});
+	}
+	else {
+	    print STDOUT (qq{Experiment is pending.\n});
+	}
+    }
+    elsif ( scalar @experiments > 1 ) {
+        print STDERR (
+            "Error: Multiple $expt_type submissions with ID $id found in accession table. Skipping.\n"
+        );
+    }
+    else {
+        print STDERR (
+            "Error: No $expt_type submission with ID $id found in accession table. Skipping.\n"
+        );
+    }
+
+    return;
+}
+
+sub parse_args {
+
+    my %args;
+
+    GetOptions(
+	"p|pending" => \$args{pending},
+        "c|check"   => \$args{check},
+        "e|export"  => \$args{export},
+    );
+
+    unless ( ( $args{pending} || $args{check} || $args{export} ) && @ARGV) {
+        print <<"NOINPUT";
+Usage: $PROGRAM_NAME <option> <list of submission directories>
+
+Options:  -c   set submission for immediate re-checking
+          -e   set submission for MAGE-ML export without re-checking
+          -p   set submission to pending status for user editing
+
+   Note that -p will not work for MIAMExpress submissions exported to MAGE-TAB;
+   for such submissions use the mx_reset_experiment.pl script.
+
+NOINPUT
+
+        exit 255;
+    }
+
+    return \%args;
+
+}
+
+########
+# MAIN #
+########
+
+my $args = parse_args();
+
+SUBMISSION:
+foreach my $dirname (@ARGV) {
+
+    my ( $expt_type, $id ) = ($dirname =~ m/\A (.+) \_ (\d+) \z/xms);
+
+    unless ( $expt_type && $id ) {
+	warn("Error: cannot parse directory name $dirname. Skipping.\n");
+	next SUBMISSION;
+    }
+
+    if ( $args->{export} ) {
+        reset_accession_cache(
+	    $id,
+	    $expt_type,
+	    $CONFIG->get_STATUS_PASSED(),
+	    $CURATION,
+	);
+    }
+    elsif ( $args->{check} ) {
+        reset_accession_cache(
+	    $id,
+	    $expt_type,
+	    $CONFIG->get_STATUS_PENDING(),
+	    $CURATION,
+	);
+    }
+    elsif ( $args->{pending} ) {
+        reset_accession_cache(
+	    $id,
+	    $expt_type,
+	    $CONFIG->get_STATUS_PENDING(),
+	    $PENDING,
+	);
+    }
+    else {
+	die("Error: Unrecognised user option.");
+    }
+}
+
+print STDOUT ("Done.\n\n");
+
+exit 0;
+
diff --git a/automation/t2m_insert_sub.pl b/automation/t2m_insert_sub.pl
new file mode 100755
index 0000000..782bbe8
--- /dev/null
+++ b/automation/t2m_insert_sub.pl
@@ -0,0 +1,77 @@
+#!/usr/bin/env perl
+#
+# Script to add a new tab2mage submission to the autosubmissions
+# system (used e.g. for FTP uploaded submissions).
+#
+# $Id: t2m_insert_sub.pl 1843 2007-12-09 21:04:53Z tfrayner $
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use Getopt::Long;
+use ArrayExpress::AutoSubmission::DB;
+use ArrayExpress::AutoSubmission::Creator;
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(date_now);
+
+########
+# MAIN #
+########
+
+my ($spreadsheet, $login, $accession);
+
+GetOptions(
+    "e|spreadsheet=s" => \$spreadsheet,
+    "l|login=s"       => \$login,
+    "A|accession=s"   => \$accession,
+);
+
+unless ($spreadsheet && $login) {
+
+    print STDERR (<<"USAGE");
+Usage: $0 -e <spreadsheet> -l <login> <list of data file archives>
+
+ Optional argument:
+
+    -A <accession number to assign to this submission>
+
+USAGE
+
+    exit 255;
+}
+
+# Quick sanity check on the file list.
+foreach my $file ($spreadsheet, @ARGV) {
+    die("Error: file not found: $file\n") unless ( -f $file && -r $file );
+}
+
+# Instantiate our Creator object.
+my $creator = ArrayExpress::AutoSubmission::Creator->new({
+    login           => $login,
+    name            => $spreadsheet,
+    spreadsheet     => $spreadsheet,
+    data_files      => \@ARGV,
+    accession       => $accession,
+    experiment_type => 'Tab2MAGE',
+    comment         => 'Submission inserted manually',
+    clobber         => 0,
+});
+
+# Create the experiment and insert the spreadsheet.
+my $expt = $creator->get_experiment();
+
+# Copy the files to the submissions directory.
+print STDERR ("Copying files...\n");
+$creator->insert_spreadsheet();
+$creator->insert_data_files();
+
+# Now we're all set, release the hounds.
+$expt->set(
+    status          => $CONFIG->get_STATUS_PENDING(),
+    in_curation     => 1,
+    num_submissions => ( $expt->num_submissions() + 1 ),
+);
+$expt->update();
diff --git a/automation/tab2mage.cgi b/automation/tab2mage.cgi
new file mode 100755
index 0000000..f4b0f6a
--- /dev/null
+++ b/automation/tab2mage.cgi
@@ -0,0 +1,61 @@
+#!/usr/bin/env perl -wT
+#
+# Example instance script for tab2mage web submissions.
+#
+# $Id: tab2mage.cgi 1606 2007-06-15 15:08:56Z tfrayner $
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use ArrayExpress::AutoSubmission::WebForm;
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+my $webapp = ArrayExpress::AutoSubmission::WebForm->new(
+    PARAMS => {
+	experiment_type  => 'Tab2MAGE',
+	spreadsheet_type => 'Tab2MAGE',
+	cgi_base         => '/cgi-bin/microarray/tab2mage.cgi',
+	stylesheet       => '/microarray/tab2mage.css',
+	sidebar_icons   => [
+	    {
+		image       => '/microarray/aelogo.png',
+		destination => 'http://www.ebi.ac.uk/arrayexpress/',
+		alt         => 'ArrayExpress',
+	    },
+	    {
+		image       => '/microarray/T2M_logo_small.png',
+		destination => 'http://tab2mage.sourceforge.net/',
+		alt         => 'Tab2MAGE',
+		width       => 100,
+	    },
+	],
+	sidebar_links   => [
+	    {
+		text        => 'Introduction',
+		destination => 'http://www.ebi.ac.uk/miamexpress/help/tab2mage_help.html',
+	    },
+	    {
+		text        => 'Creating a spreadsheet',
+		destination => 'http://tab2mage.sourceforge.net/docs/spreadsheet.html',
+	    },
+	    {
+		text        => 'Microarray data submissions',
+		destination => 'http://www.ebi.ac.uk/microarray/submissions.html',
+	    },
+	    {
+		text        => 'Query ArrayExpress',
+		destination => 'http://www.ebi.ac.uk/arrayexpress/',
+	    },
+	    {
+		text        => 'Microarray group',
+		destination => 'http://www.ebi.ac.uk/microarray/',
+	    },
+	],
+    }
+);
+
+# Start the CGI script.
+$webapp->run();
diff --git a/bin/convert_to_mcmr.pl b/bin/convert_to_mcmr.pl
new file mode 100755
index 0000000..525aa92
--- /dev/null
+++ b/bin/convert_to_mcmr.pl
@@ -0,0 +1,116 @@
+#!/usr/bin/env perl
+#
+# Script to use Tab2MAGE routines to convert data files to MC/MR/C/R format.
+#
+# Tim Rayner 2005, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: convert_to_mcmr.pl 1852 2007-12-13 10:14:27Z tfrayner $
+#
+
+use strict;
+use warnings;
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+# Added to circumvent weird Exporter bug in 5.8.3 which caused
+# crashing when requiring ArrayExpress::Datafile (which in turn uses
+# ArrayExpress::Curator::Common).
+use ArrayExpress::Curator::Common;
+
+require ArrayExpress::Datafile;
+
+unless (@ARGV) {
+
+    print STDERR <<USAGE;
+Usage: 
+  
+  convert_to_mcmr.pl <list of files to convert>
+
+Converted files are named with a ".fixed" suffix.
+
+Supported formats:
+
+     GenePix
+     Agilent
+     ScanAlyze
+     ArrayVision
+     ScanArray
+     QuantArray
+     Spotfinder
+     BlueFuse
+     UCSFSpot
+     Nimblegen (use at your own risk)
+     CodeLink
+     AppliedBiosystems
+
+USAGE
+
+    exit;
+
+}
+
+FILENAME:
+foreach my $filename (@ARGV) {
+
+    my $file = ArrayExpress::Datafile->new({
+	name            => $filename,
+	data_type       => 'raw',
+	array_design_id => 'UNKNOWN',
+    });
+
+    unless ( -T $filename ) {
+        warn("ERROR: File $filename appears to be binary. Skipping.\n");
+        next FILENAME;
+    }
+
+    # Figure out what kind of file we have, get column headings and indices
+    $file->parse_header();
+
+    print STDOUT (
+        "File $filename is " . $file->get_format_type() . " format.\n" );
+
+    unless ( @{ $file->get_column_headings() } ) {
+        print STDERR (
+            "Error: No column headings for file " . $file->get_name() . "\n" );
+    }
+
+    # Convert the file to MC/MR/C/R coordinates. The file continues to
+    # be referred to by the $input_fh filehandle.
+    ( $file->get_format_type() eq 'Generic' ) && do {
+        print STDOUT (" File needs no conversion. Skipping.\n");
+        next FILENAME;
+    };
+
+    my $rc = $file->fix_known_text_format();
+
+    # Success
+    if ($rc) {
+
+	my $input_fh = $file->get_filehandle();
+
+        # Output the column headings and data
+        open (my $output_fh, '>', "$filename.fixed")
+            or die("Error opening output file $filename.fixed: $!\n");
+
+        print $output_fh ( join( "\t", @{ $file->get_column_headings() } ),
+            "\n" );
+
+        while ( my $line = <$input_fh> ) { print $output_fh $line; }
+
+	close ($output_fh) or die("Error closing filehandle: $!\n");
+
+    }
+
+    # Failure
+    else {
+
+        # Fallback error report
+        warn(     "ERROR: Unrecognized file format "
+                . $file->get_format_type()
+                . ". Skipping.\n" );
+        next FILENAME;
+
+    }
+
+}
diff --git a/bin/expt_check.pl b/bin/expt_check.pl
new file mode 100755
index 0000000..126a1c2
--- /dev/null
+++ b/bin/expt_check.pl
@@ -0,0 +1,454 @@
+#!/usr/bin/env perl
+#
+# expt_check.pl - Tim Rayner 2006 ArrayExpress team, EBI
+# See "expt_check.pl -h" for usage information.
+#
+# $Id: expt_check.pl 1709 2007-08-22 12:52:09Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../index.html">
+                <img src="T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Script detail: expt_check.pl</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+expt_check.pl - a script to check experiment data files submitted to MIAMExpress
+
+=head1 SYNOPSIS
+
+=over 2
+
+=item B<Tab2MAGE mode:>
+
+   expt_check.pl -e <Tab2MAGE spreadsheet>
+
+=item B<MAGE-TAB mode:>
+
+   expt_check.pl -i <IDF file>
+
+=item B<MIAMExpress mode:>
+
+   expt_check.pl -l <login name> -t <experiment title>
+
+=item B<Standalone mode:>
+
+   expt_check.pl -s <list of data files>
+
+   (use -A or -a to check files against an array design in standalone mode).
+
+=back
+
+=head1 DESCRIPTION
+
+This script can be used to check experimental data for submission to
+ArrayExpress in a few different ways. For Tab2MAGE submissions it
+parses the Tab2MAGE spreadsheet format and reports on problems with
+data files and MIAME metadata. Used in conjunction with a local
+MIAMExpress installation the script takes the experiment title and the
+submitter login name, and checks the submitted data files for
+errors. Several log files are written to the relevant MIAMExpress
+submission directory unless the B<-p> option is used to redirect them
+to the current directory. Normally the script will be able to figure
+out which array design to use from the Tab2MAGE spreadsheet or by
+querying the MIAMExpress database. An optional ADF filename argument
+may also be provided using the B<-a> option, and ArrayExpress
+accession numbers may be specified using the B<-A> option. Specifying
+your own ADFs/accession numbers will make the script ignore any array
+designs pointed to by the spreadsheet or database entry for the
+experiment.
+
+=head1 QUANTITATION TYPES
+
+Known QuantitationTypes are listed in a separate file or files,
+created with a simple tab-delimited format. The layout of
+these files is described in L<ArrayExpress::Datafile::QT_list>.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-e> C<spreadsheet filename>
+
+The Tab2MAGE spreadsheet to be checked.
+
+=item B<-i> C<IDF filename>
+
+The MAGE-TAB IDF file to be checked.
+
+=item B<-l> C<login name> 
+
+The MIAMExpress login name of the experiment submitter
+
+=item B<-t> C<experiment title>
+
+The MIAMExpress title of the experiment, surrounded by quotes if the title contains spaces.
+
+=item B<-a> C<ADF filename>
+
+The B<-a> switch designates the ADF filename to be used for all the
+hybridizations in the experiment. This option overrides any database
+links between hybridizations and array designs, and is provided
+initially as a convenience.
+
+=item B<-A> C<Array accession number (ArrayExpress)>
+
+Use the B<-A> switch to indicate the accession number of an
+ArrayExpress array design to be used for checking the data
+files. Ordinarily this should not be needed, as the script should be
+able to link the MIAMExpress submission with ArrayExpress array
+designs automatically.
+
+=item B<-c>
+
+Forces overwriting of existing files ("clobber"). If this switch is
+omitted the user will be asked whether to overwrite already existing
+files.
+
+=item B<-p> 
+
+Write to files in present working directory ("pwd"). The default is to
+write to files in the submission directory.
+
+=item B<-s> 
+
+Standalone option. The script will check the files listed on the
+command line rather than connecting to MIAMExpress and
+ArrayExpress. When used with the B<-e> or B<-i> options, the Tab2MAGE
+or MAGE-TAB document is checked but no connection is made to ArrayExpress to
+retrieve array information. To check features and reporter identifiers
+with this option, an ADF must be specified with the B<-a> option or an
+ArrayExpress accession number can be used with the B<-A> option. In
+the latter case a connection is made to ArrayExpress without
+connecting to MIAMExpress.
+
+=item B<-d> C<directory>
+
+Source directory. This indicates the directory to search for data
+files. This option is only used for Tab2MAGE submissions checks, as
+MIAMExpress defines its own directory structure which is automatically
+searched by this script. If this option is omitted, only the current
+working directory is searched for Tab2MAGE submission data files.
+
+=item B<-x>
+
+Skip data file checking. This option can be used to quickly check
+experiment annotation without having to wait for the script to
+validate all the data files.
+
+=item B<-q> C<QT filename>
+
+QuantitationType file. This option allows you to specify a custom
+QuantitationType definition file to override those defined in the
+ArrayExpress::Curator::Config module. See
+L<ArrayExpress::Datafile::QT_list> for more information.
+
+=item B<-Q> C<QT filename> 
+
+QuantitationType file. This option will add the new QuantitationType
+definitions to those included with the Tab2MAGE package. See
+L<ArrayExpress::Datafile::QT_list> for more information.
+
+=item B<-R> C<namespace>
+
+Prefix of the Reporter identifier to use when checking data files
+against array designs. This prefix is added to each "Reporter
+Identifier" in the data files prior to comparison with the actual
+identifiers in the ADF. The default is to assume MIAMExpress-like
+identifiers.
+
+=item B<-C> C<namespace>
+
+Prefix of the CompositeSequence identifier to use when checking data
+files against array designs. This prefix is added to each
+"CompositeSequence Identifier" in the data files prior to comparison
+with the actual identifiers in the ADF. The default is to assume
+MIAMExpress-like identifiers.
+
+=item B<-L>
+
+Ignore the data file size limit as configured in Config.yml (i.e.,
+MAX_DATAFILE_SIZE).
+
+=item B<-m>
+
+The checker will support MAGE-TAB documents in which a single IDF and
+SDRF have been combined (in that order), with the start of each
+section marked by [IDF] and [SDRF] respectively. Note that such
+documents are not compliant with the MAGE-TAB format specification;
+this format is used by ArrayExpress to simplify data submissions.
+
+=item B<-v>
+
+Prints the version number of the script.
+
+=item B<-h>
+
+Prints a short help text.
+
+=back
+
+=head1 TESTS
+
+There are numerous tests performed by this script. Listed below are
+the tests which are performed on each data file. There is also a
+series of checks which are made on any MIAME metadata supplied to the
+script. This metadata may be in the form of a Tab2MAGE spreadsheet, or
+an experiment submission in a local MIAMExpress database.  Please see
+L<ArrayExpress::Curator::ExperimentChecker/"TESTS"> for a list of
+general tests performed on all submissions.  See also
+L<ArrayExpress::Curator::Validate/"TESTS"> for the Tab2MAGE
+spreadsheet tests, L<ArrayExpress::MAGETAB::Checker/"TESTS"> for
+MAGE-TAB document checking, and
+L<ArrayExpress::Curator::MIAMExpress/"TESTS"> for more information on
+the MIAMExpress tests.
+
+=head1 LOCAL MIAMEXPRESS INSTALLATIONS
+
+Users wishing to use this script with local installations of
+MIAMExpress should check the ArrayExpress::Curator::Config module
+and change whatever parameters are necessary.
+
+=head1 SEE ALSO
+
+=over 1
+
+=item L<ArrayExpress::Curator::ExperimentChecker>
+
+=item L<ArrayExpress::Curator::Validate>
+
+=item L<ArrayExpress::MAGETAB::Checker>
+
+=item L<ArrayExpress::Curator::MIAMExpress>
+
+=item L<ArrayExpress::Datafile::QT_list>
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2004.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=head1 BUGS
+
+Affymetrix CHP files are not currently supported. Moreover, the script
+will not check the features referred to in CEL files against an array
+design until I can figure out how to retrieve a list of valid features
+in a timely fashion. CompositeSequence IDs in final data matrix
+files from Affymetrix submissions are fully supported and checked
+against the array design. Basic data quality calculations (percent
+null, Benfords law) are made on Affymetrix CEL and FGEM data.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use Benchmark;
+use Pod::Usage;
+use Getopt::Long qw(:config no_ignore_case);
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+###############
+# Subroutines #
+###############
+
+sub parse_args {
+
+    my ( %args, $want_help, $want_version );
+    GetOptions(
+        "a|adf=s"       => \$args{adf_filename},
+        "e|edf=s"       => \$args{edf_filename},
+        "i|idf=s"       => \$args{idf_filename},
+        "A|accession=s" => \$args{array_accession},
+        "l|login=s"     => \$args{login},
+        "t|title=s"     => \$args{title},
+        "p|pwd:s"       => \$args{pwd_log},
+        "c|clobber"     => \$args{clobber},
+	"x|skip"        => \$args{skip_data_checks},	
+        "s|standalone"  => \$args{standalone},
+	"d|source=s"    => \$args{source_directory},
+        "q|qtfile=s"    => \$args{qtfile},
+        "Q|QTFILE=s"    => \$args{QTFILE},
+        "R|reporter=s"  => \$args{reporter_prefix},
+        "C|compseq=s"   => \$args{compseq_prefix},
+	"m|magetab=s"   => \$args{magetab_doc},
+	"L|large-files" => \$args{ignore_size_limits},
+        "h|help"        => \$want_help,
+        "v|version"     => \$want_version,
+    );
+
+    if ($want_version) {
+        print STDERR (
+            "This is ", $CONFIG->get_EXPTCHECK_PROGNAME(),
+            " v", $CONFIG->get_EXPTCHECK_VERSION(), "\n\n"
+        );
+        exit 255;
+    }
+
+    if ($want_help) {
+	pod2usage(
+	    -exitval => 255,
+	    -output  => \*STDERR,
+	    -verbose => 1,
+	);
+    }
+
+    unless ( ( $args{login} && $args{title} )
+        || $args{standalone}
+        || $args{idf_filename}
+        || $args{edf_filename}
+        || $args{magetab_doc} ) {
+	pod2usage(
+	    -message => 'Please see "expt_check.pl -h" for further help notes.',
+	    -exitval => 255,
+	    -output  => \*STDERR,
+	    -verbose => 0,
+	);
+    }
+
+    # FIXME this could be better.
+    if ( ( $args{idf_filename} || $args{edf_filename} || $args{magetab_doc} )
+	     && ( $args{login} || $args{title} ) ) {
+	die("Error: ambiguous options used (mixed MIAMExpress and Tab2MAGE/MAGE-TAB).\n\n");
+    }
+
+    # Clean up this path a bit, make sure it's absolute
+    if ( $args{adf_filename} ) {
+        $args{adf_filename} = File::Spec->rel2abs( $args{adf_filename} );
+        die("Error: ADF file $args{adf_filename} is not readable")
+            unless ( -f $args{adf_filename} );
+    }
+
+    return ( \%args );
+}
+
+# End subroutines
+
+########
+# MAIN #
+########
+
+# Get our arguments
+my $args = parse_args();
+
+# Initialise some stuff.
+my $starttime = new Benchmark;
+
+# Arguments common to all ExperimentChecker objects.
+my %common_args = (
+    is_standalone        => $args->{standalone},
+    adf_filename         => $args->{adf_filename},
+    array_accession      => $args->{array_accession},
+    log_to_current_dir   => $args->{pwd_log},
+    clobber              => $args->{clobber},
+    qt_filename          => ( $args->{qtfile} || $args->{QTFILE} ),
+    include_default_qts  => defined( $args->{QTFILE} ),
+    skip_data_checks     => $args->{skip_data_checks},
+    source_directory     => $args->{source_directory},
+    reporter_prefix      => $args->{reporter_prefix},
+    compseq_prefix       => $args->{compseq_prefix},
+    ignore_size_limits   => $args->{ignore_size_limits},
+);
+
+my $checker;
+unless (  $args->{standalone}
+       || $args->{edf_filename}
+       || $args->{idf_filename}
+       || $args->{magetab_doc} ) {
+
+    # MIAMExpress checking.
+    require ArrayExpress::Curator::MIAMExpress;
+    $checker = ArrayExpress::Curator::MIAMExpress->new({
+	mx_login             => $args->{login},
+	mx_title             => $args->{title},
+	%common_args,
+    });
+}
+else {
+
+    if (  $args->{standalone}
+     && ! $args->{edf_filename}
+     && ! $args->{idf_filename}
+     && ! $args->{magetab_doc} ) {
+
+	# Standalone checking.
+	require ArrayExpress::Curator::Standalone;
+	$checker = ArrayExpress::Curator::Standalone->new({
+	    data_files       => \@ARGV,
+	    %common_args,
+	});
+    }
+    elsif ( $args->{edf_filename} ) {
+
+	# Tab2MAGE checking.
+	require ArrayExpress::Curator::Validate;
+	$checker = ArrayExpress::Curator::Validate->new({
+	    spreadsheet_filename => $args->{edf_filename},
+	    %common_args,
+	});
+    }
+    elsif ( $args->{idf_filename} ) {
+
+	# MAGE-TAB checking.
+	require ArrayExpress::MAGETAB::Checker;
+	$checker = ArrayExpress::MAGETAB::Checker->new({
+	    idf => $args->{idf_filename},
+	    %common_args,
+	});
+    }
+    elsif ( $args->{magetab_doc} ) {
+
+	# MAGE-TAB (combined IDF+SDRF) checking.
+	require ArrayExpress::MAGETAB::Checker;
+	$checker = ArrayExpress::MAGETAB::Checker->new({
+	    magetab_doc => $args->{magetab_doc},
+	    %common_args,
+	});
+    }
+    else {
+	die("Error: Ambiguous command-line options used.\n");
+    }
+}
+
+$checker->check();
+
+my $endtime = new Benchmark;
+my $timediff = timediff( $endtime, $starttime );
+print STDOUT ( "\nTotal run time = ", timestr($timediff), "\n\n", );
+
+exit $checker->get_error();
diff --git a/bin/magetab.pl b/bin/magetab.pl
new file mode 100755
index 0000000..ef793b3
--- /dev/null
+++ b/bin/magetab.pl
@@ -0,0 +1,361 @@
+#!/usr/bin/env perl
+#
+# magetab.pl
+#
+# A script to generate MAGE-ML from a MAGE-TAB document and set of
+# datafiles.
+#
+# Tim Rayner 2007 ArrayExpress Team, EBI
+#
+# $Id: magetab.pl 2069 2008-06-04 14:33:52Z tfrayner $
+#
+
+use strict;
+use warnings;
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use English qw( -no_match_vars );
+use Getopt::Long qw(:config no_ignore_case);
+use File::Spec;
+use Readonly;
+use Pod::Usage;
+use Benchmark;
+
+use ArrayExpress::MAGETAB;
+use ArrayExpress::Curator::Visualize qw( dot_and_png );
+
+Readonly my $APP_NAME => 'MAGETabulator';
+
+sub parse_args {
+
+    my %args;
+
+    my ( $want_version, $want_help );
+
+    GetOptions(
+        "i|idf=s"       => \$args{idf},
+	"n|accession=s" => \$args{expt_accession},
+        "t|target=s"    => \$args{target_directory},
+	"d|source=s"    => \$args{source_directory},
+        "q|qtfile=s"    => \$args{QT_file},
+        "Q|QTFILE=s"    => \$args{included_QTs},
+        "k|all-qts"     => \$args{keep_all_qts},
+        "K|keep-accns"  => \$args{keep_protocol_accns},
+	"R|reporter=s"  => \$args{reporter_prefix},
+	"C|compseq=s"   => \$args{compseq_prefix},
+        "s|standalone"  => \$args{is_standalone},
+        "P|plain-text"  => \$args{text_datafiles},
+	"L|large-files" => \$args{ignore_size_limits},
+        "x|skip-data"   => \$args{skip_datafiles},
+	"m|magetab=s"   => \$args{magetab_doc},
+        "f|font=s"      => \$args{font},
+        "v|version"     => \$want_version,
+        "h|help"        => \$want_help,
+	"c|clobber"     => \$args{clobber},
+    );
+
+    if ($want_version) {
+        print STDERR "This is $APP_NAME v$ArrayExpress::MAGETAB::VERSION\n\n";
+        exit 255;
+    }
+
+    if ($want_help) {
+	pod2usage(
+	    -exitval => 255,
+	    -output  => \*STDERR,
+	    -verbose => 1,
+	);
+    }
+
+    unless ( ( $args{idf} || $args{magetab_doc} )
+		  && $args{target_directory} && $args{expt_accession} ) {
+	pod2usage(
+	    -message => 'Please see "magetab.pl -h" for further help notes.',
+	    -exitval => 255,
+	    -output  => \*STDERR,
+	    -verbose => 0,
+	);
+    }
+
+    return \%args;
+
+}
+
+########
+# MAIN #
+########
+
+# Get our command-line options.
+my $args = parse_args();
+
+# Note the starting time.
+my $starttime = new Benchmark;
+
+# Initialise our parser object.
+my %parser_opts = (
+    authority                 => 'ebi.ac.uk',
+    namespace                 => 'MAGETabulator',
+    expt_accession            => $args->{expt_accession},
+    output_file               => "$args->{expt_accession}.xml",
+    target_directory          => $args->{target_directory},
+    source_directory          => $args->{source_directory},
+    is_standalone             => $args->{is_standalone},
+    qt_filename               => ( $args->{QT_file} || $args->{included_QTs} ),
+    include_default_qts       => defined( $args->{included_QTs} ),
+    keep_all_qts              => $args->{keep_all_qts},
+    keep_protocol_accns       => $args->{keep_protocol_accns},
+    reporter_prefix           => $args->{reporter_prefix},
+    compseq_prefix            => $args->{compseq_prefix},
+    use_plain_text            => $args->{text_datafiles},
+    ignore_size_limits        => $args->{ignore_size_limits},
+    skip_datafiles            => $args->{skip_datafiles},
+    clobber                   => $args->{clobber},
+    protocol_accession_prefix => 'P-MTAB-',
+);
+
+my $magetab;
+if ( $args->{idf} ) {
+    $magetab = ArrayExpress::MAGETAB->new({
+	idf => $args->{idf},
+	%parser_opts,
+    });
+}
+elsif ( $args->{magetab_doc} ) {
+    $magetab = ArrayExpress::MAGETAB->new({
+	magetab_doc => $args->{magetab_doc},
+	%parser_opts,
+    });
+    $magetab->parse_magetab_doc();
+}
+else {
+    die("Error: Either IDF or MAGETAB doc required by script.");
+}
+
+# Parse the files and write out the MAGE-ML
+$magetab->write_mageml();
+
+# Create graphs
+my $graphfile_base = File::Spec->catfile(
+    $args->{target_directory},
+    $args->{expt_accession},
+);
+
+my $classes = {
+    'source'            => 'Source',
+    'sample'            => 'Sample',
+    'extract'           => 'Extract',
+    'labeledextract'    => 'Labeled Extract',
+    'pba'               => 'Hybridization',
+    'mba'               => 'Feature Extraction',
+    'datamatrix_mba'    => 'Array Data',
+    'dba'               => 'Normalization',
+    'datamatrix_dba'    => 'Array Data',
+};
+
+$args->{clobber} = dot_and_png(
+    $magetab->get_bags(),
+    $graphfile_base,
+    $args->{font},
+    $magetab->get_clobber(),
+    $classes,
+);
+
+# We're done. Write out our benchmark info and exit.
+print STDOUT ("Finished.\n");
+
+my $endtime = new Benchmark;
+my $timediff = timediff( $endtime, $starttime );
+print STDOUT ( "\nTotal run time = ", timestr($timediff), "\n\n", );
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../index.html">
+                <img src="MAGETAB_logo.png"
+                     border="0" height="50" alt="MAGE-TAB logo"></td>
+              </a>
+	    <td class="pagetitle">Script detail: magetab.pl</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+magetab.pl - a script to generate valid MAGE-ML from an input MAGE-TAB
+document.
+
+=head1 SYNOPSIS
+
+ magetab.pl -i <IDF filename> -n <experiment accession> -t <target directory>
+
+=head1 DESCRIPTION
+
+This script will process a MAGE-TAB document and a set of data files
+to generate valid MAGE-ML. The supported data file formats are listed
+in the accompanying documentation. The MAGE-TAB format specification
+may be downloaded from this link:
+
+ http://www.ebi.ac.uk/systems-srv/mp/file-exchange/MAGE-TABv1.0.tar.gz
+
+There is one auxilliary file which can be supplied alongside the data
+files. This file simply defines the QuantitationTypes which are to be
+extracted from the data files. This QuantitationType file is optional,
+however, as the script is supplied with a set of defaults determined
+by a survey of incoming QuantitationTypes by ArrayExpress
+curators. The script generates a log file in the target directory
+which details which columns have been ignored. To keep all
+unrecognized QuantitationTypes, invoke the script with the B<-k>
+option.
+
+=head1 OPTIONS
+
+=over 2
+
+=item B<-i> C<IDF>
+
+The MAGE-TAB IDF file to be parsed.
+
+=item B<-n> C<accession>
+
+The experiment accession to be used as the top-level Experiment
+identifier in the generated MAGE-ML.
+
+=item B<-t> C<directory>
+
+The target directory to be created. This directory will contain the
+MAGE-ML file and external data files ready for validation.
+
+=item B<-q> C<QT filename>
+
+QuantitationType file. This option allows you to specify a custom
+QuantitationType definition file to override those defined as part of
+the Tab2MAGE package. See L<ArrayExpress::Datafile::QT_list> for more
+information.
+
+=item B<-Q> C<QT filename>
+
+QuantitationType file. This option will add the new QuantitationType
+definitions to those included with the Tab2MAGE package. See
+L<ArrayExpress::Datafile::QT_list> for more information.
+
+=item B<-k>
+
+Keep all columns in the data files, regardless of whether they are
+recognized or not. Unrecognized QTs will be created as generic
+SpecializedQuantitationTypes in the output MAGE-ML.
+
+=item B<-K>
+
+If the autosubmissions system is configured, magetab.pl will
+automatically reassign protocol accessions to fit a local
+convention. Use the -K option to suppress this behaviour.
+
+=item B<-s>
+
+Standalone option. This prevents the script from attempting to connect
+to ArrayExpress to retrieve array information.
+
+=item B<-x>
+
+Skip data file processing during MAGE-ML generation. The script will
+attempt to generate MAGE-ML for the metadata contained in the IDF and
+SDRF only.
+
+=item B<-R> C<namespace>
+
+Reporter identifier prefix. By default the script uses the MIAMExpress
+convention for generating reporter identifiers. This option allows you
+to override this behaviour by supplying an alternate prefix for
+identifiers.
+
+=item B<-C> C<namespace>
+
+CompositeSequence identifier prefix. By default the script uses the
+MIAMExpress convention for generating composite sequence
+identifiers. This option allows you to override this behaviour by
+supplying an alternate prefix for identifiers.
+
+=item B<-d> C<directory>
+
+Source directory containing all the data files referenced in the
+SDRF. If this is omitted, the current working directory will be
+searched for data files.
+
+=item B<-P>
+
+By default, native (usually binary) file formats are used for
+Affymetrix CEL files and all NimbleScan (NimbleGen) files. This
+encoding uses far less overhead and retains the files in their
+original formats; this is often appealing to end-users. When used for
+ArrayExpress submissions, this option allows for such files to be
+directly downloadable from the ArrayExpress web interface. However, in
+unusual circumstances when you might wish to use plain-text
+encoding for these data files, use this option.
+
+=item B<-L>
+
+Ignore the data file size limit as configured in Config.yml (i.e.,
+MAX_DATAFILE_SIZE).
+
+=item B<-m>
+
+The parser will support MAGE-TAB documents in which a single IDF and
+SDRF have been combined (in that order), with the start of each
+section marked by [IDF] and [SDRF] respectively. Note that such
+documents are not compliant with the MAGE-TAB format specification;
+this format is used by ArrayExpress to simplify data submissions.
+
+=item B<-f> C<font name>
+
+Name of the font to be used for Graphviz-generated PNGs.
+
+=item B<-c>
+
+Overwrite preexisting files ("clobber" option).
+
+=item B<-v>
+
+Prints the version number of the script.
+
+=item B<-h>
+
+Print a short help text summarizing these options.
+
+=back
+
+=head1 QUANTITATIONTYPE FILE
+
+Please see L<ArrayExpress::Datafile::QT_list> for a description of
+the format of this file.
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2007.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
diff --git a/bin/parse_affy.pl b/bin/parse_affy.pl
new file mode 100755
index 0000000..840b3dd
--- /dev/null
+++ b/bin/parse_affy.pl
@@ -0,0 +1,249 @@
+#!/usr/bin/env perl
+#
+# Script to parse Affymetrix data files.
+# Shows example usage of ArrayExpress::Datafile::Affymetrix modules.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: parse_affy.pl 1852 2007-12-13 10:14:27Z tfrayner $
+#
+
+use strict;
+use warnings;
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+=pod
+
+=head1 NAME
+
+parse_affy.pl - an example Affymetrix data file parsing script. 
+
+=head1 SYNOPSIS
+
+ parse_affy.pl -i <Affymetrix data file (CEL or CHP)>
+               -o <tab-delimited data matrix output file>
+
+=head1 DESCRIPTION
+
+This script is a 'toy' script to illustrate the use of the
+ArrayExpress::Datafile::Affymetrix parsing module supplied with the
+Tab2MAGE distribution. It may nonetheless be useful in circumstances
+where other Affymetrix parsers fail. The script reads data files (CEL
+and CHP) in both old (version 3, GDAC) and new (version 4, XDA/GCOS)
+formats. The script then outputs any combination of the following: (a)
+a tab-delimited data matrix, (b) a set of MAGE identifiers making up
+the DesignElementDimension of the matrix, and/or (c) a set of
+identifiers comprising the QuantitationTypeDimension of the data
+matrix. Please see L<ArrayExpress::Datafile::Affymetrix> for more
+information on the Affymetrix parsing API.
+
+=head1 OPTIONS
+
+=over 2
+
+=item B<-i> C<input filename>
+
+Affymetrix CEL or CHP data file to parse.
+
+=item B<-o> C<output filename>
+
+File to be used for output (tab-delimited data).
+
+=item B<-d> C<DED filename>
+
+File to which the DesignElementDimension is to be written (optional).
+
+=item B<-q> C<QTD filename>
+
+File to which the QuantitationTypeDimension is to be written
+(optional).
+
+=item B<-c> C<input CDF filename>
+
+Affymetrix library CDF file to use for CHP file parsing.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments. 
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+use Getopt::Long;
+use IO::File;
+use Pod::Usage;
+use ArrayExpress::Datafile::Affymetrix;
+use ArrayExpress::Datafile::Binary qw(get_integer);
+
+sub parse_args {
+
+    my (%args, $want_help);
+    GetOptions(
+        "i|infile=s"  => \$args{infile},
+        "o|outfile=s" => \$args{outfile},
+        "d|dedfile=s" => \$args{dedfile},
+        "q|qtdfile=s" => \$args{qtdfile},
+        "c|cdf=s"     => \$args{cdf},
+	"h|help"      => \$want_help,
+    );
+
+    if ($want_help) {
+	pod2usage(
+	    -exitval => 255,
+	    -output  => \*STDERR,
+	    -verbose => 1,
+	);
+    }
+
+    unless ( $args{infile}
+        && ( $args{outfile} || $args{dedfile} || $args{qtdfile} ) ) {
+	pod2usage(
+	    -message => 'Please see "parse_affy.pl -h" for further help notes.',
+	    -exitval => 255,
+	    -output  => \*STDERR,
+	    -verbose => 0,
+	);
+    }
+    return \%args;
+}
+
+########
+# MAIN #
+########
+
+# Get our arguments
+my $args = parse_args();
+my $affy_factory = ArrayExpress::Datafile::Affymetrix->new();
+
+# Deal with CDF if supplied (XDA only for now)
+my $cdf;
+if ( $args->{cdf} ) {
+    $cdf = $affy_factory->make_parser($args->{cdf});
+    $cdf->parse();
+}
+
+# Open our file for input
+#my $infile = IO::File->new( $args->{infile}, '<' )
+#    or die("Error: cannot open input file $args->{infile}: $!\n");
+#binmode($infile);
+
+SWITCH: {
+    ( $args->{infile} =~ m/\.CEL$/i ) && do {    # CEL file
+
+        my $cel = $affy_factory->make_parser( $args->{infile} );
+
+        $cel->parse();
+        print( "Algorithm: ", $cel->get_algorithm, "\n" );
+        print( "NumCells:  ", $cel->get_num_cells, "\n" );
+
+        while ( my ( $key, $value ) = each %{ $cel->get_parameters() } ) {
+            print("$key: $value\n");
+        }
+
+        # Output
+        $args->{outfile} && do {
+            my $outfile = IO::File->new( $args->{outfile}, '>' )
+                or die(
+                "Error: cannot open data output file $args->{outfile}: $!\n");
+            $cel->export($outfile);
+        };
+
+        $args->{dedfile} && do {
+            my $dedfile = IO::File->new( $args->{dedfile}, '>' )
+                or die(
+                "Error: cannot open DED output file $args->{dedfile}: $!\n");
+            foreach my $identifier ( @{ $cel->get_ded() } ) {
+                print $dedfile ("$identifier\n");
+            }
+        };
+
+        $args->{qtdfile} && do {
+            my $qtdfile = IO::File->new( $args->{qtdfile}, '>' )
+                or die(
+                "Error: cannot open QTD output file $args->{qtdfile}: $!\n");
+            foreach my $identifier ( @{ $cel->get_qtd() } ) {
+                print $qtdfile ("$identifier\n");
+            }
+        };
+
+        last SWITCH;
+    };
+
+    ( $args->{infile} =~ m/\.CHP$/i ) && do {    # CHP file
+
+        my $chp = $affy_factory->make_parser( $args->{infile} );
+
+        unless ($cdf) {
+            die(      "Unable to export CHP data: no CDF supplied. "
+                    . "Please use the -c option to specify a CDF filename.\n"
+            );
+        }
+
+        $chp->parse();
+
+        # Output
+        $args->{outfile} && do {
+            my $outfile = IO::File->new( $args->{outfile}, '>' )
+                or die(
+                "Error: cannot open data output file $args->{outfile}: $!\n");
+            $chp->export( $outfile, $cdf );
+        };
+
+        $args->{dedfile} && do {
+            my $dedfile = IO::File->new( $args->{dedfile}, '>' )
+                or die(
+                "Error: cannot open DED output file $args->{dedfile}: $!\n");
+            foreach my $identifier ( @{ $chp->get_ded($cdf) } ) {
+                print $dedfile ("$identifier\n");
+            }
+        };
+
+        $args->{qtdfile} && do {
+            my $qtdfile = IO::File->new( $args->{qtdfile}, '>' )
+                or die(
+                "Error: cannot open QTD output file $args->{qtdfile}: $!\n");
+            foreach my $identifier ( @{ $chp->get_qtd() } ) {
+                print $qtdfile ("$identifier\n");
+            }
+        };
+
+        last SWITCH;
+    };
+
+    ( $args->{infile} =~ m/\.EXP$/i ) && do {    # EXP file
+
+        my $exp = $affy_factory->make_parser( $args->{infile} );
+
+        $exp->parse();
+
+        # Open our file for output
+        my $outfile = IO::File->new( $args->{outfile}, '>' )
+            or die("Error: cannot open file $args->{outfile}: $!\n");
+
+        $exp->export($outfile);
+
+        last SWITCH;
+    };
+
+    die("Error: Unknown file extension.\n");
+}
+
diff --git a/bin/t2m_visualize.pl b/bin/t2m_visualize.pl
new file mode 100755
index 0000000..aaca75b
--- /dev/null
+++ b/bin/t2m_visualize.pl
@@ -0,0 +1,181 @@
+#!/usr/bin/env perl
+#
+# t2m_vizualizer.pl
+#
+# A script to create a graph from a Tab2MAGE spreadsheet, allowing the
+# user to visualize the relationships between biomaterials and
+# hybridizations.
+#
+# Tim Rayner 2005 ArrayExpress Team, EBI
+#
+# $Id: t2m_visualize.pl 1519 2007-04-19 13:39:57Z tfrayner $
+#
+
+use strict;
+use warnings;
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../index.html">
+                <img src="T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Script detail: t2m_visualize.pl</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+t2m_visualize.pl - a rapid visualization script for Tab2MAGE
+spreadsheets.
+
+=head1 SYNOPSIS
+
+ t2m_visualize.pl -e <Tab2MAGE spreadsheet file>
+
+=head1 DESCRIPTION
+
+This script simply parses a Tab2MAGE spreadsheet into a format
+readable by the Graphviz "dot" application. If Graphviz has been
+installed the script then attempts to generate a PNG file. This PNG
+file contains a graph which represents the experiment which has been
+coded. Both the tab2mage.pl and expt_check.pl scripts include this
+functionality, but t2m_visualize.pl has two advantages: it does not
+need the data files to be present, and it works much faster.
+
+=head1 OPTIONS
+
+=over 2
+
+=item B<-e> C<spreadsheet filename>
+
+The Tab2MAGE spreadsheet to visualize.
+
+=item B<-f> C<font name>
+
+Font to use in graph generation (optional). 
+
+=item B<-v>
+
+Print the version of the string.
+
+=item B<-c>
+
+Overwrite ("clobber") existing files.
+
+=item B<-h>
+
+Prints a short help text.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments. 
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+use Getopt::Long;
+use Pod::Usage;
+
+use ArrayExpress::Curator::Tab2MAGE;
+use ArrayExpress::Curator::MAGE qw(unique_identifier);
+use ArrayExpress::Curator::Visualize qw(dot_and_png);
+
+###########
+# GLOBALS #
+###########
+#
+# Used to track the creation of unique objects.
+#
+
+my $PROGRAMNAME = "T2M_Visualizer";
+my $VERSION     = "1.2";
+
+my $logfile = lc($PROGRAMNAME) . ".log";
+
+########
+# MAIN #
+########
+
+my ( $spreadsheet, $want_version, $font, $clobber, $want_help );
+
+GetOptions(
+    "e|edf=s"   => \$spreadsheet,
+    "f|font=s"  => \$font,
+    "v|version" => \$want_version,
+    "c|clobber" => \$clobber,
+    "h|help"    => \$want_help,
+);
+
+if ($want_version) {
+    print STDERR "This is $PROGRAMNAME v$VERSION\n\n";
+    exit 255;
+}
+
+if ($want_help) {
+    pod2usage(
+	-exitval => 255,
+	-output  => \*STDERR,
+	-verbose => 1,
+    );
+}
+
+unless ($spreadsheet) {
+    pod2usage(
+	-message => 'Please see "t2m_visualize.pl -h" for further help notes.',
+	-exitval => 255,
+	-output  => \*STDERR,
+	-verbose => 0,
+    );
+}
+
+my $tab2mage = ArrayExpress::Curator::Tab2MAGE->new({
+    spreadsheet_filename => $spreadsheet,
+    target_directory     => q{.},
+});
+
+# Parse the EDF
+$tab2mage->read_edf( \*STDOUT );
+
+# Create and link the mage objects
+foreach my $datarow (@{ $tab2mage->get_hyb_section() } ) {
+    $tab2mage->create_mage_objects( $datarow, unique_identifier );
+}
+
+# Create the dotfile
+my $output_base = $spreadsheet;
+$output_base =~ s/\.\w{3}$//;
+$output_base ||= lc($PROGRAMNAME);    # Just in case
+
+$clobber = dot_and_png( $tab2mage->get_bags(), $output_base, $font, $clobber );
+
+exit $tab2mage->get_error();
diff --git a/bin/tab2mage.pl b/bin/tab2mage.pl
new file mode 100755
index 0000000..0e9968e
--- /dev/null
+++ b/bin/tab2mage.pl
@@ -0,0 +1,388 @@
+#!/usr/bin/env perl
+#
+# tab2mage.pl
+#
+# A script to generate MAGE-ML from a set of datafiles. A template
+# spreadsheet file (tab-delimited text) is required.
+#
+# Tim Rayner 2004 ArrayExpress Team, EBI
+#
+# $Id: tab2mage.pl 1734 2007-09-05 15:41:17Z tfrayner $
+#
+
+use strict;
+use warnings;
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use English qw( -no_match_vars );
+use Benchmark;
+use File::Spec;
+use Getopt::Long qw(:config no_ignore_case);
+use Pod::Usage;
+
+use ArrayExpress::Curator::Tab2MAGE;
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Visualize qw(dot_and_png);
+
+########
+# SUBS #
+########
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../index.html">
+                <img src="T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Script detail: tab2mage.pl</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+tab2mage.pl - a script to produce valid MAGE-ML from a set of
+raw datafiles and a summary spreadsheet of defined format.
+
+=head1 SYNOPSIS
+
+ tab2mage.pl -e <spreadsheet_filename> -t <target_directory>
+
+=head1 DESCRIPTION
+
+This script is designed to take a set of unprocessed raw datafiles and
+a spreadsheet providing the experiment metadata and generate valid
+MAGE-ML. The supported data file formats are listed in the
+accompanying documentation.
+
+Important: the data files must each contain a column heading line
+immediately preceding the start of the data. Typically, raw
+unprocessed data files have this as standard. Do not remove this line,
+as the script will use it to determine which QuantitationTypes to keep
+and which to discard.
+
+There are two auxilliary files which can be supplied alongside the
+data files. The first, experiment summary spreadsheet describes the
+way in which the experiment was performed. The second simply defines
+the QuantitationTypes which are to be extracted from the data
+files. This QuantitationType file is optional, however, as the script
+is supplied with a set of defaults determined by a survey of incoming
+QuantitationTypes by ArrayExpress curators. The script generates a log
+file in the target directory which details which columns have been
+ignored. 
+
+=head1 EXPERIMENT SUMMARY FILE
+
+The experiment summary file has a flexible format based around a set
+of predefined column headers. Comments may be inserted using the '#'
+character at the start of any line. The file consists of three
+sections, each of which ends with a blank line:
+
+=head2 Experiment section
+
+This section contains top-level information about the experiment, such
+as the title, description and accession number. The section is
+constructed in two columns, with row names as described in
+L<ArrayExpress::Curator::MAGE::Definitions/"SUPPORTED HEADINGS">.
+
+=head2 Protocol section
+
+Protocols are defined as needed in this section. The section is
+organized into rows, with column headings as described in
+L<ArrayExpress::Curator::MAGE::Definitions/"SUPPORTED HEADINGS">. If all of the protocols
+used in the experiment have previously been loaded into ArrayExpress
+and given accession numbers, this whole section can be omitted.
+
+=head2 Hybridization section
+
+This section contains the bulk of the experiment information. At its
+simplest, each row describes the route taken from BioSource to output
+data file. 
+
+Column headings for BioMaterialCharacteristics and FactorValue should
+be provided in the following form:
+
+=over 2
+
+=item BioMaterialCharacteristics[<MGED Ontology Category>] 
+
+=item FactorValue[<MGED Ontology Category>] 
+
+=back
+
+Each of these headings should contain a valid Category subclass from
+the MGED ontology. The values in the columns must likewise be valid
+ontology entry values for these subclasses.
+
+Multichannel (e.g., two-colour) data can be described by
+entering each channel as a separate line. Pooling can be described at
+multiple levels by using as many lines as necessary to describe all
+the relationships between upstream and downstream samples. 
+
+In a sense the Hybridization table can be compared to an SQL database
+table in the way that it provides links between MAGE objects. Again,
+the recognized column headings for this section are described in
+L<ArrayExpress::Curator::MAGE::Definitions/"SUPPORTED HEADINGS">.
+
+=head1 OPTIONS
+
+=over 2
+
+=item B<-e> C<filename>
+
+The Tab2MAGE spreadsheet to be checked.
+
+=item B<-t> C<directory>
+
+The target directory to be created. This directory will contain the
+MAGE-ML file and external data files ready for validation.
+
+=item B<-n> C<accession>
+
+Normally the script uses the experiment accession number from the
+spreadsheet to be parsed. In cases where no accession has been entered
+in the spreadsheet, or you wish to override that accession, use this
+option.
+
+=item B<-q> C<QT filename>
+
+QuantitationType file. This option allows you to specify a custom
+QuantitationType definition file to override those defined as part of
+the Tab2MAGE package. See L<ArrayExpress::Datafile::QT_list> for more
+information.
+
+=item B<-Q> C<QT filename>
+
+QuantitationType file. This option will add the new QuantitationType
+definitions to those included with the Tab2MAGE package. See
+L<ArrayExpress::Datafile::QT_list> for more information.
+
+=item B<-k>
+
+Keep all columns in the data files, regardless of whether they are
+recognized or not. Unrecognized QTs will be created as generic
+SpecializedQuantitationTypes in the output MAGE-ML.
+
+=item B<-K>
+
+If the autosubmissions system is configured, tab2mage.pl will
+automatically reassign protocol accessions to fit a local
+convention. Use the -K option to suppress this behaviour.
+
+=item B<-s>
+
+Standalone option. This prevents the script from attempting to connect
+to ArrayExpress to retrieve array information.
+
+=item B<-R> C<namespace>
+
+Reporter identifier prefix. By default the script uses the MIAMExpress
+convention for generating reporter identifiers. This option allows you
+to override this behaviour by supplying an alternate prefix for
+identifiers.
+
+=item B<-C> C<namespace>
+
+CompositeSequence identifier prefix. By default the script uses the
+MIAMExpress convention for generating composite sequence
+identifiers. This option allows you to override this behaviour by
+supplying an alternate prefix for identifiers.
+
+=item B<-d> C<directory>
+
+Source directory containing all the data files referenced in the tab2mage
+spreadsheet. If this is omitted, the current working directory will be
+searched for data files.
+
+=item B<-P>
+
+By default, native (usually binary) file formats are used for
+Affymetrix CEL files and all NimbleScan (NimbleGen) files. This
+encoding uses far less overhead and retains the files in their
+original formats; this is often appealing to end-users. When used for
+ArrayExpress submissions, this option allows for such files to be
+directly downloadable from the ArrayExpress web interface. However, in
+unusual circumstances when you might wish to use plain-text
+encoding for these datafile, use this option.
+
+=item B<-L>
+
+Ignore the data file size limit as configured in Config.yml (i.e.,
+MAX_DATAFILE_SIZE).
+
+=item B<-f> C<font name>
+
+Name of the font to be used for Graphviz-generated PNGs.
+
+=item B<-c>
+
+Overwrite preexisting files ("clobber" option).
+
+=item B<-v>
+
+Prints the version number of the script.
+
+=item B<-h>
+
+Print a short help text summarizing these options.
+
+=back
+
+=head1 QUANTITATIONTYPE FILE
+
+Please see L<ArrayExpress::Datafile::QT_list> for a description of
+the format of this file.
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2004.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments. Particular credit
+goes to Ele Holloway, who was responsible for curating the lists of
+QuantitationTypes included with this script.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+###########
+# GLOBALS #
+###########
+#
+# Used to track the creation of unique objects.
+#
+
+my $APP_NAME = $CONFIG->get_TAB2MAGE_PROGNAME();
+my $VERSION  = $CONFIG->get_TAB2MAGE_VERSION();
+
+###############
+# SUBROUTINES #
+###############
+
+sub parse_args {
+
+    my %args;
+
+    my ( $want_version, $want_help );
+
+    GetOptions(
+        "e|edf=s"      => \$args{spreadsheet_filename},
+        "t|target=s"   => \$args{target_directory},
+	"n|accession=s" => \$args{expt_accession},
+	"d|source=s"   => \$args{source_directory},
+        "q|qtfile=s"   => \$args{QT_file},
+        "Q|QTFILE=s"   => \$args{included_QTs},
+        "k|all-qts"    => \$args{keep_all_qts},
+        "s|standalone" => \$args{is_standalone},
+	"R|reporter=s" => \$args{reporter_prefix},
+	"C|compseq=s"  => \$args{compseq_prefix},
+        "P|plain-text" => \$args{text_datafiles},
+	"L|large-files" => \$args{ignore_size_limits},
+        "f|font=s"     => \$args{font},
+        "v|version"    => \$want_version,
+        "c|clobber"    => \$args{clobber},
+        "h|help"       => \$want_help,
+        "K|keep-accns" => \$args{keep_protocol_accns},
+    );
+
+    if ($want_version) {
+        print STDERR "This is $APP_NAME v$VERSION\n\n";
+        exit 255;
+    }
+
+    if ($want_help) {
+	pod2usage(
+	    -exitval => 255,
+	    -output  => \*STDERR,
+	    -verbose => 1,
+	);
+    }
+
+    unless ( $args{spreadsheet_filename} && $args{target_directory} ) {
+	pod2usage(
+	    -message => 'Please see "tab2mage.pl -h" for further help notes.',
+	    -exitval => 255,
+	    -output  => \*STDERR,
+	    -verbose => 0,
+	);
+    }
+
+    return \%args;
+
+}
+
+########
+# MAIN #
+########
+
+# Get our command-line options.
+my $args = parse_args();
+
+# Initialise some stuff.
+my $starttime = new Benchmark;
+
+# Generate the MAGE objects, output the MAGE-ML, and return the hash
+# of MAGE container objects and the experiment accession for further
+# processing (e.g. visualisation).
+my $exporter = ArrayExpress::Curator::Tab2MAGE->new({
+    spreadsheet_filename => $args->{spreadsheet_filename},
+    target_directory     => $args->{target_directory},
+    source_directory     => $args->{source_directory},
+    is_standalone        => $args->{is_standalone},
+    clobber              => $args->{clobber},
+    qt_filename          => ( $args->{QT_file} || $args->{included_QTs} ),
+    include_default_qts  => defined( $args->{included_QTs} ),
+    keep_all_qts         => $args->{keep_all_qts},
+    use_plain_text       => $args->{text_datafiles},
+    keep_protocol_accns  => $args->{keep_protocol_accns},
+    reporter_prefix      => $args->{reporter_prefix},
+    compseq_prefix       => $args->{compseq_prefix},
+    ignore_size_limits   => $args->{ignore_size_limits},
+    external_accession   => $args->{expt_accession},
+});
+
+my ($mage, $accession, $error) = $exporter->write_mageml();
+
+# Create graphs
+my $graphfile_base
+    = File::Spec->catfile( $args->{target_directory}, $accession );
+
+$args->{clobber} = dot_and_png(
+    $exporter->get_bags(),
+    $graphfile_base,
+    $args->{font},
+    $exporter->get_clobber(),
+);
+
+print STDOUT ("Finished.\n");
+
+my $endtime = new Benchmark;
+my $timediff = timediff( $endtime, $starttime );
+print STDOUT ( "\nTotal run time = ", timestr($timediff), "\n\n", );
+
+# If we get here, we must assume that the parser ran more-or-less
+# okay, so we return zero to the shell.
+exit 0;
+
diff --git a/debian/TODO b/debian/TODO
deleted file mode 100644
index 19e2b58..0000000
--- a/debian/TODO
+++ /dev/null
@@ -1,41 +0,0 @@
-t/creator...............ok                                                   
-        1/1 skipped: Class::DBI not installed
-t/daemon................ok                                                   
-        1/1 skipped: MIME::Lite, Archive::Any or Class::DBI not installed
-t/daemon_checker........ok                                                   
-        3/3 skipped: Parent class ArrayExpress::AutoSubmission::Daemon not available.
-t/daemon_exporter.......ok                                                   
-        3/3 skipped: Parent class ArrayExpress::AutoSubmission::Daemon not available.
-t/daemon_mxchecker......ok                                                   
-        1/1 skipped: DBD::mysql not installed, or ArrayExpress::Curator::Daemon superclass unavailable.
-t/daemon_mxexporter.....ok                                                   
-        1/1 skipped: DBD::mysql not installed, or ArrayExpress::Curator::Daemon superclass unavailable.
-t/data_metrics..........ok                                                   
-t/database..............ok                                                   
-t/datafile..............ok                                                   
-        15/142 skipped: Test::Exception not installed
-t/datafile_parser.......ok                                                   
-t/db....................ok                                                   
-        31/31 skipped: Class::DBI not installed
-t/entrez_list...........ok                                                   
-t/experiment_checker....ok                                                   
-t/fgem..................ok                                                   
-t/logger................ok                                                   
-t/mage..................ok                                                   
-t/mage_defs.............ok                                                   
-t/magetab...............ok                                                   
-t/magetab_checker.......ok                                                   
-t/magetab_datamatrix....ok                                                   
-t/magetab_idf...........ok                                                   
-t/magetab_sdrf..........ok                                                   
-t/magetab_tabfile.......ok                                                   
-t/miamexpress...........ok                                                   
-        1/1 skipped: DBI and/or DBD::mysql not installed
-t/qt_list...............ok                                                   
-t/report................ok                                                   
-t/spreadsheet...........ok                                                   
-        1/1 skipped: Class::DBI not installed
-t/standalone............ok                                                   
-t/tab2mage..............ok                                                   
-t/tracking..............ok                                                   
-        2/2 skipped: Date::Manip not installed
diff --git a/debian/changelog b/debian/changelog
deleted file mode 100644
index d948d7e..0000000
--- a/debian/changelog
+++ /dev/null
@@ -1,12 +0,0 @@
-tab2mage (20080626-1) UNRELEASED; urgency=low
-
-  * Initial release (Closes: #nnnnnn)
-
-  [ Thorsten Alteholz ]
-  * new upstream version
-  * debian/rules: target get-orig-source added
-  * debian/control: dh 8
-  * debian/control: Standards=3.9.2
-  * debian/control: VCS fields added
-
- -- David Paleino <d.paleino at gmail.com>  Sat, 31 May 2008 19:39:45 +0200
diff --git a/debian/compat b/debian/compat
deleted file mode 100644
index f599e28..0000000
--- a/debian/compat
+++ /dev/null
@@ -1 +0,0 @@
-10
diff --git a/debian/control b/debian/control
deleted file mode 100644
index 415314a..0000000
--- a/debian/control
+++ /dev/null
@@ -1,40 +0,0 @@
-Source: tab2mage
-Maintainer: Debian Med Packaging Team <debian-med-packaging at lists.alioth.debian.org>
-Uploaders: Charles Plessy <plessy at debian.org>
-Section: science
-Priority: optional
-Build-Depends: debhelper (>= 10)
-Standards-Version: 3.9.8
-Vcs-Browser: https://anonscm.debian.org/viewvc/debian-med/trunk/packages/tab2mage/trunk/
-Vcs-Svn: svn://anonscm.debian.org/debian-med/trunk/packages/tab2mage/trunk
-Homepage: http://tab2mage.sourceforge.net/
-
-Package: tab2mage
-Architecture: all
-Depends: ${misc:Depends},
-         ${perl:Depends},
-         libbio-mage-perl,
-         libbio-mage-utils-perl,
-         libwww-perl,
-         libtie-ixhash-perl,
-         liblist-moreutils-perl,
-         libtext-csv-perl,
-         libreadonly-xs-perl,
-         libconfig-yaml-perl,
-         libclass-std-perl,
-         libparse-recdescent-perl,
-         libxml-xerces-perl
-Recommends: libdbi-perl,
-            libdbd-mysql-perl
-Suggests: graphviz
-Description: submitting large microarray experiment datasets to public repository database
- Tab2MAGE is a software package written and supported by the ArrayExpress
- curation team, which aims to ease the process of submitting large
- microarray experiment datasets to our public repository database. To
- this end, Tab2MAGE currently includes two tools, the tab2mage.pl script
- itself, and a data file checking script, expt_check.pl. With these
- scripts it is possible to perform an initial data file validation
- against an array design (e.g., in the form of an "Array Description
- File" or ADF), and then to generate MAGE-ML using these data files
- alongside a separate spreadsheet providing MIAME-compliant sample
- annotation.
diff --git a/debian/copyright b/debian/copyright
deleted file mode 100644
index 0cdf2ac..0000000
--- a/debian/copyright
+++ /dev/null
@@ -1,24 +0,0 @@
-This package was debianized by Charles Plessy <charles-debian-nospam at plessy.org> on
-Fri, 11 Apr 2008 10:55:53 +0900.
-
-It was downloaded from <url://example.com>
-
-Upstream Author(s): 
-
-    <put author's name and email here>
-    <likewise for another author>
-
-Copyright: 
-
-    <Copyright (C) YYYY Name OfAuthor>
-    <likewise for another author>
-
-License:
-
-    <Put the license of the package here indented by 4 spaces>
-
-The Debian packaging is (C) 2008, Charles Plessy <charles-debian-nospam at plessy.org> and
-is licensed under the GPL, see `/usr/share/common-licenses/GPL'.
-
-# Please also look if there are files or directories which have a
-# different copyright/license attached and list them here.
diff --git a/debian/dirs b/debian/dirs
deleted file mode 100644
index ca882bb..0000000
--- a/debian/dirs
+++ /dev/null
@@ -1,2 +0,0 @@
-usr/bin
-usr/sbin
diff --git a/debian/docs b/debian/docs
deleted file mode 100644
index 68eda78..0000000
--- a/debian/docs
+++ /dev/null
@@ -1,3 +0,0 @@
-docs/*
-MAGETAB.txt
-README
diff --git a/debian/examples b/debian/examples
deleted file mode 100644
index e39721e..0000000
--- a/debian/examples
+++ /dev/null
@@ -1 +0,0 @@
-examples/*
diff --git a/debian/patches/add-binaries-to-make-install.patch b/debian/patches/add-binaries-to-make-install.patch
deleted file mode 100644
index 527371a..0000000
--- a/debian/patches/add-binaries-to-make-install.patch
+++ /dev/null
@@ -1,14 +0,0 @@
-Index: tab2mage-20080110/Makefile.PL
-===================================================================
---- tab2mage-20080110.orig/Makefile.PL	2008-04-14 10:22:36.000000000 +0900
-+++ tab2mage-20080110/Makefile.PL	2008-04-14 10:23:23.000000000 +0900
-@@ -40,6 +40,9 @@
-     bin/magetab.pl
-     bin/expt_check.pl
-     bin/convert_to_mcmr.pl
-+    bin/mx_add_affy.pl
-+    bin/parse_affy.pl
-+    bin/t2m_visualize.pl
- );
- my @LIBS    = qw(xerces-c);
- 
diff --git a/debian/patches/series b/debian/patches/series
deleted file mode 100644
index 68a745e..0000000
--- a/debian/patches/series
+++ /dev/null
@@ -1 +0,0 @@
-add-binaries-to-make-install.patch
diff --git a/debian/rules b/debian/rules
deleted file mode 100755
index 912872f..0000000
--- a/debian/rules
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/make -f
-
-%:
-	dh $@
-
-get-orig-source:
-	mkdir -p ../tarballs
-	uscan --verbose --force-download --destdir=../tarballs
diff --git a/debian/source/format b/debian/source/format
deleted file mode 100644
index 163aaf8..0000000
--- a/debian/source/format
+++ /dev/null
@@ -1 +0,0 @@
-3.0 (quilt)
diff --git a/debian/watch b/debian/watch
deleted file mode 100644
index b578f7c..0000000
--- a/debian/watch
+++ /dev/null
@@ -1,2 +0,0 @@
-version=3
-http://sf.net/tab2mage/Tab2MAGE-(.*)\.tar\.gz
diff --git a/docs/ArrayExpress/ArrayMAGE.html b/docs/ArrayExpress/ArrayMAGE.html
new file mode 100644
index 0000000..f93a400
--- /dev/null
+++ b/docs/ArrayExpress/ArrayMAGE.html
@@ -0,0 +1,476 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::ArrayMAGE.pm - an OO module providing methods
+for writing MAGE-ML for array designs.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#public_methods">PUBLIC METHODS</a></li>
+        <li><a href="#subclasses">SUBCLASSES</a></li>
+        <ul>
+
+                <li><a href="#arrayexpress__arraymage__arraydesign">ArrayExpress::ArrayMAGE::ArrayDesign</a></li>
+                <li><a href="#arrayexpress__arraymage__biosequence">ArrayExpress::ArrayMAGE::BioSequence</a></li>
+                <li><a href="#arrayexpress__arraymage__compositegroup">ArrayExpress::ArrayMAGE::CompositeGroup</a></li>
+                <li><a href="#arrayexpress__arraymage__compositesequence">ArrayExpress::ArrayMAGE::CompositeSequence</a></li>
+                <li><a href="#arrayexpress__arraymage__database">ArrayExpress::ArrayMAGE::Database</a></li>
+                <li><a href="#arrayexpress__arraymage__feature">ArrayExpress::ArrayMAGE::Feature</a></li>
+                <li><a href="#arrayexpress__arraymage__featurereportermap">ArrayExpress::ArrayMAGE::FeatureReporterMap</a></li>
+                <li><a href="#arrayexpress__arraymage__reporter">ArrayExpress::ArrayMAGE::Reporter</a></li>
+                <li><a href="#arrayexpress__arraymage__reportercompositemap">ArrayExpress::ArrayMAGE::ReporterCompositeMap</a></li>
+                <li><a href="#arrayexpress__arraymage__reportergroup">ArrayExpress::ArrayMAGE::ReporterGroup</a></li>
+                <li><a href="#arrayexpress__arraymage__zone">ArrayExpress::ArrayMAGE::Zone</a></li>
+        </ul>
+
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../index.html"><img src="../T2M_logo.png" border="0" height=
+"50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: ArrayMAGE.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::ArrayMAGE.pm - an OO module providing methods for
+writing MAGE-ML for array designs.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ # Import all the ArrayMAGE classes.
+ use ArrayExpress::ArrayMAGE qw(:ALL);
+ 
+ # Instantiate the objects we require.
+ my @classes = qw(
+     Feature
+     Reporter
+     BioSequence
+     FeatureReporterMap
+     ArrayDesign
+     Zone
+ );
+ my %object;
+ foreach my $type (@classes) {
+     if ( $type eq 'ArrayDesign' ) {
+ 
+         # ArrayDesign requires an accession.
+         $object{$type} = "ArrayExpress::ArrayMAGE::$type"->new({
+             accession => $acc,
+         });
+     }
+     else {
+         $object{$type} = "ArrayExpress::ArrayMAGE::$type"->new();
+     }
+ }
+ 
+ # ReporterGroup and CompositeGroup are typically handled
+ # slightly differently, because multiple groups may be
+ # instantiated during a run (see below).
+ my ( %reporter_group, %composite_group );
+ 
+ # Add objects, e.g. while scanning through an ADF or CDF file.
+ foreach my $row_hashref ( @list_of_lines ) {
+    $object{Feature}->add({
+        metacolumn => $row->{'MetaColumn'},
+        metarow    => $row->{'MetaRow'},
+        column     => $row->{'Column'},
+        row        => $row->{'Row'},
+    });
+ 
+    # ReporterGroups and CompositeGroups are handled in similar
+    # ways (only ReporterGroup is shown here):
+    while ( my ( $rname, $rvalue ) = each %$row ) {
+       if ( my ($type) = ( $rname =~ m!ReporterGroup\[(.*?)\]!i ) ) {
+           my $key = "$type.$rvalue";
+           $reporter_group{$key}
+               ||= ArrayExpress::ArrayMAGE::ReporterGroup->new({
+               identifier => $key,
+               tag        => $rvalue,
+               is_species => ( $type =~ m!species!i ) || 0,
+           });
+           $reporter_group{$key}->add({
+               identifier => $row->{'Reporter Identifier'},
+           });
+       }
+    }
+ }
+ 
+ # Write out the MAGE-ML to STDOUT.
+ ArrayExpress::ArrayMAGE->combine_parts(
+     \%objects,
+     \%reporter_group,
+     \%composite_group,
+     \*STDOUT,
+ );
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This is a module designed to allow the creation of MAGE-ML for
+array designs in a memory-efficient fashion. This module is the
+abstract superclass for a series of MAGE-like classes representing
+parts of the MAGE-ML document to be written. These classes, when
+instantiated, all point to temporary filehandles into which is
+written the XML describing MAGE object instances. Each subclass
+implements an 'add' method in addition to those inherited from this
+base class. Instantiating the object with <a href=
+"#item_new"><code>new()</code></a> opens up the XML tags for a list
+of objects, the 'add' adds objects based on the values it is passed
+(no surprise there) and finally the XML tags are closed and the
+MAGE-ML written out by calling the class method <a href=
+"#item_combine_parts"><code>combine_parts()</code></a> in this
+top-level class.</p>
+<hr />
+<h1><a name="public_methods" id="public_methods">PUBLIC
+METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_combine_parts" id=
+"item_combine_parts"><code>combine_parts( \%obj, \%rg, \%cg, $fh
+)</code></a></strong></dt>
+<dd>
+<p>Class method. Takes: a hashref of ArrayMAGE objects, a hashref
+of ReporterGroups, a hashref of CompositeGroups, and an open output
+filehandle, and writes the completed MAGE-ML to that
+filehandle.</p>
+</dd>
+<dt><strong><a name="item_set_namespace" id=
+"item_set_namespace"><code>set_namespace("namespace")</code></a></strong></dt>
+<dd>
+<p>Class method. Allows the user to set the identifier namespace
+for the output MAGE objects.</p>
+</dd>
+<dt><strong><a name="item_set_design" id=
+"item_set_design"><code>set_design("design")</code></a></strong></dt>
+<dd>
+<p>Class method. Allows the user to set the design name used in
+identifiers for the output MAGE objects. Typically this will be the
+same as the array accession, but e.g. for Affymetrix arrays will
+correspond to the design name.</p>
+</dd>
+<dt><strong><a name="item_set_separator" id=
+"item_set_separator"><code>set_separator("separator")</code></a></strong></dt>
+<dd>
+<p>Class method. Allows the user to set the identifier separator
+for the output MAGE objects. Usually this will be q{:} or q{.}.</p>
+</dd>
+<dt><strong><a name="item_set_programname" id=
+"item_set_programname"><code>set_programname("programname")</code></a></strong></dt>
+<dd>
+<p>Class method. Allows the user to set the name of the program
+(this is not particularly useful).</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="subclasses" id="subclasses">SUBCLASSES</a></h1>
+<h2><a name="arrayexpress__arraymage__arraydesign" id=
+"arrayexpress__arraymage__arraydesign">ArrayExpress::ArrayMAGE::ArrayDesign</a></h2>
+<dl>
+<dt><strong><a name="item_new" id="item_new"><code>new()</code>
+required attribute: <code>accession</code></a></strong></dt>
+<dd>
+<p>The accession attribute becomes the MAGE PhysicalArrayDesign
+identifier.</p>
+</dd>
+<dt><strong><a name="item_add" id="item_add"><code>add()</code> is
+not implemented for this class.</a></strong></dt>
+</dl>
+<h2><a name="arrayexpress__arraymage__biosequence" id=
+"arrayexpress__arraymage__biosequence">ArrayExpress::ArrayMAGE::BioSequence</a></h2>
+<dl>
+<dt><strong><code>new()</code> requires no
+attributes.</strong></dt>
+<dt><strong><code>add()</code> required attribute:
+<code>identifier</code></strong></dt>
+<dd>
+<p>This identifier is combined with the class namespace to create
+the BioSequence identifier.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>dbrefs</code></strong></dt>
+<dd>
+<p>A hashref with db accessions as keys, and db tags as values
+(e.g. <code>{ A12345 => 'embl' }</code> ). Typically used for
+Reporter BioSequence Database Entry ADF columns.</p>
+</dd>
+</dl>
+<h2><a name="arrayexpress__arraymage__compositegroup" id=
+"arrayexpress__arraymage__compositegroup">ArrayExpress::ArrayMAGE::CompositeGroup</a></h2>
+<dl>
+<dt><strong><code>new()</code> required attribute:
+<code>identifier</code></strong></dt>
+<dd>
+<p>This identifier is combined with the class namespace to create a
+CompositeGroup identifier.</p>
+</dd>
+<dt><strong><code>new()</code> required attribute:
+<code>tag</code></strong></dt>
+<dd>
+<p>This tag is used as the CompositeGroup name. If
+<code>is_species</code> is set, then it will also be used in a
+Species_assn element as the species name.</p>
+</dd>
+<dt><strong><code>new()</code> optional attribute:
+<code>is_species</code></strong></dt>
+<dd>
+<p>A flag indicating whether this is a grouping based on species or
+not.</p>
+</dd>
+<dt><strong><code>add()</code> required attribute:
+<code>id_ref</code></strong></dt>
+<dd>
+<p>This identifier is combined with the class namespace to create a
+CompositeSequence_ref identifier.</p>
+</dd>
+</dl>
+<h2><a name="arrayexpress__arraymage__compositesequence" id=
+"arrayexpress__arraymage__compositesequence">ArrayExpress::ArrayMAGE::CompositeSequence</a></h2>
+<dl>
+<dt><strong><code>new()</code> requires no
+attributes.</strong></dt>
+<dt><strong><code>add()</code> required attribute:
+<code>identifier</code></strong></dt>
+<dd>
+<p>This identifier is combined with the class namespace to create
+the CompositeSequence identifier.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>name</code></strong></dt>
+<dd>
+<p>The name of the CompositeSequence.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>biosequences</code></strong></dt>
+<dd>
+<p>An arrayref of BioSequence identifiers to be attached to this
+CompositeSequence.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>dbrefs</code></strong></dt>
+<dd>
+<p>A hashref with db accessions as keys, and db tags as values
+(e.g. <code>{ A12345 => 'embl' }</code> ). Typically used for
+CompositeSequence Description Database Entry ADF columns.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>controltype</code></strong></dt>
+<dd>
+<p>A ControlType ontology term to be attached to this
+CompositeSequence.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>comment</code></strong></dt>
+<dd>
+<p>A comment to be added to this CompositeSequence as a
+Description_assn.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>cs_only</code></strong></dt>
+<dd>
+<p>A flag indicating that we are not creating Reporters (and can
+therefore skip ReporterCompositeMap creation).</p>
+</dd>
+</dl>
+<h2><a name="arrayexpress__arraymage__database" id=
+"arrayexpress__arraymage__database">ArrayExpress::ArrayMAGE::Database</a></h2>
+<dl>
+<dt><strong><code>new()</code> requires no
+attributes.</strong></dt>
+<dt><strong><code>add()</code> required attribute:
+<code>tag</code></strong></dt>
+<dd>
+<p>This tag is combined with the class namespace to create the
+Database identifier. Typically this will be a standard database
+tag, e.g. <code>embl</code>.</p>
+</dd>
+</dl>
+<h2><a name="arrayexpress__arraymage__feature" id=
+"arrayexpress__arraymage__feature">ArrayExpress::ArrayMAGE::Feature</a></h2>
+<dl>
+<dt><strong><code>new()</code> requires no
+attributes.</strong></dt>
+<dt><strong><code>add()</code> required attribute:
+<code>metacolumn</code></strong></dt>
+<dt><strong><code>add()</code> required attribute:
+<code>metarow</code></strong></dt>
+<dt><strong><code>add()</code> required attribute:
+<code>column</code></strong></dt>
+<dt><strong><code>add()</code> required attribute:
+<code>row</code></strong></dt>
+<dd>
+<p>Feature coordinates, used in the Feature and Zone_ref
+identifiers and in FeatureLocation.</p>
+</dd>
+</dl>
+<h2><a name="arrayexpress__arraymage__featurereportermap" id=
+"arrayexpress__arraymage__featurereportermap">ArrayExpress::ArrayMAGE::FeatureReporterMap</a></h2>
+<dl>
+<dt><strong><code>new()</code> requires no
+attributes.</strong></dt>
+<dt><strong><code>add()</code> required attribute:
+<code>identifier</code></strong></dt>
+<dd>
+<p>The Reporter identifier to which Features should be mapped.</p>
+</dd>
+<dt><strong><code>add()</code> required attribute:
+<code>features</code></strong></dt>
+<dd>
+<p>An arrayref of Feature identifiers mapped to the Reporter.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>mismatch_info</code></strong></dt>
+<dd>
+<p>A hashref of hashrefs, keyed by Feature identifier and then by
+<code>start_coord</code>, <code>new_sequence</code> and
+<code>replaced_length</code>, used to create a
+MismatchInformation_assnlist (useful for Affymetrix array
+designs).</p>
+</dd>
+</dl>
+<h2><a name="arrayexpress__arraymage__reporter" id=
+"arrayexpress__arraymage__reporter">ArrayExpress::ArrayMAGE::Reporter</a></h2>
+<dl>
+<dt><strong><code>new()</code> requires no
+attributes.</strong></dt>
+<dt><strong><code>add()</code> required attribute:
+<code>identifier</code></strong></dt>
+<dd>
+<p>This identifier is combined with the class namespace to create
+the Reporter identifier.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>name</code></strong></dt>
+<dd>
+<p>The name of the Reporter.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>biosequences</code></strong></dt>
+<dd>
+<p>An arrayref of BioSequence identifiers to be attached to this
+Reporter.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>dbrefs</code></strong></dt>
+<dd>
+<p>A hashref with db accessions as keys, and db tags as values
+(e.g. <code>{ A12345 => 'embl' }</code> ). Typically used for
+Reporter Description Database Entry ADF columns.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>controltype</code></strong></dt>
+<dd>
+<p>A ControlType ontology term to be attached to this Reporter.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>warningtype</code></strong></dt>
+<dd>
+<p>A WarningType ontology term to be attached to this Reporter.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>failtype</code></strong></dt>
+<dd>
+<p>A FailType ontology term to be attached to this Reporter.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>comment</code></strong></dt>
+<dd>
+<p>A comment to be added to this Reporter as a
+Description_assn.</p>
+</dd>
+</dl>
+<h2><a name="arrayexpress__arraymage__reportercompositemap" id=
+"arrayexpress__arraymage__reportercompositemap">ArrayExpress::ArrayMAGE::ReporterCompositeMap</a></h2>
+<dl>
+<dt><strong><code>new()</code> requires no
+attributes.</strong></dt>
+<dt><strong><code>add()</code> required attribute:
+<code>identifier</code></strong></dt>
+<dd>
+<p>The CompositeSequence identifier to which Reporters should be
+mapped.</p>
+</dd>
+<dt><strong><code>add()</code> required attribute:
+<code>reporters</code></strong></dt>
+<dd>
+<p>An arrayref of Reporter identifiers mapped to the
+CompositeSequence.</p>
+</dd>
+</dl>
+<h2><a name="arrayexpress__arraymage__reportergroup" id=
+"arrayexpress__arraymage__reportergroup">ArrayExpress::ArrayMAGE::ReporterGroup</a></h2>
+<dl>
+<dt><strong><code>new()</code> required attribute:
+<code>identifier</code></strong></dt>
+<dd>
+<p>This identifier is combined with the class namespace to create a
+ReporterGroup identifier.</p>
+</dd>
+<dt><strong><code>new()</code> required attribute:
+<code>tag</code></strong></dt>
+<dd>
+<p>This tag is used as the ReporterGroup name. If
+<code>is_species</code> is set, then it will also be used in a
+Species_assn element as the species name.</p>
+</dd>
+<dt><strong><code>new()</code> optional attribute:
+<code>is_species</code></strong></dt>
+<dd>
+<p>A flag indicating whether this is a grouping based on species or
+not.</p>
+</dd>
+<dt><strong><code>add()</code> required attribute:
+<code>id_ref</code></strong></dt>
+<dd>
+<p>This identifier is combined with the class namespace to create a
+Reporter_ref identifier.</p>
+</dd>
+</dl>
+<h2><a name="arrayexpress__arraymage__zone" id=
+"arrayexpress__arraymage__zone">ArrayExpress::ArrayMAGE::Zone</a></h2>
+<dl>
+<dt><strong><code>new()</code> requires no
+attributes.</strong></dt>
+<dt><strong><code>add()</code> required attribute:
+<code>column</code></strong></dt>
+<dt><strong><code>add()</code> required attribute:
+<code>row</code></strong></dt>
+<dd>
+<p>Zone coordinates, used in the Zone identifier and in its row and
+column attributes.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2008.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/AutoSubmission/Creator.html b/docs/ArrayExpress/AutoSubmission/Creator.html
new file mode 100644
index 0000000..5ce7ff9
--- /dev/null
+++ b/docs/ArrayExpress/AutoSubmission/Creator.html
@@ -0,0 +1,185 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::AutoSubmission::Creator - A class providing
+convenience methods for experiment object creation and insertion
+into the autosubmissions tracking database.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#methods">METHODS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: Creator.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::AutoSubmission::Creator - A class providing
+convenience methods for experiment object creation and insertion
+into the autosubmissions tracking database.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::AutoSubmission::Creator;
+ my $creator = ArrayExpress::AutoSubmission::Creator->new({
+    login           => $login,
+    name            => $expt_name,
+    spreadsheet     => $startfile,
+    data_files      => \@datafiles,
+    accession       => $accession,
+    experiment_type => 'MAGE-TAB',
+    comment         => 'Submission inserted manually',
+    clobber         => 0,
+ });
+</pre>
+<pre>
+ # Create the experiment and insert the spreadsheet.
+ my $expt = $creator->get_experiment();
+</pre>
+<pre>
+ # Copy the files to the submissions directory.
+ print STDERR ("Copying files...\n");
+ $creator->insert_spreadsheet();
+ $creator->insert_data_files();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module provides some basic convenience methods to automate
+error handling when inserting new experiments into the tracking
+database.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_new" id=
+"item_new"><code>new()</code></a></strong></dt>
+<dd>
+<p>Object constructor. Takes a hashref of options, including the
+following:</p>
+</dd>
+<dd>
+<dl>
+<dt><strong><a name="item_login" id=
+"item_login"><code>login</code></a></strong></dt>
+<dd>
+<p>The login name of the user to which the experiment should be
+assigned.</p>
+</dd>
+<dt><strong><a name="item_name" id=
+"item_name"><code>name</code></a></strong></dt>
+<dd>
+<p>The name of the experiment</p>
+</dd>
+<dt><strong><a name="item_spreadsheet" id=
+"item_spreadsheet"><code>spreadsheet</code></a></strong></dt>
+<dd>
+<p>The path to spreadsheet file to be inserted.</p>
+</dd>
+<dt><strong><a name="item_data_files" id=
+"item_data_files"><code>data_files</code></a></strong></dt>
+<dd>
+<p>An arrayref listing the paths to the data files to be
+inserted.</p>
+</dd>
+<dt><strong><a name="item_accession" id=
+"item_accession"><code>accession</code></a></strong></dt>
+<dd>
+<p>The accession number for the experiment.</p>
+</dd>
+<dt><strong><a name="item_comment" id=
+"item_comment"><code>comment</code></a></strong></dt>
+<dd>
+<p>Any comments to be attached to the experiment.</p>
+</dd>
+<dt><strong><a name="item_experiment_type" id=
+"item_experiment_type"><code>experiment_type</code></a></strong></dt>
+<dd>
+<p>The experiment type. This should typically be one of the
+following: MAGE-TAB, Tab2MAGE, MIAMExpress, GEO, MUGEN,
+Unknown.</p>
+</dd>
+<dt><strong><a name="item_clobber" id=
+"item_clobber"><code>clobber</code></a></strong></dt>
+<dd>
+<p>A flag indicating whether to overwrite files without prompting
+the user.</p>
+</dd>
+<dt><strong><a name="item_organisms" id=
+"item_organisms"><code>organisms</code></a></strong></dt>
+<dd>
+<p>An arrayref of organism scientific names to associate with this
+experiment.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="item_mk_passwd" id=
+"item_mk_passwd"><code>mk_passwd</code></a></strong></dt>
+<dd>
+<p>Returns a random string suitable for use as an account password.
+Can pass an optional length argument, otherwise defaults to eight
+characters.</p>
+</dd>
+<dt><strong><a name="item_insert_spreadsheet" id=
+"item_insert_spreadsheet"><code>insert_spreadsheet</code></a></strong></dt>
+<dd>
+<p>Copy the spreadsheet file into the appropriate filesystem
+location and insert a record into the database.</p>
+</dd>
+<dt><strong><a name="item_insert_data_files" id=
+"item_insert_data_files"><code>insert_data_files</code></a></strong></dt>
+<dd>
+<p>Copy the data files into the appropriate filesystem location and
+insert records into the database.</p>
+</dd>
+<dt><strong><a name="item_get_user" id=
+"item_get_user"><code>get_user</code></a></strong></dt>
+<dd>
+<p>Returns the appropriate Class::DBI user object as retrieved from
+(or inserted into) the database.</p>
+</dd>
+<dt><strong><a name="item_get_experiment" id=
+"item_get_experiment"><code>get_experiment</code></a></strong></dt>
+<dd>
+<p>Returns the appropriate Class::DBI experiment object as
+retrieved from (or inserted into) the database.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2008.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/AutoSubmission/DB.html b/docs/ArrayExpress/AutoSubmission/DB.html
new file mode 100644
index 0000000..4276dc9
--- /dev/null
+++ b/docs/ArrayExpress/AutoSubmission/DB.html
@@ -0,0 +1,2905 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::AutoSubmission::DB - Class::DBI based
+interface to the autosubmissions tracking database.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#tables">TABLES</a></li>
+        <ul>
+
+                <li><a href="#array_designs">array_designs</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#miamexpress_subid">miamexpress_subid</a></li>
+                                <li><a href="#accession">accession</a></li>
+                                <li><a href="#name">name</a></li>
+                                <li><a href="#miamexpress_login">miamexpress_login</a></li>
+                                <li><a href="#status">status</a></li>
+                                <li><a href="#data_warehouse_ready">data_warehouse_ready</a></li>
+                                <li><a href="#date_last_processed">date_last_processed</a></li>
+                                <li><a href="#comment">comment</a></li>
+                                <li><a href="#is_deleted">is_deleted</a></li>
+                                <li><a href="#miame_score">miame_score</a></li>
+                                <li><a href="#in_data_warehouse">in_data_warehouse</a></li>
+                                <li><a href="#annotation_source">annotation_source</a></li>
+                                <li><a href="#annotation_version">annotation_version</a></li>
+                                <li><a href="#biomart_table_name">biomart_table_name</a></li>
+                                <li><a href="#release_date">release_date</a></li>
+                                <li><a href="#is_released">is_released</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                                <li><a href="#unique">UNIQUE</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#array_designs_experiments">array_designs_experiments</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#array_design_id">array_design_id</a></li>
+                                <li><a href="#experiment_id">experiment_id</a></li>
+                        </ul>
+
+                        <li><a href="#indices">INDICES</a></li>
+                        <ul>
+
+                                <li><a href="#normal">NORMAL</a></li>
+                                <li><a href="#normal">NORMAL</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#array_designs_organisms">array_designs_organisms</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#organism_id">organism_id</a></li>
+                                <li><a href="#array_design_id">array_design_id</a></li>
+                        </ul>
+
+                        <li><a href="#indices">INDICES</a></li>
+                        <ul>
+
+                                <li><a href="#normal">NORMAL</a></li>
+                                <li><a href="#normal">NORMAL</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#categories">categories</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#ontology_term">ontology_term</a></li>
+                                <li><a href="#display_label">display_label</a></li>
+                                <li><a href="#is_common">is_common</a></li>
+                                <li><a href="#is_bmc">is_bmc</a></li>
+                                <li><a href="#is_fv">is_fv</a></li>
+                                <li><a href="#is_deleted">is_deleted</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#categories_designs">categories_designs</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#category_id">category_id</a></li>
+                                <li><a href="#design_id">design_id</a></li>
+                        </ul>
+
+                        <li><a href="#indices">INDICES</a></li>
+                        <ul>
+
+                                <li><a href="#normal">NORMAL</a></li>
+                                <li><a href="#normal">NORMAL</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#categories_materials">categories_materials</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#category_id">category_id</a></li>
+                                <li><a href="#material_id">material_id</a></li>
+                        </ul>
+
+                        <li><a href="#indices">INDICES</a></li>
+                        <ul>
+
+                                <li><a href="#normal">NORMAL</a></li>
+                                <li><a href="#normal">NORMAL</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#categories_taxons">categories_taxons</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#category_id">category_id</a></li>
+                                <li><a href="#taxon_id">taxon_id</a></li>
+                        </ul>
+
+                        <li><a href="#indices">INDICES</a></li>
+                        <ul>
+
+                                <li><a href="#normal">NORMAL</a></li>
+                                <li><a href="#normal">NORMAL</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#data_files">data_files</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#experiment_id">experiment_id</a></li>
+                                <li><a href="#name">name</a></li>
+                                <li><a href="#is_unpacked">is_unpacked</a></li>
+                                <li><a href="#is_deleted">is_deleted</a></li>
+                        </ul>
+
+                        <li><a href="#indices">INDICES</a></li>
+                        <ul>
+
+                                <li><a href="#normal">NORMAL</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#data_formats">data_formats</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#name">name</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                                <li><a href="#unique">UNIQUE</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#design_instances">design_instances</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#design_id">design_id</a></li>
+                                <li><a href="#experiment_id">experiment_id</a></li>
+                        </ul>
+
+                        <li><a href="#indices">INDICES</a></li>
+                        <ul>
+
+                                <li><a href="#normal">NORMAL</a></li>
+                                <li><a href="#normal">NORMAL</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#designs">designs</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#display_label">display_label</a></li>
+                                <li><a href="#ontology_category">ontology_category</a></li>
+                                <li><a href="#ontology_value">ontology_value</a></li>
+                                <li><a href="#design_type">design_type</a></li>
+                                <li><a href="#is_deleted">is_deleted</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#events">events</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#array_design_id">array_design_id</a></li>
+                                <li><a href="#experiment_id">experiment_id</a></li>
+                                <li><a href="#event_type">event_type</a></li>
+                                <li><a href="#was_successful">was_successful</a></li>
+                                <li><a href="#source_db">source_db</a></li>
+                                <li><a href="#target_db">target_db</a></li>
+                                <li><a href="#start_time">start_time</a></li>
+                                <li><a href="#end_time">end_time</a></li>
+                                <li><a href="#machine">machine</a></li>
+                                <li><a href="#operator">operator</a></li>
+                                <li><a href="#log_file">log_file</a></li>
+                                <li><a href="#jobregister_dbid">jobregister_dbid</a></li>
+                                <li><a href="#comment">comment</a></li>
+                                <li><a href="#is_deleted">is_deleted</a></li>
+                        </ul>
+
+                        <li><a href="#indices">INDICES</a></li>
+                        <ul>
+
+                                <li><a href="#normal">NORMAL</a></li>
+                                <li><a href="#normal">NORMAL</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#experiments">experiments</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#accession">accession</a></li>
+                                <li><a href="#name">name</a></li>
+                                <li><a href="#user_id">user_id</a></li>
+                                <li><a href="#checker_score">checker_score</a></li>
+                                <li><a href="#software">software</a></li>
+                                <li><a href="#status">status</a></li>
+                                <li><a href="#data_warehouse_ready">data_warehouse_ready</a></li>
+                                <li><a href="#date_last_edited">date_last_edited</a></li>
+                                <li><a href="#date_submitted">date_submitted</a></li>
+                                <li><a href="#date_last_processed">date_last_processed</a></li>
+                                <li><a href="#in_curation">in_curation</a></li>
+                                <li><a href="#curator">curator</a></li>
+                                <li><a href="#comment">comment</a></li>
+                                <li><a href="#experiment_type">experiment_type</a></li>
+                                <li><a href="#miamexpress_login">miamexpress_login</a></li>
+                                <li><a href="#miamexpress_subid">miamexpress_subid</a></li>
+                                <li><a href="#is_affymetrix">is_affymetrix</a></li>
+                                <li><a href="#is_mx_batchloader">is_mx_batchloader</a></li>
+                                <li><a href="#is_deleted">is_deleted</a></li>
+                                <li><a href="#miame_score">miame_score</a></li>
+                                <li><a href="#in_data_warehouse">in_data_warehouse</a></li>
+                                <li><a href="#num_submissions">num_submissions</a></li>
+                                <li><a href="#submitter_description">submitter_description</a></li>
+                                <li><a href="#curated_name">curated_name</a></li>
+                                <li><a href="#num_samples">num_samples</a></li>
+                                <li><a href="#num_hybridizations">num_hybridizations</a></li>
+                                <li><a href="#has_raw_data">has_raw_data</a></li>
+                                <li><a href="#has_processed_data">has_processed_data</a></li>
+                                <li><a href="#release_date">release_date</a></li>
+                                <li><a href="#is_released">is_released</a></li>
+                                <li><a href="#ae_miame_score">ae_miame_score</a></li>
+                                <li><a href="#ae_data_warehouse_score">ae_data_warehouse_score</a></li>
+                        </ul>
+
+                        <li><a href="#indices">INDICES</a></li>
+                        <ul>
+
+                                <li><a href="#normal">NORMAL</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#experiments_factors">experiments_factors</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#experiment_id">experiment_id</a></li>
+                                <li><a href="#factor_id">factor_id</a></li>
+                        </ul>
+
+                        <li><a href="#indices">INDICES</a></li>
+                        <ul>
+
+                                <li><a href="#normal">NORMAL</a></li>
+                                <li><a href="#normal">NORMAL</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#experiments_loaded_data">experiments_loaded_data</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#experiment_id">experiment_id</a></li>
+                                <li><a href="#loaded_data_id">loaded_data_id</a></li>
+                        </ul>
+
+                        <li><a href="#indices">INDICES</a></li>
+                        <ul>
+
+                                <li><a href="#normal">NORMAL</a></li>
+                                <li><a href="#normal">NORMAL</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#experiments_quantitation_types">experiments_quantitation_types</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#quantitation_type_id">quantitation_type_id</a></li>
+                                <li><a href="#experiment_id">experiment_id</a></li>
+                        </ul>
+
+                        <li><a href="#indices">INDICES</a></li>
+                        <ul>
+
+                                <li><a href="#normal">NORMAL</a></li>
+                                <li><a href="#normal">NORMAL</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#factors">factors</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#name">name</a></li>
+                                <li><a href="#is_deleted">is_deleted</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#loaded_data">loaded_data</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#identifier">identifier</a></li>
+                                <li><a href="#md5_hash">md5_hash</a></li>
+                                <li><a href="#data_format_id">data_format_id</a></li>
+                                <li><a href="#is_deleted">is_deleted</a></li>
+                                <li><a href="#platform_id">platform_id</a></li>
+                                <li><a href="#needs_metrics_calculation">needs_metrics_calculation</a></li>
+                                <li><a href="#date_hashed">date_hashed</a></li>
+                        </ul>
+
+                        <li><a href="#indices">INDICES</a></li>
+                        <ul>
+
+                                <li><a href="#normal">NORMAL</a></li>
+                                <li><a href="#normal">NORMAL</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#loaded_data_quality_metrics">loaded_data_quality_metrics</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#value">value</a></li>
+                                <li><a href="#quality_metric_id">quality_metric_id</a></li>
+                                <li><a href="#loaded_data_id">loaded_data_id</a></li>
+                                <li><a href="#date_calculated">date_calculated</a></li>
+                        </ul>
+
+                        <li><a href="#indices">INDICES</a></li>
+                        <ul>
+
+                                <li><a href="#normal">NORMAL</a></li>
+                                <li><a href="#normal">NORMAL</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#material_instances">material_instances</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#material_id">material_id</a></li>
+                                <li><a href="#experiment_id">experiment_id</a></li>
+                        </ul>
+
+                        <li><a href="#indices">INDICES</a></li>
+                        <ul>
+
+                                <li><a href="#normal">NORMAL</a></li>
+                                <li><a href="#normal">NORMAL</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#materials">materials</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#display_label">display_label</a></li>
+                                <li><a href="#ontology_category">ontology_category</a></li>
+                                <li><a href="#ontology_value">ontology_value</a></li>
+                                <li><a href="#is_deleted">is_deleted</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#meta">meta</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#name">name</a></li>
+                                <li><a href="#value">value</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#organism_instances">organism_instances</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#organism_id">organism_id</a></li>
+                                <li><a href="#experiment_id">experiment_id</a></li>
+                        </ul>
+
+                        <li><a href="#indices">INDICES</a></li>
+                        <ul>
+
+                                <li><a href="#normal">NORMAL</a></li>
+                                <li><a href="#normal">NORMAL</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#organisms">organisms</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#scientific_name">scientific_name</a></li>
+                                <li><a href="#common_name">common_name</a></li>
+                                <li><a href="#accession">accession</a></li>
+                                <li><a href="#taxon_id">taxon_id</a></li>
+                                <li><a href="#is_deleted">is_deleted</a></li>
+                        </ul>
+
+                        <li><a href="#indices">INDICES</a></li>
+                        <ul>
+
+                                <li><a href="#normal">NORMAL</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#permissions">permissions</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#name">name</a></li>
+                                <li><a href="#info">info</a></li>
+                                <li><a href="#is_deleted">is_deleted</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#permissions_roles">permissions_roles</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#role_id">role_id</a></li>
+                                <li><a href="#permission_id">permission_id</a></li>
+                        </ul>
+
+                        <li><a href="#indices">INDICES</a></li>
+                        <ul>
+
+                                <li><a href="#normal">NORMAL</a></li>
+                                <li><a href="#normal">NORMAL</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#platforms">platforms</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#name">name</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                                <li><a href="#unique">UNIQUE</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#protocols">protocols</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#accession">accession</a></li>
+                                <li><a href="#user_accession">user_accession</a></li>
+                                <li><a href="#expt_accession">expt_accession</a></li>
+                                <li><a href="#name">name</a></li>
+                                <li><a href="#date_last_processed">date_last_processed</a></li>
+                                <li><a href="#comment">comment</a></li>
+                                <li><a href="#is_deleted">is_deleted</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                                <li><a href="#unique">UNIQUE</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#quality_metrics">quality_metrics</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#type">type</a></li>
+                                <li><a href="#description">description</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                                <li><a href="#unique">UNIQUE</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#quantitation_types">quantitation_types</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#name">name</a></li>
+                                <li><a href="#is_deleted">is_deleted</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#roles">roles</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#name">name</a></li>
+                                <li><a href="#info">info</a></li>
+                                <li><a href="#is_deleted">is_deleted</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#roles_users">roles_users</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#user_id">user_id</a></li>
+                                <li><a href="#role_id">role_id</a></li>
+                        </ul>
+
+                        <li><a href="#indices">INDICES</a></li>
+                        <ul>
+
+                                <li><a href="#normal">NORMAL</a></li>
+                                <li><a href="#normal">NORMAL</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#spreadsheets">spreadsheets</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#experiment_id">experiment_id</a></li>
+                                <li><a href="#name">name</a></li>
+                                <li><a href="#is_deleted">is_deleted</a></li>
+                        </ul>
+
+                        <li><a href="#indices">INDICES</a></li>
+                        <ul>
+
+                                <li><a href="#normal">NORMAL</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                                <li><a href="#foreign_key">FOREIGN KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#taxons">taxons</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#scientific_name">scientific_name</a></li>
+                                <li><a href="#common_name">common_name</a></li>
+                                <li><a href="#accession">accession</a></li>
+                                <li><a href="#is_deleted">is_deleted</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                        </ul>
+
+                </ul>
+
+                <li><a href="#users">users</a></li>
+                <ul>
+
+                        <li><a href="#fields">FIELDS</a></li>
+                        <ul>
+
+                                <li><a href="#id">id</a></li>
+                                <li><a href="#login">login</a></li>
+                                <li><a href="#name">name</a></li>
+                                <li><a href="#password">password</a></li>
+                                <li><a href="#email">email</a></li>
+                                <li><a href="#modified_at">modified_at</a></li>
+                                <li><a href="#created_at">created_at</a></li>
+                                <li><a href="#access">access</a></li>
+                                <li><a href="#is_deleted">is_deleted</a></li>
+                        </ul>
+
+                        <li><a href="#constraints">CONSTRAINTS</a></li>
+                        <ul>
+
+                                <li><a href="#primary_key">PRIMARY KEY</a></li>
+                                <li><a href="#unique">UNIQUE</a></li>
+                        </ul>
+
+                </ul>
+
+        </ul>
+
+        <li><a href="#produced_by">PRODUCED BY</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: DB.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::AutoSubmission::DB - Class::DBI based interface to
+the autosubmissions tracking database.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::AutoSubmission::DB::Experiment;
+ my $expt = ArrayExpress::AutoSubmission::DB::Experiment->retrieve( $id );
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module is the abstract superclass for a set of Class::DBI -
+based modules, which are used as an object-relational mapping to
+the underlying submissions tracking database (currently implemented
+using MySQL). You should not use this class directly; rather, you
+should use the relevant table subclasses to query the database (see
+<a href="#synopsis">SYNOPSIS</a> for an example). Please refer to
+the Class::DBI documentation for information on query syntax.</p>
+<hr />
+<h1><a name="tables" id="tables">TABLES</a></h1>
+<p>Included below are auto-generated descriptions of the MySQL
+tables. This listing was generated using the
+SQL::Translator::Producer::POD module.</p>
+<h2><a name="array_designs" id=
+"array_designs">array_designs</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><a name="item_int" id=
+"item_int"><code>int(11)</code></a></strong></li>
+<li><strong><a name="item_primary_key" id=
+"item_primary_key">PRIMARY KEY</a></strong></li>
+<li><strong><a name="item_nullable__27no_27" id=
+"item_nullable__27no_27">Nullable 'No'</a></strong></li>
+</ul>
+<h4><a name="miamexpress_subid" id=
+"miamexpress_subid">miamexpress_subid</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong><a name="item_default__27null_27" id=
+"item_default__27null_27">Default 'NULL'</a></strong></li>
+<li><strong><a name="item_nullable__27yes_27" id=
+"item_nullable__27yes_27">Nullable 'Yes'</a></strong></li>
+</ul>
+<h4><a name="accession" id="accession">accession</a></h4>
+<ul>
+<li><strong><a name="item_varchar" id=
+"item_varchar"><code>varchar(255)</code></a></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="name" id="name">name</a></h4>
+<ul>
+<li><strong><code>varchar(255)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="miamexpress_login" id=
+"miamexpress_login">miamexpress_login</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="status" id="status">status</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="data_warehouse_ready" id=
+"data_warehouse_ready">data_warehouse_ready</a></h4>
+<ul>
+<li><strong><a name="item_char" id=
+"item_char"><code>char(15)</code></a></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="date_last_processed" id=
+"date_last_processed">date_last_processed</a></h4>
+<ul>
+<li><strong><a name="item_datetime" id=
+"item_datetime">datetime</a></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="comment" id="comment">comment</a></h4>
+<ul>
+<li><strong><a name="item_text" id=
+"item_text"><code>text(65535)</code></a></strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="miame_score" id="miame_score">miame_score</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="in_data_warehouse" id=
+"in_data_warehouse">in_data_warehouse</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="annotation_source" id=
+"annotation_source">annotation_source</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="annotation_version" id=
+"annotation_version">annotation_version</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="biomart_table_name" id=
+"biomart_table_name">biomart_table_name</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="release_date" id="release_date">release_date</a></h4>
+<ul>
+<li><strong>datetime</strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_released" id="is_released">is_released</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_id" id=
+"item_fields__3d_id">Fields = id</a></strong></li>
+</ul>
+<h4><a name="unique" id="unique">UNIQUE</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_miamexpress_subid" id=
+"item_fields__3d_miamexpress_subid">Fields =
+miamexpress_subid</a></strong></li>
+</ul>
+<h2><a name="array_designs_experiments" id=
+"array_designs_experiments">array_designs_experiments</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="array_design_id" id=
+"array_design_id">array_design_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="experiment_id" id=
+"experiment_id">experiment_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_array_design_id" id=
+"item_fields__3d_array_design_id">Fields =
+array_design_id</a></strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_experiment_id" id=
+"item_fields__3d_experiment_id">Fields =
+experiment_id</a></strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = array_design_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2farray_designs" id=
+"item_reference_table__3d__2farray_designs">Reference Table =</a>
+<a href="#array_designs">array_designs</a></strong></li>
+<li><strong><a name="item_reference_fields__3d__2fid" id=
+"item_reference_fields__3d__2fid">Reference Fields =</a> <a href=
+"#id">id</a></strong></li>
+<li><strong><a name="item_on_delete__3d_cascade" id=
+"item_on_delete__3d_cascade">On delete = CASCADE</a></strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2fexperiments" id=
+"item_reference_table__3d__2fexperiments">Reference Table =</a>
+<a href="#experiments">experiments</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="array_designs_organisms" id=
+"array_designs_organisms">array_designs_organisms</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="organism_id" id="organism_id">organism_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="array_design_id" id=
+"array_design_id">array_design_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_organism_id" id=
+"item_fields__3d_organism_id">Fields =
+organism_id</a></strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = array_design_id</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = organism_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2forganisms" id=
+"item_reference_table__3d__2forganisms">Reference Table =</a>
+<a href="#organisms">organisms</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = array_design_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#array_designs">array_designs</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="categories" id="categories">categories</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="ontology_term" id=
+"ontology_term">ontology_term</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="display_label" id=
+"display_label">display_label</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_common" id="is_common">is_common</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_bmc" id="is_bmc">is_bmc</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_fv" id="is_fv">is_fv</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h2><a name="categories_designs" id=
+"categories_designs">categories_designs</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="category_id" id="category_id">category_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="design_id" id="design_id">design_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_category_id" id=
+"item_fields__3d_category_id">Fields =
+category_id</a></strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_design_id" id=
+"item_fields__3d_design_id">Fields = design_id</a></strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = category_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2fcategories" id=
+"item_reference_table__3d__2fcategories">Reference Table =</a>
+<a href="#categories">categories</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = design_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2fdesigns" id=
+"item_reference_table__3d__2fdesigns">Reference Table =</a>
+<a href="#designs">designs</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="categories_materials" id=
+"categories_materials">categories_materials</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="category_id" id="category_id">category_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="material_id" id="material_id">material_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = category_id</strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_material_id" id=
+"item_fields__3d_material_id">Fields =
+material_id</a></strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = category_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#categories">categories</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = material_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2fmaterials" id=
+"item_reference_table__3d__2fmaterials">Reference Table =</a>
+<a href="#materials">materials</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="categories_taxons" id=
+"categories_taxons">categories_taxons</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="category_id" id="category_id">category_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="taxon_id" id="taxon_id">taxon_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = category_id</strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_taxon_id" id=
+"item_fields__3d_taxon_id">Fields = taxon_id</a></strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = category_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#categories">categories</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = taxon_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2ftaxons" id=
+"item_reference_table__3d__2ftaxons">Reference Table =</a> <a href=
+"#taxons">taxons</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="data_files" id="data_files">data_files</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="experiment_id" id=
+"experiment_id">experiment_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="name" id="name">name</a></h4>
+<ul>
+<li><strong><code>varchar(255)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_unpacked" id="is_unpacked">is_unpacked</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#experiments">experiments</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+</ul>
+<h2><a name="data_formats" id="data_formats">data_formats</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="name" id="name">name</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="unique" id="unique">UNIQUE</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_name" id=
+"item_fields__3d_name">Fields = name</a></strong></li>
+</ul>
+<h2><a name="design_instances" id=
+"design_instances">design_instances</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="design_id" id="design_id">design_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="experiment_id" id=
+"experiment_id">experiment_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = design_id</strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = design_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#designs">designs</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#experiments">experiments</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="designs" id="designs">designs</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="display_label" id=
+"display_label">display_label</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="ontology_category" id=
+"ontology_category">ontology_category</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="ontology_value" id=
+"ontology_value">ontology_value</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="design_type" id="design_type">design_type</a></h4>
+<ul>
+<li><strong><code>char(15)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h2><a name="events" id="events">events</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="array_design_id" id=
+"array_design_id">array_design_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="experiment_id" id=
+"experiment_id">experiment_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="event_type" id="event_type">event_type</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="was_successful" id=
+"was_successful">was_successful</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="source_db" id="source_db">source_db</a></h4>
+<ul>
+<li><strong><code>varchar(30)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="target_db" id="target_db">target_db</a></h4>
+<ul>
+<li><strong><code>varchar(30)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="start_time" id="start_time">start_time</a></h4>
+<ul>
+<li><strong>datetime</strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="end_time" id="end_time">end_time</a></h4>
+<ul>
+<li><strong>datetime</strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="machine" id="machine">machine</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="operator" id="operator">operator</a></h4>
+<ul>
+<li><strong><code>varchar(30)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="log_file" id="log_file">log_file</a></h4>
+<ul>
+<li><strong><code>varchar(511)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="jobregister_dbid" id=
+"jobregister_dbid">jobregister_dbid</a></h4>
+<ul>
+<li><strong><code>int(15)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="comment" id="comment">comment</a></h4>
+<ul>
+<li><strong><code>text(65535)</code></strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = array_design_id</strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = array_design_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#array_designs">array_designs</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#experiments">experiments</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+</ul>
+<h2><a name="experiments" id="experiments">experiments</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="accession" id="accession">accession</a></h4>
+<ul>
+<li><strong><code>varchar(255)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="name" id="name">name</a></h4>
+<ul>
+<li><strong><code>varchar(255)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="user_id" id="user_id">user_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="checker_score" id=
+"checker_score">checker_score</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="software" id="software">software</a></h4>
+<ul>
+<li><strong><code>varchar(100)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="status" id="status">status</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="data_warehouse_ready" id=
+"data_warehouse_ready">data_warehouse_ready</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="date_last_edited" id=
+"date_last_edited">date_last_edited</a></h4>
+<ul>
+<li><strong>datetime</strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="date_submitted" id=
+"date_submitted">date_submitted</a></h4>
+<ul>
+<li><strong>datetime</strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="date_last_processed" id=
+"date_last_processed">date_last_processed</a></h4>
+<ul>
+<li><strong>datetime</strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="in_curation" id="in_curation">in_curation</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="curator" id="curator">curator</a></h4>
+<ul>
+<li><strong><code>char(30)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="comment" id="comment">comment</a></h4>
+<ul>
+<li><strong><code>text(65535)</code></strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="experiment_type" id=
+"experiment_type">experiment_type</a></h4>
+<ul>
+<li><strong><code>char(30)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="miamexpress_login" id=
+"miamexpress_login">miamexpress_login</a></h4>
+<ul>
+<li><strong><code>char(30)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="miamexpress_subid" id=
+"miamexpress_subid">miamexpress_subid</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_affymetrix" id=
+"is_affymetrix">is_affymetrix</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_mx_batchloader" id=
+"is_mx_batchloader">is_mx_batchloader</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="miame_score" id="miame_score">miame_score</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="in_data_warehouse" id=
+"in_data_warehouse">in_data_warehouse</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="num_submissions" id=
+"num_submissions">num_submissions</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="submitter_description" id=
+"submitter_description">submitter_description</a></h4>
+<ul>
+<li><strong><code>text(65535)</code></strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="curated_name" id="curated_name">curated_name</a></h4>
+<ul>
+<li><strong><code>varchar(255)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="num_samples" id="num_samples">num_samples</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="num_hybridizations" id=
+"num_hybridizations">num_hybridizations</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="has_raw_data" id="has_raw_data">has_raw_data</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="has_processed_data" id=
+"has_processed_data">has_processed_data</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="release_date" id="release_date">release_date</a></h4>
+<ul>
+<li><strong>datetime</strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_released" id="is_released">is_released</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="ae_miame_score" id=
+"ae_miame_score">ae_miame_score</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="ae_data_warehouse_score" id=
+"ae_data_warehouse_score">ae_data_warehouse_score</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_user_id" id=
+"item_fields__3d_user_id">Fields = user_id</a></strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = user_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2fusers" id=
+"item_reference_table__3d__2fusers">Reference Table =</a> <a href=
+"#users">users</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+</ul>
+<h2><a name="experiments_factors" id=
+"experiments_factors">experiments_factors</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="experiment_id" id=
+"experiment_id">experiment_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="factor_id" id="factor_id">factor_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_factor_id" id=
+"item_fields__3d_factor_id">Fields = factor_id</a></strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = factor_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2ffactors" id=
+"item_reference_table__3d__2ffactors">Reference Table =</a>
+<a href="#factors">factors</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#experiments">experiments</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="experiments_loaded_data" id=
+"experiments_loaded_data">experiments_loaded_data</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="experiment_id" id=
+"experiment_id">experiment_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="loaded_data_id" id=
+"loaded_data_id">loaded_data_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_loaded_data_id" id=
+"item_fields__3d_loaded_data_id">Fields =
+loaded_data_id</a></strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#experiments">experiments</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = loaded_data_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2floaded_data" id=
+"item_reference_table__3d__2floaded_data">Reference Table =</a>
+<a href="#loaded_data">loaded_data</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="experiments_quantitation_types" id=
+"experiments_quantitation_types">experiments_quantitation_types</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="quantitation_type_id" id=
+"quantitation_type_id">quantitation_type_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="experiment_id" id=
+"experiment_id">experiment_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_quantitation_type_id" id=
+"item_fields__3d_quantitation_type_id">Fields =
+quantitation_type_id</a></strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = quantitation_type_id</strong></li>
+<li><strong><a name=
+"item_reference_table__3d__2fquantitation_types" id=
+"item_reference_table__3d__2fquantitation_types">Reference Table
+=</a> <a href=
+"#quantitation_types">quantitation_types</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#experiments">experiments</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="factors" id="factors">factors</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="name" id="name">name</a></h4>
+<ul>
+<li><strong><code>varchar(128)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h2><a name="loaded_data" id="loaded_data">loaded_data</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="identifier" id="identifier">identifier</a></h4>
+<ul>
+<li><strong><code>varchar(255)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="md5_hash" id="md5_hash">md5_hash</a></h4>
+<ul>
+<li><strong><code>char(35)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="data_format_id" id=
+"data_format_id">data_format_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="platform_id" id="platform_id">platform_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="needs_metrics_calculation" id=
+"needs_metrics_calculation">needs_metrics_calculation</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="date_hashed" id="date_hashed">date_hashed</a></h4>
+<ul>
+<li><strong>datetime</strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_data_format_id" id=
+"item_fields__3d_data_format_id">Fields =
+data_format_id</a></strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_platform_id" id=
+"item_fields__3d_platform_id">Fields =
+platform_id</a></strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = platform_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2fplatforms" id=
+"item_reference_table__3d__2fplatforms">Reference Table =</a>
+<a href="#platforms">platforms</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = data_format_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2fdata_formats" id=
+"item_reference_table__3d__2fdata_formats">Reference Table =</a>
+<a href="#data_formats">data_formats</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+</ul>
+<h2><a name="loaded_data_quality_metrics" id=
+"loaded_data_quality_metrics">loaded_data_quality_metrics</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="value" id="value">value</a></h4>
+<ul>
+<li><strong><a name="item_decimal" id=
+"item_decimal"><code>decimal(12,5)</code></a></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="quality_metric_id" id=
+"quality_metric_id">quality_metric_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="loaded_data_id" id=
+"loaded_data_id">loaded_data_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="date_calculated" id=
+"date_calculated">date_calculated</a></h4>
+<ul>
+<li><strong>datetime</strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_quality_metric_id" id=
+"item_fields__3d_quality_metric_id">Fields =
+quality_metric_id</a></strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = loaded_data_id</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = loaded_data_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#loaded_data">loaded_data</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = quality_metric_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2fquality_metrics"
+id="item_reference_table__3d__2fquality_metrics">Reference Table
+=</a> <a href="#quality_metrics">quality_metrics</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="material_instances" id=
+"material_instances">material_instances</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="material_id" id="material_id">material_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="experiment_id" id=
+"experiment_id">experiment_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = material_id</strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = material_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#materials">materials</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#experiments">experiments</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="materials" id="materials">materials</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="display_label" id=
+"display_label">display_label</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="ontology_category" id=
+"ontology_category">ontology_category</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="ontology_value" id=
+"ontology_value">ontology_value</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h2><a name="meta" id="meta">meta</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="name" id="name">name</a></h4>
+<ul>
+<li><strong><code>varchar(128)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong><a name="item_default__27_27" id=
+"item_default__27_27">Default ''</a></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="value" id="value">value</a></h4>
+<ul>
+<li><strong><code>varchar(128)</code></strong></li>
+<li><strong>Default ''</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = name</strong></li>
+</ul>
+<h2><a name="organism_instances" id=
+"organism_instances">organism_instances</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="organism_id" id="organism_id">organism_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="experiment_id" id=
+"experiment_id">experiment_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = organism_id</strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = organism_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#organisms">organisms</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#experiments">experiments</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="organisms" id="organisms">organisms</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="scientific_name" id=
+"scientific_name">scientific_name</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="common_name" id="common_name">common_name</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="accession" id="accession">accession</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="taxon_id" id="taxon_id">taxon_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = taxon_id</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = taxon_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#taxons">taxons</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+</ul>
+<h2><a name="permissions" id="permissions">permissions</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="name" id="name">name</a></h4>
+<ul>
+<li><strong><code>varchar(40)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="info" id="info">info</a></h4>
+<ul>
+<li><strong><code>varchar(80)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h2><a name="permissions_roles" id=
+"permissions_roles">permissions_roles</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="role_id" id="role_id">role_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="permission_id" id=
+"permission_id">permission_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_role_id" id=
+"item_fields__3d_role_id">Fields = role_id</a></strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_permission_id" id=
+"item_fields__3d_permission_id">Fields =
+permission_id</a></strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = role_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2froles" id=
+"item_reference_table__3d__2froles">Reference Table =</a> <a href=
+"#roles">roles</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = permission_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2fpermissions" id=
+"item_reference_table__3d__2fpermissions">Reference Table =</a>
+<a href="#permissions">permissions</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="platforms" id="platforms">platforms</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="name" id="name">name</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="unique" id="unique">UNIQUE</a></h4>
+<ul>
+<li><strong>Fields = name</strong></li>
+</ul>
+<h2><a name="protocols" id="protocols">protocols</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="accession" id="accession">accession</a></h4>
+<ul>
+<li><strong><code>char(15)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="user_accession" id=
+"user_accession">user_accession</a></h4>
+<ul>
+<li><strong><code>varchar(100)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="expt_accession" id=
+"expt_accession">expt_accession</a></h4>
+<ul>
+<li><strong><code>char(15)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="name" id="name">name</a></h4>
+<ul>
+<li><strong><code>varchar(255)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="date_last_processed" id=
+"date_last_processed">date_last_processed</a></h4>
+<ul>
+<li><strong>datetime</strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="comment" id="comment">comment</a></h4>
+<ul>
+<li><strong><code>text(65535)</code></strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="unique" id="unique">UNIQUE</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_accession" id=
+"item_fields__3d_accession">Fields = accession</a></strong></li>
+</ul>
+<h2><a name="quality_metrics" id=
+"quality_metrics">quality_metrics</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="type" id="type">type</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="description" id="description">description</a></h4>
+<ul>
+<li><strong><code>text(65535)</code></strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="unique" id="unique">UNIQUE</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_type" id=
+"item_fields__3d_type">Fields = type</a></strong></li>
+</ul>
+<h2><a name="quantitation_types" id=
+"quantitation_types">quantitation_types</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="name" id="name">name</a></h4>
+<ul>
+<li><strong><code>varchar(128)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h2><a name="roles" id="roles">roles</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="name" id="name">name</a></h4>
+<ul>
+<li><strong><code>varchar(40)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="info" id="info">info</a></h4>
+<ul>
+<li><strong><code>varchar(80)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h2><a name="roles_users" id="roles_users">roles_users</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="user_id" id="user_id">user_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="role_id" id="role_id">role_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = user_id</strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = role_id</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = user_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#users">users</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = role_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#roles">roles</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="spreadsheets" id="spreadsheets">spreadsheets</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="experiment_id" id=
+"experiment_id">experiment_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="name" id="name">name</a></h4>
+<ul>
+<li><strong><code>varchar(255)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#experiments">experiments</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+</ul>
+<h2><a name="taxons" id="taxons">taxons</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="scientific_name" id=
+"scientific_name">scientific_name</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="common_name" id="common_name">common_name</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="accession" id="accession">accession</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h2><a name="users" id="users">users</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="login" id="login">login</a></h4>
+<ul>
+<li><strong><code>varchar(40)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="name" id="name">name</a></h4>
+<ul>
+<li><strong><code>varchar(40)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="password" id="password">password</a></h4>
+<ul>
+<li><strong><code>varchar(40)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="email" id="email">email</a></h4>
+<ul>
+<li><strong><code>varchar(100)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="modified_at" id="modified_at">modified_at</a></h4>
+<ul>
+<li><strong>datetime</strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="created_at" id="created_at">created_at</a></h4>
+<ul>
+<li><strong>datetime</strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="access" id="access">access</a></h4>
+<ul>
+<li><strong>datetime</strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="unique" id="unique">UNIQUE</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_login" id=
+"item_fields__3d_login">Fields = login</a></strong></li>
+</ul>
+<hr />
+<h1><a name="produced_by" id="produced_by">PRODUCED BY</a></h1>
+<p>SQL::Translator::Producer::POD</p>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2004.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/AutoSubmission/Spreadsheet.html b/docs/ArrayExpress/AutoSubmission/Spreadsheet.html
new file mode 100644
index 0000000..8344337
--- /dev/null
+++ b/docs/ArrayExpress/AutoSubmission/Spreadsheet.html
@@ -0,0 +1,118 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::AutoSubmission::Spreadsheet - A
+Tab2MAGE/MAGE-TAB document templating class.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#methods">METHODS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: Spreadsheet.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::AutoSubmission::Spreadsheet - A Tab2MAGE/MAGE-TAB
+document templating class.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::AutoSubmission::Spreadsheet;
+ my $ss = ArrayExpress::AutoSubmission::Spreadsheet->new({
+     experiment => $expt,
+     url        => $self->query()->url(),
+     date       => date_now(),
+ });
+</pre>
+<pre>
+ $ss_content = $spreadsheet->magetab();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module provides Tab2MAGE and MAGE-TAB document templating
+functions. The functions take Class::DBI experiment objects from
+the tracking database and generate the appropriate output strings
+which can then be printed to file.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_new" id=
+"item_new"><code>new</code></a></strong></dt>
+<dd>
+<p>Object constructor, taking the following arguments:</p>
+</dd>
+<dd>
+<dl>
+<dt><strong><a name="item_experiment" id=
+"item_experiment"><code>experiment</code></a></strong></dt>
+<dd>
+<p>The Class::DBI experiment object to use in template
+generation.</p>
+</dd>
+<dt><strong><a name="item_url" id=
+"item_url"><code>url</code></a></strong></dt>
+<dd>
+<p>The URL of the page to which completed templates should be
+submitted (this is embedded in the template for convenience).</p>
+</dd>
+<dt><strong><a name="item_date" id=
+"item_date"><code>date</code></a></strong></dt>
+<dd>
+<p>The date of template generation.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="item_magetab" id=
+"item_magetab"><code>magetab</code></a></strong></dt>
+<dd>
+<p>Return a string representing the MAGE-TAB template for this
+experiment.</p>
+</dd>
+<dt><strong><a name="item_tab2mage" id=
+"item_tab2mage"><code>tab2mage</code></a></strong></dt>
+<dd>
+<p>Return a string representing the Tab2MAGE template for this
+experiment.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2008.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/AutoSubmission/WebForm.html b/docs/ArrayExpress/AutoSubmission/WebForm.html
new file mode 100644
index 0000000..871f4c0
--- /dev/null
+++ b/docs/ArrayExpress/AutoSubmission/WebForm.html
@@ -0,0 +1,118 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::AutoSubmission::WebForm - A CGI::Application
+web form for Tab2MAGE/MAGE-TAB data submissions.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#methods">METHODS</a></li>
+        <li><a href="#todo">TODO</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: WebForm.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::AutoSubmission::WebForm - A CGI::Application web
+form for Tab2MAGE/MAGE-TAB data submissions.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::AutoSubmission::WebForm;
+ my $webapp = ArrayExpress::AutoSubmission::WebForm->new(
+     PARAMS => {
+        experiment_type  => 'MAGE-TAB',
+        spreadsheet_type => 'MAGE-TAB',
+        cgi_base         => '/cgi-bin/microarray/magetab.cgi',
+        stylesheet       => '/microarray/tab2mage.css',
+        intro_html_template    => 'magetab_intro.html',
+        upload_html_template   => 'magetab_upload.html',
+        template_html_template => 'magetab_template.html',
+        sidebar_icons   => [
+            {
+                image       => '/microarray/aelogo.png',
+                destination => '<a href=
+"http://www.ebi.ac.uk/arrayexpress/">http://www.ebi.ac.uk/arrayexpress/</a>',
+                alt         => 'ArrayExpress',
+            },
+        ],
+        sidebar_links   => [
+            {
+                text        => 'Microarray group',
+                destination => '<a href=
+"http://www.ebi.ac.uk/microarray/">http://www.ebi.ac.uk/microarray/</a>',
+            },
+        ],
+    }
+ );
+</pre>
+<pre>
+ # Start the CGI script.
+ $webapp->run();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module is the core of the web application used in the
+Tab2MAGE and MAGE-TAB data submission web forms. Objects are
+instantiated with a PARAMS attribute which can be used to customise
+various aspects of the web form behaviour.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_new" id=
+"item_new"><code>new</code></a></strong></dt>
+<dd>
+<p>Object constructor. See the CGI::Application documentation for
+details.</p>
+</dd>
+<dt><strong><a name="item_run" id=
+"item_run"><code>run</code></a></strong></dt>
+<dd>
+<p>The method used to start the web application.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="todo" id="todo">TODO</a></h1>
+<p>Documentation of the options which can be customised using the
+PARAMS attribute.</p>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2008.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Curator/Common.html b/docs/ArrayExpress/Curator/Common.html
new file mode 100644
index 0000000..891bb65
--- /dev/null
+++ b/docs/ArrayExpress/Curator/Common.html
@@ -0,0 +1,201 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Curator::Common.pm - a module providing some
+simple utility functions and regexps.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#functions">FUNCTIONS</a></li>
+        <li><a href="#regexps">REGEXPS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../index.html"><img src="../T2M_logo.png" border="0" height=
+"50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: Common.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Curator::Common.pm - a module providing some
+simple utility functions and regexps.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Curator::Common qw(date_now $RE_EMPTY_STRING);
+ 
+ if ( $test =~ $RE_EMPTY_STRING ) {
+    print date_now();
+ }
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This is a simple module providing utility functions and regexp
+patterns used elsewhere in the Tab2MAGE package.</p>
+<hr />
+<h1><a name="functions" id="functions">FUNCTIONS</a></h1>
+<dl>
+<dt><strong><a name="item_strip_discards" id=
+"item_strip_discards"><code>strip_discards( $indexcols, $line_array
+)</code></a></strong></dt>
+<dd>
+<p>Sub to strip values out of an arrayref ($line_array) based on an
+arrayref of unwanted array indices ($indexcols). Does not modify
+the input lists, but instead returns a suitably stripped
+arrayref.</p>
+</dd>
+<dt><strong><a name="item_round" id="item_round"><code>round(
+$number, $precision )</code></a></strong></dt>
+<dd>
+<p>A simple rounding function that returns $number rounded to
+$precision decimal places.</p>
+</dd>
+<dt><strong><a name="item_get_indexcol" id=
+"item_get_indexcol"><code>get_indexcol( $list, $name
+)</code></a></strong></dt>
+<dd>
+<p>Function to return the first index within @$list matching the
+string or regexp passed as $name. Returns -1 on failure.</p>
+</dd>
+<dt><strong><a name="item_check_linebreaks" id=
+"item_check_linebreaks"><code>check_linebreaks( $path
+)</code></a></strong></dt>
+<dd>
+<p>Takes a filename as an argument, checks for Mac, Unix or DOS
+line endings by reading the whole file in chunks, and regexp
+matching the various linebreak types. Returns the appropriate
+linebreak for acceptable line breaks (line breaks must be
+unanimous), undef if a consensus is unreachable.</p>
+</dd>
+<dt><strong><a name="item_clean_hash" id=
+"item_clean_hash"><code>clean_hash( $hashref
+)</code></a></strong></dt>
+<dd>
+<p>Strip out undef or empty string values from $hashref.</p>
+</dd>
+<dt><strong><a name="item_date_now" id=
+"item_date_now"><code>date_now()</code></a></strong></dt>
+<dd>
+<p>Return current date and time as a MAGE best practice date-time
+string (uses mage_date(), below).</p>
+</dd>
+<dt><strong><a name="item_mage_date" id=
+"item_mage_date"><code>mage_date( $time )</code></a></strong></dt>
+<dd>
+<p>When passed the return value from e.g. perl's
+<code>time()</code> built-in, returns a MAGE best practice
+date-time string. This string will always be from the UTC/GMT time
+zone.</p>
+</dd>
+<dt><strong><a name="item_untaint" id="item_untaint"><code>untaint(
+$string )</code></a></strong></dt>
+<dd>
+<p>A convenient data untainting function which replaces any run of
+non-whitelisted characters with a single underscore.</p>
+</dd>
+<dt><strong><a name="item_find_cdf" id=
+"item_find_cdf"><code>find_cdf( $name, $dir
+)</code></a></strong></dt>
+<dd>
+<p>Given a filename and an optional directory, returns an actual
+filename found in a case-insensitive fashion. Preferentially checks
+the main Affy library directory if set in $CONFIG (see <a href=
+"./Config.html">the ArrayExpress::Curator::Config manpage</a>).</p>
+</dd>
+<dt><strong><a name="item_decamelize" id=
+"item_decamelize"><code>decamelize( $string
+)</code></a></strong></dt>
+<dd>
+<p>Function to convert CamelCase strings to
+underscore_delimited.</p>
+</dd>
+<dt><strong><a name="item_get_filepath_from_uri" id=
+"item_get_filepath_from_uri"><code>get_filepath_from_uri( $string,
+$dir )</code></a></strong></dt>
+<dd>
+<p>Given a string and an optional directory argument, this function
+determines which URI scheme is in use (default is ``file://''), and
+downloads ``http://'' and ``ftp://'' URIs to either the indicated
+filesystem directory or the current working directory. Returns the
+filesystem path to the file.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="regexps" id="regexps">REGEXPS</a></h1>
+<dl>
+<dt><strong><a name="item__re_empty_string" id=
+"item__re_empty_string"><code>$RE_EMPTY_STRING</code></a></strong></dt>
+<dd>
+<p>Matches an empty string (whitespace ignored).</p>
+</dd>
+<dt><strong><a name="item__re_commented_string" id=
+"item__re_commented_string"><code>$RE_COMMENTED_STRING</code></a></strong></dt>
+<dd>
+<p>Matches a string beginning with #.</p>
+</dd>
+<dt><strong><a name="item__re_surrounded_by_whitespace" id=
+"item__re_surrounded_by_whitespace"><code>$RE_SURROUNDED_BY_WHITESPACE</code></a></strong></dt>
+<dd>
+<p>Matches a string with whitespace on either side; $1 contains the
+string minus the whitespace.</p>
+</dd>
+<dt><strong><a name="item__re_within_parentheses" id=
+"item__re_within_parentheses"><code>$RE_WITHIN_PARENTHESES</code></a></strong></dt>
+<dd>
+<p>Matches a string with parentheses on either side; $1 contains
+the string minus the parentheses.</p>
+</dd>
+<dt><strong><a name="item__re_within_brackets" id=
+"item__re_within_brackets"><code>$RE_WITHIN_BRACKETS</code></a></strong></dt>
+<dd>
+<p>Matches a string with brackets on either side; $1 contains the
+string minus the brackets.</p>
+</dd>
+<dt><strong><a name="item__re_square_brackets" id=
+"item__re_square_brackets"><code>$RE_SQUARE_BRACKETS</code></a></strong></dt>
+<dd>
+<p>Matches either [ or ]; $1 contains the matched character.</p>
+</dd>
+<dt><strong><a name="item__re_line_break" id=
+"item__re_line_break"><code>$RE_LINE_BREAK</code></a></strong></dt>
+<dd>
+<p>Matches a linebreak (DOS, Unix or MacOS).</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2008.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Curator/Config.html b/docs/ArrayExpress/Curator/Config.html
new file mode 100644
index 0000000..cf1c2a2
--- /dev/null
+++ b/docs/ArrayExpress/Curator/Config.html
@@ -0,0 +1,462 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Curator::Config.pm - a module defining general
+config options.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <ul>
+
+                <li><a href="#configuration_options">Configuration options</a></li>
+        </ul>
+
+        <li><a href="#private_options">Private options</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: ExperimentChecker.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Curator::Config.pm - a module defining general
+config options.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Curator::Config qw($CONFIG);
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module provides definition of some configuration options
+used throughout the code. These options may be changed by editing
+the ArrayExpress/Curator/Config.yml file. If you wish to point your
+Tab2MAGE installation to an alternate site config YAML file, please
+edit the $siteconf variable in the ArrayExpress/Curator/Config.pm
+file. For user-specific configuration, this module will also read
+any .tab2mage.conf file located in your home directory (i.e.,
+$HOME/.tab2mage.conf).</p>
+<p>See <a href="MAGE/Definitions.html">the
+ArrayExpress::Curator::MAGE::Definitions manpage</a> for other
+MAGE-specific constants.</p>
+<h2><a name="configuration_options" id=
+"configuration_options">Configuration options</a></h2>
+<dl>
+<dt><strong><a name="item_mx_dsn" id=
+"item_mx_dsn">MX_DSN</a></strong></dt>
+<dd>
+<p>MIAMExpress data source name or DSN, e.g.,
+``DBI:mysql:db_name:host:port''.</p>
+</dd>
+<dt><strong><a name="item_mx_username" id=
+"item_mx_username">MX_USERNAME</a></strong></dt>
+<dd>
+<p>The username used to connect to the MIAMExpress database.</p>
+</dd>
+<dt><strong><a name="item_mx_password" id=
+"item_mx_password">MX_PASSWORD</a></strong></dt>
+<dd>
+<p>The password used to connect to the MIAMExpress database.</p>
+</dd>
+<dt><strong><a name="item_ae_dsn" id=
+"item_ae_dsn">AE_DSN</a></strong></dt>
+<dd>
+<p>For local ArrayExpress installations only. This is the
+ArrayExpress data source name or DSN, e.g.,
+``DBI:Oracle:db_name:host:port'' used in direct connection to the
+database. Note that the appropriate DBD::Oracle module must be
+installed for this to work.</p>
+</dd>
+<dt><strong><a name="item_ae_username" id=
+"item_ae_username">AE_USERNAME</a></strong></dt>
+<dd>
+<p>The username used to connect to the ArrayExpress database.</p>
+</dd>
+<dt><strong><a name="item_ae_password" id=
+"item_ae_password">AE_PASSWORD</a></strong></dt>
+<dd>
+<p>The password used to connect to the ArrayExpress database.</p>
+</dd>
+<dt><strong><a name="item_aedw_dsn" id=
+"item_aedw_dsn">AEDW_DSN</a></strong></dt>
+<dd>
+<p>For checking the data warehouse readiness of an experiment
+submission. This database connection is used in checking that any
+required array designs have been loaded into the data warehouse.
+This config value should be the ArrayExpress DW data source name or
+DSN, e.g., ``DBI:Oracle:db_name:host:port'' used in direct
+connection to the data warehouse. Note that the appropriate
+DBD::Oracle module must be installed for this to work.</p>
+</dd>
+<dt><strong><a name="item_aedw_username" id=
+"item_aedw_username">AEDW_USERNAME</a></strong></dt>
+<dd>
+<p>The username used to connect to the AEDW database.</p>
+</dd>
+<dt><strong><a name="item_aedw_password" id=
+"item_aedw_password">AEDW_PASSWORD</a></strong></dt>
+<dd>
+<p>The password used to connect to the AEDW database.</p>
+</dd>
+<dt><strong><a name="item_ae_arraydesign_list" id=
+"item_ae_arraydesign_list">AE_ARRAYDESIGN_LIST</a></strong></dt>
+<dd>
+<p>Remote ArrayExpress array design list web page. Used by
+Tab2MAGE, MIAMExpress and the experiment checker. This is currently
+accessible from outside EBI, and so the setting can be left as it
+is.</p>
+</dd>
+<dt><strong><a name="item_ae_retrieve_featurelist" id=
+"item_ae_retrieve_featurelist">AE_RETRIEVE_FEATURELIST</a></strong></dt>
+<dd>
+<p>The ArrayExpress web page root for retrieving array feature
+lists. In use, the accession number for the PhysicalArrayDesign is
+appended to AE_RETRIEVE_FEATURELIST before it is used in an HTTP
+GET.</p>
+</dd>
+<dt><strong><a name="item_ae_retrieve_adf" id=
+"item_ae_retrieve_adf">AE_RETRIEVE_ADF</a></strong></dt>
+<dd>
+<p>The ArrayExpress web page root for retrieving ADFs. In use, the
+database identifier for the PhysicalArrayDesign is appended to
+AE_RETRIEVE_ADF before it is used in an HTTP GET.</p>
+</dd>
+<dt><strong><a name="item_affymetrix_librarypath" id=
+"item_affymetrix_librarypath">AFFYMETRIX_LIBRARYPATH</a></strong></dt>
+<dd>
+<p>The path to a directory containing CDF files needed for parsing
+Affymetrix CHP files. The default value is an empty string, which
+indicates to the scripts that the CDF files are in the current
+working directory.</p>
+</dd>
+<dt><strong><a name="item_t2m_protocol_prefix" id=
+"item_t2m_protocol_prefix">T2M_PROTOCOL_PREFIX</a></strong></dt>
+<dd>
+<p>The prefix used to autogenerate reassigned protocol accessions.
+Default value is ``P-TABM-''. Please note that you should change
+this to prevent conflicts if you intend to use protocol accession
+reassignment and submit the resulting MAGE-ML to ArrayExpress.</p>
+</dd>
+<dt><strong><a name="item_t2m_experiment_prefix" id=
+"item_t2m_experiment_prefix">T2M_EXPERIMENT_PREFIX</a></strong></dt>
+<dd>
+<p>The prefix used in creating experiment accessions. This is used
+to check that a valid experiment accession number has been used in
+conjunction with the protocol reassignment mechanism.</p>
+</dd>
+<dt><strong><a name="item_max_lwp_download" id=
+"item_max_lwp_download">MAX_LWP_DOWNLOAD</a></strong></dt>
+<dd>
+<p>The maximum size of LWP::UserAgent downloads. This applies to
+ArrayExpress ADF and feature list downloads. Currently set to
+40MB.</p>
+</dd>
+<dt><strong><a name="item_max_datafile_size" id=
+"item_max_datafile_size">MAX_DATAFILE_SIZE</a></strong></dt>
+<dd>
+<p>The maximum size of data file which these scripts will attempt
+to parse. Currently set to 100MB.</p>
+</dd>
+<dt><strong><a name="item_visualize_font" id=
+"item_visualize_font">VISUALIZE_FONT</a></strong></dt>
+<dd>
+<p>The name of the default font to use in visualization graph
+creation. This gets passed to the ``dot'' program. Currently set to
+``Courier''.</p>
+</dd>
+<dt><strong><a name="item_autosubs_pidfile" id=
+"item_autosubs_pidfile">AUTOSUBS_PIDFILE</a></strong></dt>
+<dd>
+<p>Full path to the file used by autosubs_checkd.pl to track
+running instances of itself.</p>
+</dd>
+<dt><strong><a name="item_autosubs_admin" id=
+"item_autosubs_admin">AUTOSUBS_ADMIN</a></strong></dt>
+<dd>
+<p>Email address of the administrator responsible for managing the
+checker and exporter daemon processes. Emails will be sent to this
+address on abnormal termination of the process (e.g. on
+crashes).</p>
+</dd>
+<dt><strong><a name="item_autosubs_admin_username" id=
+"item_autosubs_admin_username">AUTOSUBS_ADMIN_USERNAME</a></strong></dt>
+<dd>
+<p>Login for the administrator responsible for managing the checker
+and exporter daemon processes. Other users are restricted from
+launching these daemons to aid in process management.</p>
+</dd>
+<dt><strong><a name="item_autosubs_domain" id=
+"item_autosubs_domain">AUTOSUBS_DOMAIN</a></strong></dt>
+<dd>
+<p>The default domain used in creating MAGE-ML identifiers when
+exporting experiments using the autosubmissions system. This is set
+by default to 'ebi.ac.uk'. Note that this does not affect
+submissions exported from a MIAMExpress database.</p>
+</dd>
+<dt><strong><a name="item_autosubs_curator_email" id=
+"item_autosubs_curator_email">AUTOSUBS_CURATOR_EMAIL</a></strong></dt>
+<dd>
+<p>The email to which enquiries will be directed from the
+autosubmissions web form.</p>
+</dd>
+<dt><strong><a name="item_autosubs_smtp_server" id=
+"item_autosubs_smtp_server">AUTOSUBS_SMTP_SERVER</a></strong></dt>
+<dd>
+<p>The SMTP server used to send notification email from the
+autosubmissions system.</p>
+</dd>
+<dt><strong><a name="item_autosubs_dsn" id=
+"item_autosubs_dsn">AUTOSUBS_DSN</a></strong></dt>
+<dd>
+<p>The DSN to use when connecting to the autosubmissions database
+system. Typically this will be of the form
+``DBI:mysql:dbname:host:port''.</p>
+</dd>
+<dt><strong><a name="item_autosubs_username" id=
+"item_autosubs_username">AUTOSUBS_USERNAME</a></strong></dt>
+<dd>
+<p>The username to use when connecting to the autosubmissions
+database.</p>
+</dd>
+<dt><strong><a name="item_autosubs_password" id=
+"item_autosubs_password">AUTOSUBS_PASSWORD</a></strong></dt>
+<dd>
+<p>The password to use when connecting to the autosubmissions
+database.</p>
+</dd>
+<dt><strong><a name="item_autosubmissions_filebase" id=
+"item_autosubmissions_filebase">AUTOSUBMISSIONS_FILEBASE</a></strong></dt>
+<dd>
+<p>The filesystem path to the top-level directory where the
+autosubmissions system should store uploaded spreadsheets and data
+files.</p>
+</dd>
+<dt><strong><a name="item_autosubmissions_target" id=
+"item_autosubmissions_target">AUTOSUBMISSIONS_TARGET</a></strong></dt>
+<dd>
+<p>The filesystem directory into which new submissions are exported
+as MAGE-ML. A new directory, named using the automatically assigned
+experiment accession, will be created and populated.</p>
+</dd>
+<dt><strong><a name="item_webform_template_dir" id=
+"item_webform_template_dir">WEBFORM_TEMPLATE_DIR</a></strong></dt>
+<dd>
+<p>The filesystem directory where the web submissions form may
+write temporary files. This is principally used for temporary
+storage of spreadsheet template files. It is recommended that you
+run a regular script from e.g. your crontab to delete old
+files.</p>
+</dd>
+<dt><strong><a name="item_webform_template_url" id=
+"item_webform_template_url">WEBFORM_TEMPLATE_URL</a></strong></dt>
+<dd>
+<p>The URL pointing to the WEBFORM_TEMPLATE_DIR, used to create
+links to the temporary template files from the web submissions
+form.</p>
+</dd>
+<dt><strong><a name="item_mx_autosubs_pidfile" id=
+"item_mx_autosubs_pidfile">MX_AUTOSUBS_PIDFILE</a></strong></dt>
+<dd>
+<p>Full path to the file used by mx_autocheck_daemon.pl to track
+running instances of itself.</p>
+</dd>
+<dt><strong><a name="item_mx_autosubs_admin" id=
+"item_mx_autosubs_admin">MX_AUTOSUBS_ADMIN</a></strong></dt>
+<dd>
+<p>Email address of the administrator responsible for managing the
+mx_autocheck_daemon.pl process. See notes for AUTOSUBS_ADMIN.</p>
+</dd>
+<dt><strong><a name="item_mx_mageml_export_command" id=
+"item_mx_mageml_export_command">MX_MAGEML_EXPORT_COMMAND</a></strong></dt>
+<dd>
+<p>Full command (minus arguments) used to export MAGE-ML from a
+local MIAMExpress database.</p>
+</dd>
+<dt><strong><a name="item_aedw_design_types" id=
+"item_aedw_design_types">AEDW_DESIGN_TYPES</a></strong></dt>
+<dd>
+<p>A list of MO ExperimentDesignTypes that indicate an experiment
+is suitable for the ArrayExpress Data Warehouse.</p>
+</dd>
+<dt><strong><a name="item_aedw_unwanted_design_types" id=
+"item_aedw_unwanted_design_types">AEDW_UNWANTED_DESIGN_TYPES</a></strong></dt>
+<dd>
+<p>A list of MO ExperimentDesignTypes that indicate an experiment
+is not suitable for the ArrayExpress Data Warehouse. This is used
+to differentiate between experiments known to be of the wrong type
+from those which are merely under-annotated.</p>
+</dd>
+<dt><strong><a name="item_aedw_minimum_hybs" id=
+"item_aedw_minimum_hybs">AEDW_MINIMUM_HYBS</a></strong></dt>
+<dd>
+<p>The minimum number of hybridizations required in an experiment
+for it to be considered for the AE Data Warehouse.</p>
+</dd>
+<dt><strong><a name="item_miame_compliant_array_pipelines" id=
+"item_miame_compliant_array_pipelines">MIAME_COMPLIANT_ARRAY_PIPELINES</a></strong></dt>
+<dd>
+<p>A list of array design accession prefixes (e.g. ``A-AFFY-'')
+where every design can be assumed to be MIAME compliant, for the
+purposes of experiment MIAME checking.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="private_options" id="private_options">Private
+options</a></h1>
+<p>These are options we recommend you don't change, unless you know
+what you're doing.</p>
+<dl>
+<dt><strong><a name="item_t2m_indices" id=
+"item_t2m_indices">T2M_INDICES</a></strong></dt>
+<dd>
+<p>A hashref, with keys representing data file format type and
+values as arrayrefs listing the coordinate index columns to be used
+for parsing those formats. See also <a href="../Datafile.html">the
+ArrayExpress::Datafile manpage</a>. Current format types are:
+Generic, GenePix, Affymetrix, ArrayVision, Agilent, Scanalyze,
+ScanArray, QuantArray, Spotfinder, BlueFuse, UCSF Spot, Illumina,
+CodeLink, Applied Biosystems, NimbleScan.</p>
+</dd>
+<dt><strong><a name="item_t2m_file_types" id=
+"item_t2m_file_types">T2M_FILE_TYPES</a></strong></dt>
+<dd>
+<p>Supported data file types for per-hyb parsing and MAGE-ML
+creation. These tags can appear in the Tab2MAGE spreadsheet as part
+of a File[] column heading. Currently supported: ``raw'' and
+``normalized''.</p>
+</dd>
+<dt><strong><a name="item_fgem_file_type" id=
+"item_fgem_file_type">FGEM_FILE_TYPE</a></strong></dt>
+<dd>
+<p>Supported data file type for FGEM parsing and MAGE-ML creation.
+These tags can appear in the Tab2MAGE spreadsheet as part of a
+File[] column heading. Currently supported: ``transformed''.</p>
+</dd>
+<dt><strong><a name="item_ignored_qts" id=
+"item_ignored_qts">IGNORED_QTS</a></strong></dt>
+<dd>
+<p>A list of regular expression which match QTs which are omitted
+from analyses or MAGE-ML output from non-data matrix files.</p>
+</dd>
+<dt><strong><a name=
+"item_error_innocent_2c_error_miame_2c_error_parsebad_2c" id=
+"item_error_innocent_2c_error_miame_2c_error_parsebad_2c">ERROR_INNOCENT,
+ERROR_MIAME, ERROR_PARSEBAD, ERROR_PARSEFAIL</a></strong></dt>
+<dd>
+<p>Errors returned to the shell by each of the scripts are
+represented by 8-bit integers; here we map them to the constants
+used.</p>
+</dd>
+<dt><strong><a name="item_error_checkercrash" id=
+"item_error_checkercrash">ERROR_CHECKERCRASH</a></strong></dt>
+<dd>
+<p>Similar to the errors above, this error indicates that the
+checking process crashed for some reason.</p>
+</dd>
+<dt><strong><a name=
+"item_error_message_args_2c_error_message_private" id=
+"item_error_message_args_2c_error_message_private">ERROR_MESSAGE_ARGS,
+ERROR_MESSAGE_PRIVATE</a></strong></dt>
+<dd>
+<p>A selection of internal error message texts.</p>
+</dd>
+<dt><strong><a name="item_mx_extended_report" id=
+"item_mx_extended_report">MX_EXTENDED_REPORT</a></strong></dt>
+<dd>
+<p>Controls whether extended reporting of sample annotation and
+factor values is available (only supported for generic MIAMExpress
+installations).</p>
+</dd>
+<dt><strong><a name="item_default_qt_filename" id=
+"item_default_qt_filename">DEFAULT_QT_FILENAME</a></strong></dt>
+<dd>
+<p>Name of the file to use as the default source of QT information.
+This is used to point to a file installed alongside the perl
+modules, and should not be changed unless you know what you're
+doing.</p>
+</dd>
+<dt><strong><a name="item_default_entrez_filename" id=
+"item_default_entrez_filename">DEFAULT_ENTREZ_FILENAME</a></strong></dt>
+<dd>
+<p>Name of the file containing a list of Entrez-approved
+publication abbreviations. Again, this value should not be changed
+unless strictly necessary.</p>
+</dd>
+<dt><strong><a name="item_file_permissions" id=
+"item_file_permissions">FILE_PERMISSIONS</a></strong></dt>
+<dd>
+<p>Octal number indicating the default permissions to use when
+creating files using the autosubmission system. This is useful if,
+for example, your webserver process is in a different group from
+that of your users. The default is 0555.</p>
+</dd>
+<dt><strong><a name="item_dir_permissions" id=
+"item_dir_permissions">DIR_PERMISSIONS</a></strong></dt>
+<dd>
+<p>Octal number indicating the default directory permissions for
+the autosubmission system. The default is 0777.</p>
+</dd>
+<dt><strong><a name="item_mx_dbparams" id=
+"item_mx_dbparams">MX_DBPARAMS</a></strong></dt>
+<dd>
+<p>A hashref of parameters used in the MIAMExpress database
+connection.</p>
+</dd>
+<dt><strong><a name="item_ae_dbparams" id=
+"item_ae_dbparams">AE_DBPARAMS</a></strong></dt>
+<dd>
+<p>A hashref of parameters used in the ArrayExpress database
+connection.</p>
+</dd>
+<dt><strong><a name="item_aedw_dbparams" id=
+"item_aedw_dbparams">AEDW_DBPARAMS</a></strong></dt>
+<dd>
+<p>A hashref of parameters used in the AEDW database
+connection.</p>
+</dd>
+<dt><strong><a name="item_autosubs_dbparams" id=
+"item_autosubs_dbparams">AUTOSUBS_DBPARAMS</a></strong></dt>
+<dd>
+<p>A hashref of parameters used in the autosubmissions database
+connection.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2004.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Curator/Database.html b/docs/ArrayExpress/Curator/Database.html
new file mode 100644
index 0000000..7bcd15b
--- /dev/null
+++ b/docs/ArrayExpress/Curator/Database.html
@@ -0,0 +1,137 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Curator::Database - a module used by
+expt_check.pl</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#functions">FUNCTIONS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../index.html"><img src="../T2M_logo.png" border="0" height=
+"50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: Database.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Curator::Database - a module used by
+expt_check.pl</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Curator::Database qw(retrieve_AE_adf parse_adf);
+</pre>
+<pre>
+ my $db_id = 123456789;
+ my ( $fh, $accession ) = retrieve_AE_adf( $db_id, undef, \*STDERR, 0 );
+ my $array_design = parse_adf( $fh, $accession );
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module provides a set of subroutines used by the experiment
+checker script to interact with the ArrayExpress databases. Queries
+may be redirected to local ArrayExpress instances by editing values
+in the <a href="./Config.html">the ArrayExpress::Curator::Config
+manpage</a> package.</p>
+<hr />
+<h1><a name="functions" id="functions">FUNCTIONS</a></h1>
+<dl>
+<dt><strong><a name="item_get_ae_dbh" id=
+"item_get_ae_dbh"><code>get_ae_dbh()</code></a></strong></dt>
+<dd>
+<p>Returns a singleton database handle for the currently-configured
+ArrayExpress repository database instance.</p>
+</dd>
+<dt><strong><a name="item_get_aedw_dbh" id=
+"item_get_aedw_dbh"><code>get_aedw_dbh()</code></a></strong></dt>
+<dd>
+<p>Returns a singleton database handle for the currently-configured
+ArrayExpress warehouse database instance.</p>
+</dd>
+<dt><strong><a name="item_retrieve_ae_adf" id=
+"item_retrieve_ae_adf"><code>retrieve_AE_adf( $db_id, $accession,
+$error_fh, $skip_adf_download )</code></a></strong></dt>
+<dd>
+<p>Given either AE database table row identifier or AE accession,
+an open filehandle for error reporting and a flag indicating
+whether the ADF should be downloaded, check for the existence of
+said ADF (by database id or accession), download it and return an
+opened filehandle and the accession number (the latter is useful
+when querying by database id).</p>
+</dd>
+<dt><strong><a name="item_retrieve_ae_featurelist" id=
+"item_retrieve_ae_featurelist"><code>retrieve_AE_featurelist(
+$accession )</code></a></strong></dt>
+<dd>
+<p>Given an array accession number, return a hashref where the keys
+are q{.}-delimited lists of MetaColumn.MetaRow.Column.Row feature
+coordinates, and the values are Reporter identifiers.</p>
+</dd>
+<dt><strong><a name="item_parse_adf" id=
+"item_parse_adf"><code>parse_adf( $adf_fh, $accno,
+$reporter_prefix, $compseq_prefix )</code></a></strong></dt>
+<dd>
+<p>Passed an ADF filehandle and an accession number (see
+retrieve_AE_adf()), and optional Reporter or Composite Sequence
+identifier prefixes, parse the ADF and return an ArrayDesign object
+(see <a href="../Datafile/ArrayDesign.html">the
+ArrayExpress::Datafile::ArrayDesign manpage</a>).</p>
+</dd>
+<dt><strong><a name="item_arrayaccession_in_aedw" id=
+"item_arrayaccession_in_aedw"><code>arrayaccession_in_aedw(
+$accession )</code></a></strong></dt>
+<dd>
+<p>Given an array design accession number, return true if the
+design is loaded into the currently-configured AE data warehouse,
+false otherwise.</p>
+</dd>
+<dt><strong><a name="item_map_affy_accno_to_name" id=
+"item_map_affy_accno_to_name"><code>map_affy_accno_to_name(
+$accession )</code></a></strong></dt>
+<dd>
+<p>Given an array design accession number, attempt to match it to
+an Affymetrix design name. This relies on the names of loaded Affy
+array designs including the design name (e.g. HG-U133A) in square
+brackets (e.g. ``Affymetrix GeneChip Human Genome HG-U133A
+[HG-U133A]'').</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2008.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Curator/Entrez_list.html b/docs/ArrayExpress/Curator/Entrez_list.html
new file mode 100644
index 0000000..e070fd3
--- /dev/null
+++ b/docs/ArrayExpress/Curator/Entrez_list.html
@@ -0,0 +1,82 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Curator::Entrez_list - a module used by
+expt_check.pl</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#functions">FUNCTIONS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../index.html"><img src="../T2M_logo.png" border="0" height=
+"50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: Entrez_list.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Curator::Entrez_list - a module used by
+expt_check.pl</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Curator::Entrez_list qw(parse_entrez_names);
+ 
+ my $is_recognized = parse_entrez_names();
+ die("Error: Unknown journal: $pub") unless $is_recognized->{ $pub };
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module provides a very basic interface to the listing of
+approved Entrez publication names included with the Tab2MAGE
+package.</p>
+<hr />
+<h1><a name="functions" id="functions">FUNCTIONS</a></h1>
+<dl>
+<dt><strong><a name="item_parse_entrez_names" id=
+"item_parse_entrez_names"><code>parse_entrez_names( $fh
+)</code></a></strong></dt>
+<dd>
+<p>Parses the contents of either the optional passed filehandle, or
+the internally-defined Entrez_list.txt filehandle, to generate a
+hashref of publication name keys and true values.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2008.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Curator/ExperimentChecker.html b/docs/ArrayExpress/Curator/ExperimentChecker.html
new file mode 100644
index 0000000..71fbac8
--- /dev/null
+++ b/docs/ArrayExpress/Curator/ExperimentChecker.html
@@ -0,0 +1,392 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Curator::ExperimentChecker - a module used by
+expt_check.pl</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#options">OPTIONS</a></li>
+        <li><a href="#methods">METHODS</a></li>
+        <li><a href="#files">FILES</a></li>
+        <li><a href="#tests">TESTS</a></li>
+        <li><a href="#see_also">SEE ALSO</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: ExperimentChecker.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Curator::ExperimentChecker - a module used by
+expt_check.pl</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use base qw/ArrayExpress::Curator::ExperimentChecker/;
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module represents an abstract parent class providing
+methods for data file and experiment annotation checks to its child
+classes. See the following documentation for concrete checker
+classes: <a href="./Validate.html">the
+ArrayExpress::Curator::Validate manpage</a> (Tab2MAGE), <a href=
+"../MAGETAB/Checker.html">the ArrayExpress::MAGETAB::Checker
+manpage</a> (MAGE-TAB), <a href="./MIAMExpress.html">the
+ArrayExpress::Curator::MIAMExpress manpage</a> (MIAMExpress) and
+<a href="./Standalone.html">the ArrayExpress::Curator::Standalone
+manpage</a> (standalone).</p>
+<hr />
+<h1><a name="options" id="options">OPTIONS</a></h1>
+<p>The objects created by this module can be instantiated with the
+following options, common to all submission routes.</p>
+<dl>
+<dt><strong><a name="item_log_to_current_dir" id=
+"item_log_to_current_dir"><code>log_to_current_dir</code></a></strong></dt>
+<dd>
+<p>Write the log files to the current working directory, rather
+than in the submissions directory.</p>
+</dd>
+<dt><strong><a name="item_is_standalone" id=
+"item_is_standalone"><code>is_standalone</code></a></strong></dt>
+<dd>
+<p>Run checker in standalone mode.</p>
+</dd>
+<dt><strong><a name="item_clobber" id=
+"item_clobber"><code>clobber</code></a></strong></dt>
+<dd>
+<p>Overwrite existing log and graph files without asking for
+confirmation.</p>
+</dd>
+<dt><strong><a name="item_adf_filename" id=
+"item_adf_filename"><code>adf_filename</code></a></strong></dt>
+<dd>
+<p>The name of the file to use as ADF in feature/reporter checks.
+This overrides any array designs specified in the submission.</p>
+</dd>
+<dt><strong><a name="item_array_accession" id=
+"item_array_accession"><code>array_accession</code></a></strong></dt>
+<dd>
+<p>The ArrayExpress accession number of the array design to use for
+feature/reporter checks. This overrides any array accessions
+specified in the submission.</p>
+</dd>
+<dt><strong><a name="item_qt_filename" id=
+"item_qt_filename"><code>qt_filename</code></a></strong></dt>
+<dd>
+<p>The name of the file to be used for QT definitions. See <a href=
+"../Datafile/QT_list.html">the ArrayExpress::Datafile::QT_list
+manpage</a> for information on the format of this file. See also
+<a href=
+"#item_include_default_qts"><code>include_default_qts</code></a>.</p>
+</dd>
+<dt><strong><a name="item_include_default_qts" id=
+"item_include_default_qts"><code>include_default_qts</code></a></strong></dt>
+<dd>
+<p>Use the QT definitions supplied with these scripts alongside any
+new QT definitions provided by the <a href=
+"#item_qt_filename"><code>qt_filename</code></a> option.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_check" id=
+"item_check"><code>check()</code></a></strong></dt>
+<dd>
+<p>Starts the checks, based on the options specified in the
+constructor.</p>
+</dd>
+<dt><strong><a name="item_get_miamexpress_software_type" id=
+"item_get_miamexpress_software_type"><code>get_miamexpress_software_type()</code></a></strong></dt>
+<dd>
+<p>Returns the software term to use for a MIAMExpress export, if a
+unanimous verdict can be reached. Otherwise returns undef. This is
+also used for information purposes with Tab2MAGE and MAGE-TAB
+submissions, which is why it's in this superclass.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="files" id="files">FILES</a></h1>
+<p>There are currently five log files written out by the
+script:</p>
+<dl>
+<dt><strong><a name="item_expt_report_2elog" id=
+"item_expt_report_2elog"><code>expt_report.log</code></a></strong></dt>
+<dd>
+<p>Contains some summary information and data quality statistics
+for each data file.</p>
+</dd>
+<dt><strong><a name="item_expt_errors_2elog" id=
+"item_expt_errors_2elog"><code>expt_errors.log</code></a></strong></dt>
+<dd>
+<p>Lists the errors that were encountered in parsing the data
+files. Details on Feature and QuantitationType errors are given in
+separate log files. Qualifier Value Source usage is also logged in
+this error log file.</p>
+</dd>
+<dt><strong><a name="item_expt_biomaterials_2elog" id=
+"item_expt_biomaterials_2elog"><code>expt_biomaterials.log</code></a></strong></dt>
+<dd>
+<p>Provides a basic sanity check of the flow of BioMaterials
+through the experiment (i.e. Sample -> Extract -> Labeled
+Extract -> Hybridization). For Tab2MAGE checking the majority of
+the information in this file has been moved to an output PNG file,
+since it is a much clearer and more flexible visualization
+format.</p>
+</dd>
+<dt><strong><a name="item_expt_feature_2elog" id=
+"item_expt_feature_2elog"><code>expt_feature.log</code></a></strong></dt>
+<dd>
+<p>Lists feature coordinates and/or reporter identifiers (FGEM
+only) missing from the array <code>design(s)</code> used in the
+experiment. Entries here will typically mean that a dummy array has
+to be used. Update: This file now also lists duplicate features in
+a separate list.</p>
+</dd>
+<dt><strong><a name="item_expt_columnheadings_2elog" id=
+"item_expt_columnheadings_2elog"><code>expt_columnheadings.log</code></a></strong></dt>
+<dd>
+<p>Lists unrecognized QuantitationTypes or hybridization IDs (FGEM
+only) appearing in the data column headings.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="tests" id="tests">TESTS</a></h1>
+<p>The following tests are performed by this module, with output
+printed to the error and/or report filehandles:</p>
+<dl>
+<dt><strong><a name="item_file_existence" id=
+"item_file_existence"><strong>File
+existence</strong></a></strong></dt>
+<dd>
+<p>Checks that the data files referred to in the Tab2MAGE
+spreadsheet/MAGE-TAB SDRF/MIAMExpress database actually exist on
+the filesystem (error log).</p>
+</dd>
+<dt><strong><a name="item_affymetrix_chp_file_as_normalized_data"
+id="item_affymetrix_chp_file_as_normalized_data"><strong>Affymetrix
+CHP file as normalized data</strong></a></strong></dt>
+<dd>
+<p>Checks that CHP files have been submitted as normalized rather
+than raw data (error log).</p>
+</dd>
+<dt><strong><a name="item_text_file_check" id=
+"item_text_file_check"><strong>Text file
+check</strong></a></strong></dt>
+<dd>
+<p>Confirms that submitted data files are text, not binary. This
+test is not applied to Affymetrix CHP files.</p>
+</dd>
+<dt><strong><a name="item_file_line_endings_check" id=
+"item_file_line_endings_check"><strong>File line endings
+check</strong></a></strong></dt>
+<dd>
+<p>Checks for Unix/DOS/Mac line endings (report log).</p>
+</dd>
+<dt><strong><a name="item_affymetrix_exp_file_check" id=
+"item_affymetrix_exp_file_check"><strong>Affymetrix EXP file
+check</strong></a></strong></dt>
+<dd>
+<p>Checks that EXP files are submitted as raw data and that they
+all have Protocol, Station and Module information (error log).</p>
+</dd>
+<dt><strong><a name="item_basic_data_file_summary" id=
+"item_basic_data_file_summary"><strong>Basic data file
+summary</strong></a></strong></dt>
+<dd>
+<p>Prints out the file format (Affymetrix, GenePix or Generic), the
+type (raw, normalized or transformed), row and column counts
+(report log).</p>
+</dd>
+<dt><strong><a name="item_duplicate_columns" id=
+"item_duplicate_columns"><strong>Duplicate
+columns</strong></a></strong></dt>
+<dd>
+<p>Checks for repeated column headings in each data file (error
+log).</p>
+</dd>
+<dt><strong><a name="item_excel_truncated_files" id=
+"item_excel_truncated_files"><strong>Excel truncated
+files</strong></a></strong></dt>
+<dd>
+<p>Checks for possible data corruption by Excel truncation of the
+file (error log).</p>
+</dd>
+<dt><strong><a name="item_quantitationtypes" id=
+"item_quantitationtypes"><strong>QuantitationTypes</strong></a></strong></dt>
+<dd>
+<p>Checks column headings against a list of known QTs. Reports on
+unrecognized QTs (error log, column headings log). This is a work
+in progress.</p>
+</dd>
+<dt><strong><a name="item_fgem_hybridization_ids" id=
+"item_fgem_hybridization_ids"><strong>FGEM hybridization
+IDs</strong></a></strong></dt>
+<dd>
+<p>Checks FGEM column headings against the hybridization IDs for
+the submission, reports on those which are not recognized. This
+incorporates a check on the QTs for FGEM files (error log, column
+headings log).</p>
+</dd>
+<dt><strong><a name="item_fgem_biodatacube_order" id=
+"item_fgem_biodatacube_order"><strong>FGEM BioDataCube
+order</strong></a></strong></dt>
+<dd>
+<p>Checks that the included final data matrix is laid out in DBQ
+order, rather than DQB. The ArrayExpress MAGE-ML loader software
+does not support the DQB order.</p>
+</dd>
+<dt><strong><a name="item_data_checks" id=
+"item_data_checks"><strong>Data checks</strong></a></strong></dt>
+<dd>
+<p>Data checks are only performed on recognized QT columns. Checks
+are for:</p>
+</dd>
+<dd>
+<pre>
+  Text in numeric columns
+  Null values in numeric columns
+  Floats in integer columns
+  Inappropriate boolean values (i.e., not 0 or 1)
+  Log ratios outside reasonable range
+  Basic check on saturation indicators (primarily GenePix files)
+</pre></dd>
+<dd>
+<p>(error log).</p>
+</dd>
+<dt><strong><a name="item_benford_27s_law" id=
+"item_benford_27s_law"><strong>Benford's
+law</strong></a></strong></dt>
+<dd>
+<p>Calculates Benford's law across dimensioned float data. In
+theory this should be approximately 30% for good data (report
+log).</p>
+</dd>
+<dt><strong><a name="item_percent_null" id=
+"item_percent_null"><strong>Percent null</strong></a></strong></dt>
+<dd>
+<p>Calculates overall percent null across the whole data set. Zero
+values are also counted as null. In practice under 10% null values
+seems to be a reasonable expectation (report log).</p>
+</dd>
+<dt><strong><a name=
+"item_feature_2freporter_check_vs_2e_array_design" id=
+"item_feature_2freporter_check_vs_2e_array_design"><strong>Feature/Reporter
+check vs. array design</strong></a></strong></dt>
+<dd>
+<p>Checks the feature coordinates (raw, normalized data) or the
+reporter identifiers (FGEM) against either the array designs linked
+to the hybridization in MIAMExpress, or against a user-supplied
+ADF. Prints out a list of features not found in the array design
+(error log, features log). Also alerts the curator when
+significantly fewer features are found in the data file compared to
+the array design (error log). Note that this will give false errors
+on array designs associated with dummy array designs (e.g., some
+Affy arrays).</p>
+</dd>
+<dt><strong><a name="item_duplicate_features_2freporters" id=
+"item_duplicate_features_2freporters"><strong>Duplicate
+Features/Reporters</strong></a></strong></dt>
+<dd>
+<p>Checks that Features or Reporter identifiers are not repeated
+within the same file. Prints out a list of duplicate
+features/reporters (error log, features log).</p>
+</dd>
+<dt><strong><a name="item_duplicate_filenames" id=
+"item_duplicate_filenames"><strong>Duplicate
+filenames</strong></a></strong></dt>
+<dd>
+<p>Checks that there are no duplicate files associated with the
+submission (error log).</p>
+</dd>
+<dt><strong><a name="item_row_2fcolumn_count_consistency" id=
+"item_row_2fcolumn_count_consistency"><strong>Row/column count
+consistency</strong></a></strong></dt>
+<dd>
+<p>Checks that all the files of a given type (raw, normalized) have
+consistent numbers of rows and columns (error log).</p>
+</dd>
+<dt><strong><a name="item_affy_exp_file_consistency" id=
+"item_affy_exp_file_consistency"><strong>Affy EXP file
+consistency</strong></a></strong></dt>
+<dd>
+<p>Checks that the parameters in each EXP file which should be the
+same are the same (error log).</p>
+</dd>
+<dt><strong><a name=
+"item_potentially_duplicated_files_with_different_names" id=
+"item_potentially_duplicated_files_with_different_names"><strong>Potentially
+duplicated files with different names</strong></a></strong></dt>
+<dd>
+<p>Checks a single line from each file against the same line in
+every other file, and reports on any matching pairs of files (error
+log).</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="see_also" id="see_also">SEE ALSO</a></h1>
+<dl>
+<dt><strong><a name="item_arrayexpress_3a_3acurator_3a_3avalidate"
+id="item_arrayexpress_3a_3acurator_3a_3avalidate"></a><a href=
+"./Validate.html">the ArrayExpress::Curator::Validate
+manpage</a></strong></dt>
+<dt><strong><a name="item_arrayexpress_3a_3amagetab_3a_3achecker"
+id="item_arrayexpress_3a_3amagetab_3a_3achecker"></a><a href=
+"../MAGETAB/Checker.html">the ArrayExpress::MAGETAB::Checker
+manpage</a></strong></dt>
+<dt><strong><a name=
+"item_arrayexpress_3a_3acurator_3a_3amiamexpress" id=
+"item_arrayexpress_3a_3acurator_3a_3amiamexpress"></a><a href=
+"./MIAMExpress.html">the ArrayExpress::Curator::MIAMExpress
+manpage</a></strong></dt>
+<dt><strong><a name=
+"item_arrayexpress_3a_3acurator_3a_3astandalone" id=
+"item_arrayexpress_3a_3acurator_3a_3astandalone"></a><a href=
+"./Standalone.html">the ArrayExpress::Curator::Standalone
+manpage</a></strong></dt>
+<dt><strong><a name="item_arrayexpress_3a_3adatafile_3a_3aqt_list"
+id="item_arrayexpress_3a_3adatafile_3a_3aqt_list"></a><a href=
+"../Datafile/QT_list.html">the ArrayExpress::Datafile::QT_list
+manpage</a></strong></dt>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2004.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Curator/Logger.html b/docs/ArrayExpress/Curator/Logger.html
new file mode 100644
index 0000000..1fca5af
--- /dev/null
+++ b/docs/ArrayExpress/Curator/Logger.html
@@ -0,0 +1,204 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>lib/ArrayExpress/Curator/Logger.pm</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#options">OPTIONS</a></li>
+        <li><a href="#public_methods">PUBLIC METHODS</a></li>
+        <li><a href="#available_log_types_and_templates">AVAILABLE LOG TYPES AND TEMPLATES</a></li>
+        <li><a href="#see_also">SEE ALSO</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: ExperimentChecker.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Curator::Logger</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use base qw/ArrayExpress::Curator::Logger/;
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module is subclassed by ExperimentChecker and Tab2MAGE
+classes to provide them both with logging and error recording
+facilities.</p>
+<hr />
+<h1><a name="options" id="options">OPTIONS</a></h1>
+<p>The objects created by this module can be instantiated with the
+following options.</p>
+<dl>
+<dt><strong><a name="item_log_to_current_dir" id=
+"item_log_to_current_dir"><code>log_to_current_dir</code></a></strong></dt>
+<dd>
+<p>A flag indicating whether to write log files to the current
+directory or not. Note that this has been overloaded such that any
+true value is now interpreted as the name of a directory into which
+to write.</p>
+</dd>
+<dt><strong><a name="item_clobber" id=
+"item_clobber"><code>clobber</code></a></strong></dt>
+<dd>
+<p>A flag indicating whether to silently overwrite old logs or
+not.</p>
+</dd>
+<dt><strong><a name="item_progname" id=
+"item_progname"><code>progname</code></a></strong></dt>
+<dd>
+<p>A program name string, used in log headers.</p>
+</dd>
+<dt><strong><a name="item_version" id=
+"item_version"><code>version</code></a></strong></dt>
+<dd>
+<p>A version string, used in log headers.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="public_methods" id="public_methods">PUBLIC
+METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_add_error" id=
+"item_add_error"><code>add_error( $error )</code></a></strong></dt>
+<dd>
+<p>Takes an integer, bitwise ORs it with the object's
+internally-stored error, records and returns the result. Used to
+store the overall error code determining suitability for MAGE-ML
+export. The final value is accessible using get_error().</p>
+</dd>
+<dt><strong><a name="item_logfiles" id=
+"item_logfiles"><code>logfiles( $type )</code></a></strong></dt>
+<dd>
+<p>Given the desired log file type, returns the desired filename
+template. Otherwise, returns a list of log file types.</p>
+</dd>
+<dt><strong><a name="item_log_fh" id="item_log_fh"><code>log_fh(
+$type )</code></a></strong></dt>
+<dd>
+<p>Given the desired log file type, returns the opened
+filehandle.</p>
+</dd>
+<dt><strong><a name="item_logprint" id=
+"item_logprint"><code>logprint( $type, $message
+)</code></a></strong></dt>
+<dd>
+<p>Given the desired log file type and a message, prints the
+message to that log file. Linebreaks are not added to the
+output.</p>
+</dd>
+<dt><strong><a name="item_logprint_line" id=
+"item_logprint_line"><code>logprint_line( $type, $message
+)</code></a></strong></dt>
+<dd>
+<p>Given the desired log file type and a message, prints the
+message as part of a section-delimiting line in the log file. In
+this case linebreaks are added to the output.</p>
+</dd>
+<dt><strong><a name="item_localize_logfiles" id=
+"item_localize_logfiles"><code>localize_logfiles( $args
+)</code></a></strong></dt>
+<dd>
+<p>Given an optional hashref argument (keys: ``volume'',
+``directory'', ``name''), attempts to localize the logfiles to the
+directory structure given. Overridden by the log_to_current_dir
+attribute when setting log file location.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="available_log_types_and_templates" id=
+"available_log_types_and_templates">AVAILABLE LOG TYPES AND
+TEMPLATES</a></h1>
+<dl>
+<dt><strong><a name="item_error__3d_3e__27expt_error_2elog_27_2c"
+id="item_error__3d_3e__27expt_error_2elog_27_2c">error =>
+'expt_error.log',</a></strong></dt>
+<dt><strong><a name="item_report__3d_3e__27expt_report_2elog_27_2c"
+id="item_report__3d_3e__27expt_report_2elog_27_2c">report =>
+'expt_report.log',</a></strong></dt>
+<dt><strong><a name=
+"item_workflow__3d_3e__27expt_biomaterials_2elog_27_2c" id=
+"item_workflow__3d_3e__27expt_biomaterials_2elog_27_2c">workflow
+=> 'expt_biomaterials.log',</a></strong></dt>
+<dt><strong><a name=
+"item_feature__3d_3e__27expt_feature_2elog_27_2c" id=
+"item_feature__3d_3e__27expt_feature_2elog_27_2c">feature =>
+'expt_feature.log',</a></strong></dt>
+<dt><strong><a name=
+"item_columns__3d_3e__27expt_columnheadings_2elog_27_2c" id=
+"item_columns__3d_3e__27expt_columnheadings_2elog_27_2c">columns
+=> 'expt_columnheadings.log',</a></strong></dt>
+<dt><strong><a name=
+"item_sample__3d_3e__27expt_samples_2elog_27_2c" id=
+"item_sample__3d_3e__27expt_samples_2elog_27_2c">sample =>
+'expt_samples.log',</a></strong></dt>
+<dt><strong><a name=
+"item_protocol__3d_3e__27expt_protocols_2elog_27_2c" id=
+"item_protocol__3d_3e__27expt_protocols_2elog_27_2c">protocol =>
+'expt_protocols.log',</a></strong></dt>
+<dt><strong><a name="item_miame__3d_3e__27expt_miame_2elog_27_2c"
+id="item_miame__3d_3e__27expt_miame_2elog_27_2c">miame =>
+'expt_miame.log',</a></strong></dt>
+<dt><strong><a name="item_aedw__3d_3e__27expt_aedw_2elog_27_2c" id=
+"item_aedw__3d_3e__27expt_aedw_2elog_27_2c">aedw =>
+'expt_aedw.log',</a></strong></dt>
+<dt><strong><a name="item_tab2mage__3d_3e__27tab2mage_2elog_27_2c"
+id="item_tab2mage__3d_3e__27tab2mage_2elog_27_2c">tab2mage =>
+'tab2mage.log',</a></strong></dt>
+<dt><strong><a name="item_magetab__3d_3e__27magetab_2elog_27_2c"
+id="item_magetab__3d_3e__27magetab_2elog_27_2c">magetab =>
+'magetab.log',</a></strong></dt>
+</dl>
+<hr />
+<h1><a name="see_also" id="see_also">SEE ALSO</a></h1>
+<dl>
+<dt><strong><a name=
+"item_arrayexpress_3a_3acurator_3a_3aexperimentchecker" id=
+"item_arrayexpress_3a_3acurator_3a_3aexperimentchecker"></a><a href="./ExperimentChecker.html">the
+ArrayExpress::Curator::ExperimentChecker manpage</a></strong></dt>
+<dt><strong><a name="item_arrayexpress_3a_3acurator_3a_3atab2mage"
+id="item_arrayexpress_3a_3acurator_3a_3atab2mage"></a><a href=
+"./Tab2MAGE.html">the ArrayExpress::Curator::Tab2MAGE
+manpage</a></strong></dt>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2008.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Curator/MAGE/Definitions.html b/docs/ArrayExpress/Curator/MAGE/Definitions.html
new file mode 100644
index 0000000..7a07945
--- /dev/null
+++ b/docs/ArrayExpress/Curator/MAGE/Definitions.html
@@ -0,0 +1,712 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Curator::MAGE::Definitions.pm - a module
+providing a central location for managing EDF column names,
+OntologyEntry categories and values.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#supported_headings">SUPPORTED HEADINGS</a></li>
+        <ul>
+
+                <li><a href="#experiment_section__row_names">Experiment section: row names</a></li>
+                <li><a href="#protocol_section__column_names">Protocol section: column names</a></li>
+                <li><a href="#hybridization_section__column_names">Hybridization section: column names</a></li>
+        </ul>
+
+        <li><a href="#validation_subroutines">VALIDATION SUBROUTINES</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: Definitions.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Curator::MAGE::Definitions.pm - a module providing
+a central location for managing EDF column names, OntologyEntry
+categories and values.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Curator::MAGE::Definitions 
+ qw($EDF_EXPTACCESSION 
+    validate_hybridization_section
+    $OE_CAT_PROTOCOLTYPE 
+    $OE_VAL_GROW);
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module provides a central location for various ontology
+terms and column heading definitions. In other words, if you want
+to change the parsing of your EDF or the output OntologyEntries,
+edit this module. Also provided is a series of optional validation
+subroutines for checking column or row names against the expected
+EDF headers.</p>
+<hr />
+<h1><a name="supported_headings" id="supported_headings">SUPPORTED
+HEADINGS</a></h1>
+<h2><a name="experiment_section__row_names" id=
+"experiment_section__row_names">Experiment section: row
+names</a></h2>
+<dl>
+<dt><strong><a name="item_accession" id=
+"item_accession">accession</a></strong></dt>
+<dd>
+<p>A string unique to the experiment. Used in all identifiers, and
+as a top-level Experiment identifier. Synonymous with the
+ArrayExpress accession number for an experiment. Example:
+E-MEXP-100</p>
+</dd>
+<dt><strong><a name="item_domain" id=
+"item_domain">domain</a></strong></dt>
+<dd>
+<p>A string identifying the origin of the information provided.
+Typically this will be an internet domain name such as
+``ebi.ac.uk''. This string is used to define the namespace of the
+MAGE identifiers created.</p>
+</dd>
+<dt><strong><a name="item_name" id=
+"item_name">name</a></strong></dt>
+<dd>
+<p>The name of the experiment. The forms the name attribute of the
+top-level Experiment object.</p>
+</dd>
+<dt><strong><a name="item_description" id=
+"item_description">description</a></strong></dt>
+<dd>
+<p>A short description of the experiment. This is inserted into a
+Description text attribute attached to the top-level Experiment
+object.</p>
+</dd>
+<dt><strong><a name="item_release_date" id=
+"item_release_date">release_date</a></strong></dt>
+<dd>
+<p>In the form
+YYYY-MM-DD<strong>T</strong>hh:mm:ss<strong>Z</strong>. This is
+currently used in a NameValueType (name ArrayExpressReleaseDate) in
+the top-level Experiment object.</p>
+</dd>
+<dt><strong><a name="item_submission_date" id=
+"item_submission_date">submission_date</a></strong></dt>
+<dd>
+<p>In the form
+YYYY-MM-DD<strong>T</strong>hh:mm:ss<strong>Z</strong>. This is
+currently used in a NameValueType (name ArrayExpressSubmissionDate)
+in the top-level Experiment object.</p>
+</dd>
+<dt><strong><a name="item_experiment_design_type" id=
+"item_experiment_design_type">experiment_design_type</a></strong></dt>
+<dd>
+<p>A comma-separated list of MO ExperimentDesignType terms used in
+the top-level ExperimentDesign object.</p>
+</dd>
+<dt><strong><a name="item_quality_control" id=
+"item_quality_control">quality_control</a></strong></dt>
+<dd>
+<p>A comma-separated list of MO QualityControlDescriptionType used
+in the top-level ExperimentDesign object.</p>
+</dd>
+<dt><strong><a name="item_submitter" id=
+"item_submitter">submitter</a></strong></dt>
+<dd>
+<p>The name of the person submitting the experiment. This is used
+to create a Person object in the AuditAndSecurity package with a
+Roles:submitter OntologyEntry. The Person object is referenced in
+the Experiment package.</p>
+</dd>
+<dt><strong><a name="item_submitter_email" id=
+"item_submitter_email">submitter_email</a></strong></dt>
+<dd>
+<p>The email address of the person submitting the experiment.</p>
+</dd>
+<dt><strong><a name="item_data_coder" id=
+"item_data_coder">data_coder</a></strong></dt>
+<dd>
+<p>The name of the person responsible for coding the experiment
+into MAGE-ML. This is used to create a Person object in the
+AuditAndSecurity package with a Roles:data_coder OntologyEntry. The
+Person object is referenced in the Experiment package.</p>
+</dd>
+<dt><strong><a name="item_data_coder_email" id=
+"item_data_coder_email">data_coder_email</a></strong></dt>
+<dd>
+<p>The email address of the person who coded the experiment in
+MAGE-ML.</p>
+</dd>
+<dt><strong><a name="item_curator" id=
+"item_curator">curator</a></strong></dt>
+<dd>
+<p>The name of the person who curated the experiment. This is used
+to create a Person object in the AuditAndSecurity package with a
+Roles:curator OntologyEntry. The Person object is referenced in the
+Experiment package.</p>
+</dd>
+<dt><strong><a name="item_curator_email" id=
+"item_curator_email">curator_email</a></strong></dt>
+<dd>
+<p>The email address of the person who curated the experiment.</p>
+</dd>
+<dt><strong><a name="item_investigator" id=
+"item_investigator">investigator</a></strong></dt>
+<dd>
+<p>The name of the primary investigator on the experiment. This is
+used to create a Person object in the AuditAndSecurity package with
+a Roles:curator OntologyEntry. The Person object is referenced in
+the Experiment package.</p>
+</dd>
+<dt><strong><a name="item_investigator_email" id=
+"item_investigator_email">investigator_email</a></strong></dt>
+<dd>
+<p>The email address of the primary investigator.</p>
+</dd>
+<dt><strong><a name="item_organization" id=
+"item_organization">organization</a></strong></dt>
+<dd>
+<p>The name of the organisation to which the submitter is
+affiliated. This is used to create an Organization MAGE object,
+which is then associated with each Person object.</p>
+</dd>
+<dt><strong><a name="item_address" id=
+"item_address">address</a></strong></dt>
+<dd>
+<p>The address of the organisation to which the submitter is
+affiliated.</p>
+</dd>
+<dt><strong><a name="item_ae_display_name" id=
+"item_ae_display_name">ae_display_name</a></strong></dt>
+<dd>
+<p>The name of the experiment to be displayed in the ArrayExpress
+repository interface. This will typically be added by the
+ArrayExpress curators after data submission.</p>
+</dd>
+<dt><strong><a name="item_uri" id="item_uri">URI</a></strong></dt>
+<dd>
+<p>A URI pointing to an alternative location for the experimental
+data, e.g. in a public repository database.</p>
+</dd>
+<dt><strong><a name="item_geo_release_date" id=
+"item_geo_release_date">geo_release_date</a></strong></dt>
+<dd>
+<p>For processing of GEO experiments; this value is used to create
+a ``GEOReleaseDate'' NameValueType object associated with the
+top-level Experiment; this is used internally by the ArrayExpress
+database.</p>
+</dd>
+<dt><strong><a name="item_secondary_accession" id=
+"item_secondary_accession">secondary_accession</a></strong></dt>
+<dd>
+<p>This value is used to populate the ``SecondaryAcession''
+NameValueType object associated with the top-level Experiment. This
+is used internally by ArrayExpress to represent e.g. GEO accession
+numbers.</p>
+</dd>
+</dl>
+<p>The following are used in a BibliographicReference associated
+with a second Description object in the top-level Experiment
+object:</p>
+<dl>
+<dt><strong><a name="item_publication_title" id=
+"item_publication_title">publication_title</a></strong></dt>
+<dd>
+<p>The free-text title of any associated publication. This is
+currently assumed to have PublicationType journal_article (MO),
+although more control over this may be added in future. Used in</p>
+</dd>
+<dt><strong><a name="item_authors" id=
+"item_authors">authors</a></strong></dt>
+<dd>
+<p>A free-text list of publication authors.</p>
+</dd>
+<dt><strong><a name="item_journal" id=
+"item_journal">journal</a></strong></dt>
+<dd>
+<p>The name of the journal. This should be a standard Pubmed
+abbreviation.</p>
+</dd>
+<dt><strong><a name="item_year" id=
+"item_year">year</a></strong></dt>
+<dd>
+<p>Year of publication.</p>
+</dd>
+<dt><strong><a name="item_volume" id=
+"item_volume">volume</a></strong></dt>
+<dd>
+<p>Journal volume.</p>
+</dd>
+<dt><strong><a name="item_issue" id=
+"item_issue">issue</a></strong></dt>
+<dd>
+<p>Journal issue.</p>
+</dd>
+<dt><strong><a name="item_pages" id=
+"item_pages">pages</a></strong></dt>
+<dd>
+<p>Page range of journal article.</p>
+</dd>
+<dt><strong><a name="item_publication_uri" id=
+"item_publication_uri">publication_URI</a></strong></dt>
+<dd>
+<p>Any URI associated with the publication.</p>
+</dd>
+<dt><strong><a name="item_pubmed_id" id=
+"item_pubmed_id">pubmed_id</a></strong></dt>
+<dd>
+<p>The Pubmed ID associated with the publication.</p>
+</dd>
+</dl>
+<h2><a name="protocol_section__column_names" id=
+"protocol_section__column_names">Protocol section: column
+names</a></h2>
+<p>The following are all attached to individual protocols defined
+by successive lines in this section:</p>
+<dl>
+<dt><strong>accession</strong></dt>
+<dd>
+<p>Database (e.g. ArrayExpress) accession no.</p>
+</dd>
+<dt><strong>name</strong></dt>
+<dd>
+<p>Protocol name.</p>
+</dd>
+<dt><strong><a name="item_type" id=
+"item_type">type</a></strong></dt>
+<dd>
+<p>MO ProtocolType term; this is an optional field. In its absence,
+Tab2MAGE will use default ProtocolType terms based on how the
+protocol is used within the Hybridization section.</p>
+</dd>
+<dt><strong><a name="item_text" id=
+"item_text">text</a></strong></dt>
+<dd>
+<p>Protocol text.</p>
+</dd>
+<dt><strong><a name="item_parameters" id=
+"item_parameters">parameters</a></strong></dt>
+<dd>
+<p>Protocol parameters, listed in the following form:
+name1(unit1);name2(unit2);...</p>
+</dd>
+</dl>
+<h2><a name="hybridization_section__column_names" id=
+"hybridization_section__column_names">Hybridization section: column
+names</a></h2>
+<dl>
+<dt><strong><a name="item_biosource" id=
+"item_biosource">BioSource</a></strong></dt>
+<dd>
+<p>Arbitrary name for a BioSource. This term is used as a unique
+identifier within a Tab2MAGE run to determine correct linking
+between objects. It may however be omitted, in favour of using the
+set of BioMaterialCharacteristics associated with a BioMaterial as
+the sole indicator of BioSource identity.</p>
+</dd>
+<dt><strong><a name="item_sample" id=
+"item_sample">Sample</a></strong></dt>
+<dd>
+<p>Arbitrary name for a BioSample (associated with a
+BioSampleType:not_extract OntologyEntry). Used to control linking
+between objects; may be omitted if desired, in which case a
+BioSample name constructed from the raw data filename is used
+instead.</p>
+</dd>
+<dt><strong><a name="item_extract" id=
+"item_extract">Extract</a></strong></dt>
+<dd>
+<p>Arbitrary name for a BioSample (associated with a
+BioSampleType:extract OntologyEntry). Used to control linking
+between objects; may be omitted if desired, in which case a Extract
+name constructed from the raw data filename is used instead.</p>
+</dd>
+<dt><strong><a name="item_immunoprecipitate" id=
+"item_immunoprecipitate">Immunoprecipitate</a></strong></dt>
+<dd>
+<p>Arbitrary name for a BioSample (associated with a
+BioSampleType:extract OntologyEntry). Used to control linking
+between objects; may be omitted if desired, in which case an
+Immunoprecipitate name constructed from the raw data filename is
+used instead. These objects are used in ChIP experiments and may be
+ignored for expression or CGH studies.</p>
+</dd>
+<dt><strong><a name="item_labeledextract" id=
+"item_labeledextract">LabeledExtract</a></strong></dt>
+<dd>
+<p>Arbitrary name for a LabeledExtract. Used to control linking
+between objects; may be omitted if desired, in which case a
+LabeledExtract name constructed from the raw data filename and the
+label dye name is used instead.</p>
+</dd>
+<dt><strong><a name="item_dye" id="item_dye">Dye</a></strong></dt>
+<dd>
+<p>Name of the dye linked to the labeled extract (e.g., Cy3, Cy5,
+biotin).</p>
+</dd>
+<dt><strong><a name="item_biosourcematerial" id=
+"item_biosourcematerial">BioSourceMaterial</a></strong></dt>
+<dd>
+<p>MO MaterialType term to be attached to the BioSource. Default:
+whole_organism</p>
+</dd>
+<dt><strong><a name="item_samplematerial" id=
+"item_samplematerial">SampleMaterial</a></strong></dt>
+<dd>
+<p>MO MaterialType term to be attached to the BioSample. Default:
+organism_part</p>
+</dd>
+<dt><strong><a name="item_extractmaterial" id=
+"item_extractmaterial">ExtractMaterial</a></strong></dt>
+<dd>
+<p>MO MaterialType term to be attached to the Extract. Default:
+total_RNA</p>
+</dd>
+<dt><strong><a name="item_immunoprecipitatematerial" id=
+"item_immunoprecipitatematerial">ImmunoprecipitateMaterial</a></strong></dt>
+<dd>
+<p>MO MaterialType term to be attached to the Immunoprecipitate.
+Default: genomic_DNA</p>
+</dd>
+<dt><strong><a name="item_labeledextractmaterial" id=
+"item_labeledextractmaterial">LabeledExtractMaterial</a></strong></dt>
+<dd>
+<p>MO MaterialType term to be attached to the LabeledExtract.
+Default: synthetic_DNA</p>
+</dd>
+<dt><strong><a name="item_biosourcedescription" id=
+"item_biosourcedescription">BioSourceDescription</a></strong></dt>
+<dd>
+<p>Free-text description to attached to the BioSource (this should
+be used sparingly, if at all).</p>
+</dd>
+<dt><strong><a name="item_hybridization" id=
+"item_hybridization">Hybridization</a></strong></dt>
+<dd>
+<p>Arbitrary name for a hybridization. Used to control linking
+between objects; may be omitted if desired, in which case a
+Hybridization name constructed from the raw data filename is used
+instead.</p>
+</dd>
+<dt><strong><a name="item_scan" id=
+"item_scan">Scan</a></strong></dt>
+<dd>
+<p>Arbitrary name for a scanning event. Used to control linking
+between objects; may be omitted if desired, in which case a Scan
+name constructed from the raw data filename is used instead.</p>
+</dd>
+<dt><strong><a name="item_normalization" id=
+"item_normalization">Normalization</a></strong></dt>
+<dd>
+<p>Arbitrary name for a normalization procedure. Used to control
+linking between objects; may be omitted if desired, in which case a
+Normalization name constructed from the normalized data filename is
+used instead.</p>
+</dd>
+<dt><strong><a name="item_normalizationtype" id=
+"item_normalizationtype">NormalizationType</a></strong></dt>
+<dd>
+<p>MO DerivedBioAssayType term, attached to the relevant
+DerivedBioAssayData object.</p>
+</dd>
+<dt><strong><a name="item_transformation" id=
+"item_transformation">Transformation</a></strong></dt>
+<dd>
+<p>Arbitrary name for a data transformation procedure. Used to
+control linking between objects; may be omitted if desired.</p>
+</dd>
+<dt><strong><a name="item_transformationtype" id=
+"item_transformationtype">TransformationType</a></strong></dt>
+<dd>
+<p>MO DerivedBioAssayType term, attached to the relevant
+DerivedBioAssayData object.</p>
+</dd>
+<dt><strong><a name="item_imageformat" id=
+"item_imageformat">ImageFormat</a></strong></dt>
+<dd>
+<p>MO ImageFormat term. Only used if File[image] columns have been
+included in the spreadsheet. Used to create Image objects.</p>
+</dd>
+</dl>
+<dl>
+<dt><strong><a name="item_protocol_5bgrow_5d" id=
+"item_protocol_5bgrow_5d">Protocol[grow]</a></strong></dt>
+<dd>
+<p>Accession number for the ``growth'' protocol
+(BioSource->BioSample Treatment). The accession number should
+either be present in the protocol section of the spreadsheet, or
+pre-existing in ArrayExpress. Default ProtocolType: grow</p>
+</dd>
+<dt><strong><a name="item_protocol_5btreatment_5d" id=
+"item_protocol_5btreatment_5d">Protocol[treatment]</a></strong></dt>
+<dd>
+<p>Accession number for the ``treatment'' protocol
+(BioSource->BioSample Treatment). The accession number should
+either be present in the protocol section of the spreadsheet, or
+pre-existing in ArrayExpress. Default ProtocolType:
+specified_biomaterial_action</p>
+</dd>
+<dt><strong><a name="item_protocol_5bextraction_5d" id=
+"item_protocol_5bextraction_5d">Protocol[extraction]</a></strong></dt>
+<dd>
+<p>Accession number for the ``extraction'' protocol
+(BioSample->Extract Treatment). The accession number should
+either be present in the protocol section of the spreadsheet, or
+pre-existing in ArrayExpress. Default ProtocolType:
+nucleic_acid_extraction</p>
+</dd>
+<dt><strong><a name="item_protocol_5bpool_5d" id=
+"item_protocol_5bpool_5d">Protocol[pool]</a></strong></dt>
+<dd>
+<p>Accession number for the ``pooling'' protocol
+(BioSample->Extract Treatment). The accession number should
+either be present in the protocol section of the spreadsheet, or
+pre-existing in ArrayExpress. Default ProtocolType: pool</p>
+</dd>
+<dt><strong><a name="item_protocol_5blabeling_5d" id=
+"item_protocol_5blabeling_5d">Protocol[labeling]</a></strong></dt>
+<dd>
+<p>Accession number for the ``labeling'' protocol
+(Extract->LabeledExtract Treatment). The accession number should
+either be present in the protocol section of the spreadsheet, or
+pre-existing in ArrayExpress. Default ProtocolType: labeling</p>
+</dd>
+<dt><strong><a name="item_protocol_5bimmunoprecipitate_5d" id=
+"item_protocol_5bimmunoprecipitate_5d">Protocol[immunoprecipitate]</a></strong></dt>
+<dd>
+<p>Accession number for the ``immunoprecipitation'' protocol
+(Extract->Immunoprecipitate Treatment). The accession number
+should either be present in the protocol section of the
+spreadsheet, or pre-existing in ArrayExpress. Omit if
+Immunoprecipitates are not used in the experiment. Default
+ProtocolType: immunoprecipitate</p>
+</dd>
+<dt><strong><a name="item_protocol_5bhybridization_5d" id=
+"item_protocol_5bhybridization_5d">Protocol[hybridization]</a></strong></dt>
+<dd>
+<p>Accession number for the ``hybridization'' protocol
+(PhysicalBioAssay BioAssayCreation). The accession number should
+either be present in the protocol section of the spreadsheet, or
+pre-existing in ArrayExpress. Default ProtocolType:
+hybridization</p>
+</dd>
+<dt><strong><a name="item_protocol_5bscanning_5d" id=
+"item_protocol_5bscanning_5d">Protocol[scanning]</a></strong></dt>
+<dd>
+<p>Accession number for the ``image acquisition'' protocol
+(PhysicalBioAssay BioAssayTreatment). The accession number should
+either be present in the protocol section of the spreadsheet, or
+pre-existing in ArrayExpress. Default ProtocolType:
+image_acquisition; note however that if Protocol[image_analysis] is
+not specified then the scanning protocol defaults to
+feature_extraction. This is an ArrayExpress-specific behaviour and
+relates to the appearance of the experiment in the ArrayExpress web
+interface.</p>
+</dd>
+<dt><strong><a name="item_protocol_5bimage_analysis_5d" id=
+"item_protocol_5bimage_analysis_5d">Protocol[image_analysis]</a></strong></dt>
+<dd>
+<p>Accession number for the ``feature extraction'' protocol
+(MeasuredBioAssay FeatureExtraction). The accession number should
+either be present in the protocol section of the spreadsheet, or
+pre-existing in ArrayExpress. Default ProtocolType:
+feature_extraction</p>
+</dd>
+<dt><strong><a name="item_protocol_5bnormalization_5d" id=
+"item_protocol_5bnormalization_5d">Protocol[normalization]</a></strong></dt>
+<dd>
+<p>Accession number for the ``normalization'' protocol
+(DerivedBioAssayData ProducerTransformation). The accession number
+should either be present in the protocol section of the
+spreadsheet, or pre-existing in ArrayExpress. Default ProtocolType:
+bioassay_data_transformation</p>
+</dd>
+<dt><strong><a name="item_protocol_5btransformation_5d" id=
+"item_protocol_5btransformation_5d">Protocol[transformation]</a></strong></dt>
+<dd>
+<p>Accession number for the ``transformation'' protocol
+(DerivedBioAssayData ProducerTransformation). The accession number
+should either be present in the protocol section of the
+spreadsheet, or pre-existing in ArrayExpress. Default ProtocolType:
+bioassay_data_transformation</p>
+</dd>
+</dl>
+<p>Software is created in the Protocol package, and referenced as
+SoftwareApplication in the appropriate ProtocolApplication (see
+above).</p>
+<dl>
+<dt><strong><a name="item_software_5bscanning_5d" id=
+"item_software_5bscanning_5d">Software[scanning]</a></strong></dt>
+<dd>
+<p>Name of the scanning software, followed by its version in
+parentheses. The Software name is used to create a Software object
+in the Protocol package, and the version is inserted into the
+relevant SoftwareApplication objects.</p>
+</dd>
+<dt><strong><a name="item_software_5bimage_analysis_5d" id=
+"item_software_5bimage_analysis_5d">Software[image_analysis]</a></strong></dt>
+<dd>
+<p>Name of the feature extraction software, followed by its version
+in parentheses. The Software name is used to create a Software
+object in the Protocol package, and the version is inserted into
+the relevant SoftwareApplication objects.</p>
+</dd>
+<dt><strong><a name="item_software_5bnormalization_5d" id=
+"item_software_5bnormalization_5d">Software[normalization]</a></strong></dt>
+<dd>
+<p>Name of the normalization software, followed by its version in
+parentheses. The Software name is used to create a Software object
+in the Protocol package, and the version is inserted into the
+relevant SoftwareApplication objects.</p>
+</dd>
+<dt><strong><a name="item_software_5btransformation_5d" id=
+"item_software_5btransformation_5d">Software[transformation]</a></strong></dt>
+<dd>
+<p>Name of the data transformation software, followed by its
+version in parentheses. The Software name is used to create a
+Software object in the Protocol package, and the version is
+inserted into the relevant SoftwareApplication objects.</p>
+</dd>
+</dl>
+<dl>
+<dt><strong><a name="item_array_5baccession_5d" id=
+"item_array_5baccession_5d">Array[accession]</a></strong></dt>
+<dd>
+<p>ArrayExpress array accession number (e.g., A-MEXP-1). This is
+used in the Array package, and also to create Feature and Reporter
+identifiers for the DesignElementDimensions.</p>
+</dd>
+<dt><strong><a name="item_array_5bserial_5d" id=
+"item_array_5bserial_5d">Array[serial]</a></strong></dt>
+<dd>
+<p>Serial or lot number of the array. This is used to define
+ArrayManufacture objects within the Array package.</p>
+</dd>
+</dl>
+<dl>
+<dt><strong><a name="item_file_5braw_5d" id=
+"item_file_5braw_5d">File[raw]</a></strong></dt>
+<dd>
+<p>Name of the raw data file for a given hybridization.</p>
+</dd>
+<dt><strong><a name="item_file_5bnormalized_5d" id=
+"item_file_5bnormalized_5d">File[normalized]</a></strong></dt>
+<dd>
+<p>Name of the normalized data file for a given
+hybridization/normalization.</p>
+</dd>
+<dt><strong><a name="item_file_5btransformed_5d" id=
+"item_file_5btransformed_5d">File[transformed]</a></strong></dt>
+<dd>
+<p>Name of the transformed final data matrix file for the
+experiment.</p>
+</dd>
+<dt><strong><a name="item_file_5bimage_5d" id=
+"item_file_5bimage_5d">File[image]</a></strong></dt>
+<dd>
+<p>URI locating the Image object associated with an
+ImageAcquisition (scanning) event. ArrayExpress does not store
+images, although we can link to images stored on external web
+sites, where desired.</p>
+</dd>
+<dt><strong><a name="item_file_5bcdf_5d" id=
+"item_file_5bcdf_5d">File[cdf]</a></strong></dt>
+<dd>
+<p>Affymetrix array library file pertaining to a given
+hybridization. Required for correct parsing of Affymetrix data
+files, although for standard Affymetrix arrays the actual CDF file
+does not need to be supplied.</p>
+</dd>
+<dt><strong><a name="item_file_5bexp_5d" id=
+"item_file_5bexp_5d">File[exp]</a></strong></dt>
+<dd>
+<p>Affymetrix experiment description file pertaining to a given
+hybridization. Required for correct parsing of Affymetrix data
+files.</p>
+</dd>
+<dt><strong><a name=
+"item_biomaterialcharacteristics_5b_3ccategory_3e_5d" id=
+"item_biomaterialcharacteristics_5b_3ccategory_3e_5d">BioMaterialCharacteristics[<<em>category</em>>]</a></strong></dt>
+<dd>
+<p>MO term describing some feature of the BioSource (e.g.,
+Genotype, Sex, DiseaseState, etc.). As many
+BioMaterialCharacteristics columns may be used as are desired. Each
+column should contain values from a different
+<strong>category</strong> within the MGED Ontology.</p>
+</dd>
+<dt><strong><a name="item_factorvalue_5b_3ccategory_3e_5d" id=
+"item_factorvalue_5b_3ccategory_3e_5d">FactorValue[<<em>category</em>>]</a></strong></dt>
+<dd>
+<p>MO term describing a FactorValue associated with a given
+hybridization (e.g., Genotype, Sex, DiseaseState, etc.). As many
+FactorValue columns may be used as are desired. Each column should
+contain values from a different <strong>category</strong> within
+the MGED Ontology.</p>
+</dd>
+<dt><strong><a name="item_parameter_5b_3cparameter_name_3e_5d" id=
+"item_parameter_5b_3cparameter_name_3e_5d">Parameter[<<em>parameter
+name</em>>]</a></strong></dt>
+<dd>
+<p>Parameter values for the parameters declared in the Protocol
+section of the spreadsheet. Note that a protocol must be declared
+in the spreadsheet in order to be able to use parameters with
+it.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="validation_subroutines" id=
+"validation_subroutines">VALIDATION SUBROUTINES</a></h1>
+<dl>
+<dt><strong><a name="item_validate_expriment_section" id=
+"item_validate_expriment_section">validate_expriment_section($test_arrayref,
+$error_fh)</a></strong></dt>
+<dt><strong><a name="item_validate_protocol_section" id=
+"item_validate_protocol_section">validate_protocol_section($test_arrayref,
+$error_fh)</a></strong></dt>
+<dt><strong><a name="item_validate_hybridization_section" id=
+"item_validate_hybridization_section">validate_hybridization_section($test_arrayref,
+$error_fh)</a></strong></dt>
+<dd>
+<p>Each of these subroutines simply takes a reference to an array
+containing the list of headings to be checked, and prints warnings
+on STDERR (and on the optional error filehandle) if it finds
+unrecognized headings. No further action is taken; these
+subroutines merely serve as a warning to the user.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2004.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Curator/MIAMExpress.html b/docs/ArrayExpress/Curator/MIAMExpress.html
new file mode 100644
index 0000000..06e5191
--- /dev/null
+++ b/docs/ArrayExpress/Curator/MIAMExpress.html
@@ -0,0 +1,193 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Curator::MIAMExpress - a module used by
+expt_check.pl</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#options">OPTIONS</a></li>
+        <li><a href="#tests">TESTS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: MIAMExpress.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Curator::MIAMExpress - a module used by
+expt_check.pl</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Curator::MIAMExpress;
+ 
+ my $checker = ArrayExpress::Curator::MIAMExpress->new({
+     mx_login => $login,
+     mx_title => $title,
+ });
+ 
+ $checker->check();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module provides a set of subroutines used by the experiment
+checker script to interact with a local MIAMExpress installation.
+It optionally exports just two (fairly heavyweight) subroutines.
+Please see the end of this document for the tests carried out by
+this module.</p>
+<hr />
+<h1><a name="options" id="options">OPTIONS</a></h1>
+<p>The following options must be used in addition to those provided
+by the parent class (see <a href="./ExperimentChecker.html">the
+ArrayExpress::Curator::ExperimentChecker manpage</a>):</p>
+<dl>
+<dt><strong><a name="item_mx_login" id=
+"item_mx_login"><code>mx_login</code></a></strong></dt>
+<dd>
+<p>The submitter login name associated with a MIAMExpress
+submission. See <a href=
+"#item_mx_title"><code>mx_title</code></a>.</p>
+</dd>
+<dt><strong><a name="item_mx_title" id=
+"item_mx_title"><code>mx_title</code></a></strong></dt>
+<dd>
+<p>The title of a MIAMExpress submission. See <a href=
+"#item_mx_login"><code>mx_login</code></a>.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="tests" id="tests">TESTS</a></h1>
+<p>The following tests are performed by this module, with output
+printed to the error and/or report filehandles (see <a href=
+"./ExperimentChecker.html">the
+ArrayExpress::Curator::ExperimentChecker manpage</a> for a list of
+more general tests which are also run):</p>
+<dl>
+<dt><strong><a name="item_biomaterials" id=
+"item_biomaterials"><strong>BioMaterials</strong></a></strong></dt>
+<dd>
+<p>Checks the linking between sample, extract, labeled extract and
+hybridization, and writes a biomaterials log file which attempts to
+portray these links in ASCII. Highlights BioMaterials which have
+not been properly linked through to hybridizations (biomaterials
+log).</p>
+</dd>
+<dt><strong><a name="item_experimental_factor_values" id=
+"item_experimental_factor_values"><strong>Experimental factor
+values</strong></a></strong></dt>
+<dd>
+<p>Generates a report showing how hybridizations have been linked
+to experimental factor values, and vice versa (biomaterials
+log).</p>
+</dd>
+<dt><strong><a name="item_sample_annotation" id=
+"item_sample_annotation"><strong>Sample
+annotation</strong></a></strong></dt>
+<dd>
+<p>Generates a report on all the annotation attached to the samples
+in the experiment (sample log).</p>
+</dd>
+<dt><strong><a name="item_publication" id=
+"item_publication"><strong>Publication</strong></a></strong></dt>
+<dd>
+<p>Checks whether the publication details include standard Pubmed
+abbreviations; also alerts the curator if a new publication
+(``other'') has been indicated (error log).</p>
+</dd>
+<dt><strong><a name="item_qualifiervaluesource" id=
+"item_qualifiervaluesource"><strong>QualifierValueSource</strong></a></strong></dt>
+<dd>
+<p>Alerts the curator if QVS entries have been used under
+Experiment or Sample annotation (error log).</p>
+</dd>
+<dt><strong><a name="item_other" id=
+"item_other"><strong>Other</strong></a></strong></dt>
+<dd>
+<p>Alerts the curator if ``Other'' entries have been used under
+Experiment, Protocol or Sample annotation (error log).</p>
+</dd>
+<dt><strong><a name="item_transformation_protocol" id=
+"item_transformation_protocol"><strong>Transformation
+protocol</strong></a></strong></dt>
+<dd>
+<p>Alerts the curator if a final data matrix file has been supplied
+without a transformation protocol (error log).</p>
+</dd>
+<dt><strong><a name="item_protocol_description_length" id=
+"item_protocol_description_length"><strong>Protocol description
+length</strong></a></strong></dt>
+<dd>
+<p>Alerts the curator if protocols with fewer than 50 characters
+are submitted (error log).</p>
+</dd>
+<dt><strong><a name="item_pooling_protocol" id=
+"item_pooling_protocol"><strong>Pooling
+protocol</strong></a></strong></dt>
+<dd>
+<p>Alerts the curator if a pooling protocol has been linked to a
+submission (error log).</p>
+</dd>
+<dt><strong><a name="item_normalization_protocol" id=
+"item_normalization_protocol"><strong>Normalization
+protocol</strong></a></strong></dt>
+<dd>
+<p>Alerts the curator if normalized data files have been supplied
+without a normalization protocol (error log).</p>
+</dd>
+<dt><strong><a name="item_other_unbound_protocols" id=
+"item_other_unbound_protocols"><strong>Other unbound
+protocols</strong></a></strong></dt>
+<dd>
+<p>Alerts the curator if protocols other than pooling,
+normalization or transformation have not been bound to their
+appropriate objects (error log).</p>
+</dd>
+<dt><strong><a name=
+"item_number_of_files_submitted_per_hybridization" id=
+"item_number_of_files_submitted_per_hybridization"><strong>Number
+of files submitted per hybridization</strong></a></strong></dt>
+<dd>
+<p>Prints a table indicating which kinds of raw and normalized data
+file have been submitted for each hybridization (report log).</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Curator/Report.html b/docs/ArrayExpress/Curator/Report.html
new file mode 100644
index 0000000..ce2a54c
--- /dev/null
+++ b/docs/ArrayExpress/Curator/Report.html
@@ -0,0 +1,105 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Curator::Reporter.pm - a module providing some
+functions useful in generating reports.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#functions">FUNCTIONS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../index.html"><img src="../T2M_logo.png" border="0" height=
+"50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: Reporter.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Curator::Reporter.pm - a module providing some
+functions useful in generating reports.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Curator::Report qw(datafile_consistency_table);
+ 
+ print datafile_consistency_table( \@filelist );
+ 
+=head1 DESCRIPTION
+</pre>
+<p>This is a simple module providing utility functions and regexp
+patterns used elsewhere in the Tab2MAGE package.</p>
+<hr />
+<h1><a name="functions" id="functions">FUNCTIONS</a></h1>
+<dl>
+<dt><strong><a name="item_datafile_consistency_table" id=
+"item_datafile_consistency_table"><code>datafile_consistency_table(
+\@filelist )</code></a></strong></dt>
+<dd>
+<p>Given an arrayref of Datafile objects (see <a href=
+"../Datafile.html">the ArrayExpress::Datafile manpage</a>), returns
+a string representing a summary of files tabulated by
+hyb_identifier, reporting on presence or absence in the
+filesystem.</p>
+</dd>
+<dt><strong><a name="item_biomaterials_report" id=
+"item_biomaterials_report"><code>biomaterials_report( $HoH, $title,
+$fh )</code></a></strong></dt>
+<dd>
+<p>Given a nested hashref of hashrefs (of indeterminate depth), a
+report title and an output filehandle, prints a report giving links
+between nodes in the hashref structure (recursively).</p>
+</dd>
+<dt><strong><a name="item_report_accumulated_errors" id=
+"item_report_accumulated_errors"><code>report_accumulated_errors(
+$HoH, $fh, $string )</code></a></strong></dt>
+<dd>
+<p>Given a hashref of hashrefs keyed by filename and then by
+elements (e.g. duplicate feature identifiers), an output filehandle
+and an explanatory string, print a report.</p>
+</dd>
+<dt><strong><a name="item_format_description" id=
+"item_format_description"><code>format_description( $text, $fh
+)</code></a></strong></dt>
+<dd>
+<p>Takes a text string and an output filehandle, and prints the
+string in lines of less than 80 chars, splitting words on
+whitespace.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2008.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Curator/Standalone.html b/docs/ArrayExpress/Curator/Standalone.html
new file mode 100644
index 0000000..26f4d97
--- /dev/null
+++ b/docs/ArrayExpress/Curator/Standalone.html
@@ -0,0 +1,87 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Curator::Standalone - Standalone experiment
+checking.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#options">OPTIONS</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: Standalone.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Curator::Standalone - Standalone experiment
+checking.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Curator::Standalone;
+ 
+ my $checker =
+   ArrayExpress::Curator::Standalone->new({
+       data_files => \@ARGV,
+   });
+ 
+ $checker->check();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module provides the basic operations needed for checking a
+set of data files in full standalone mode. For MIAMExpress and
+Tab2MAGE experiment checking, see <a href="./MIAMExpress.html">the
+ArrayExpress::Curator::MIAMExpress manpage</a> and <a href=
+"./Validate.html">the ArrayExpress::Curator::Validate manpage</a>,
+respectively.</p>
+<hr />
+<h1><a name="options" id="options">OPTIONS</a></h1>
+<p>The following options must be used in addition to those provided
+by the parent class (see <a href="./ExperimentChecker.html">the
+ArrayExpress::Curator::ExperimentChecker manpage</a>):</p>
+<dl>
+<dt><strong><a name="item_data_files" id=
+"item_data_files"><code>data_files</code></a></strong></dt>
+<dd>
+<p>An array reference containing a list of data filenames to be
+checked.</p>
+</dd>
+</dl>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2007.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Curator/Validate.html b/docs/ArrayExpress/Curator/Validate.html
new file mode 100644
index 0000000..718e9ec
--- /dev/null
+++ b/docs/ArrayExpress/Curator/Validate.html
@@ -0,0 +1,184 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Curator::Validate - a module used by
+expt_check.pl</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#options">OPTIONS</a></li>
+        <li><a href="#tests">TESTS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: Validate.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Curator::Validate - a module used by
+expt_check.pl</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Curator::Validate;
+ 
+ my $checker = ArrayExpress::Curator::Validate->new({
+     spreadsheet_filename => $tab2mage_file,
+ });
+ 
+ $checker->check();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module provides a set of subroutines used by the experiment
+checker script to confirm the MIAME metadata supplied in a Tab2MAGE
+spreadsheet.</p>
+<hr />
+<h1><a name="options" id="options">OPTIONS</a></h1>
+<p>The following options must be used in addition to those provided
+by the parent class (see <a href="./ExperimentChecker.html">the
+ArrayExpress::Curator::ExperimentChecker manpage</a>):</p>
+<dl>
+<dt><strong><a name="item_spreadsheet_filename" id=
+"item_spreadsheet_filename"><code>spreadsheet_filename</code></a></strong></dt>
+<dd>
+<p>The Tab2MAGE spreadsheet to check.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="tests" id="tests">TESTS</a></h1>
+<p>The following tests are performed by this module, with output
+printed to the error and/or report filehandles:</p>
+<dl>
+<dt><strong><a name="item_hybridizations" id=
+"item_hybridizations"><strong>Hybridizations</strong></a></strong></dt>
+<dd>
+<p>Confirms that each hybridization is associated with an array
+design (i.e., ArrayExpress accession number), and at least one
+experimental factor value.</p>
+</dd>
+<dt><strong><a name="item_biomaterialcharacteristics" id=
+"item_biomaterialcharacteristics"><strong>BioMaterialCharacteristics</strong></a></strong></dt>
+<dd>
+<p>Checks that each BioSource has at least some annotation. This is
+not, however, a full test of MIAME compliance.</p>
+</dd>
+<dt><strong><a name="item_undeclared_experimental_factors" id=
+"item_undeclared_experimental_factors"><strong>Undeclared
+Experimental Factors</strong></a></strong></dt>
+<dd>
+<p>Checks all material characteristics against the factors
+described by the document, alerting the user if any such
+characteristics vary during the experiment without having been
+declared as an experimental factor.</p>
+</dd>
+<dt><strong><a name="item_protocols" id=
+"item_protocols"><strong>Protocols</strong></a></strong></dt>
+<dd>
+<p>Checks that all protocols referenced in the Hybridization
+spreadsheet section are declared in the Protocol section (and vice
+versa).</p>
+</dd>
+<dt><strong><a name="item_protocol_accessions" id=
+"item_protocol_accessions"><strong>Protocol
+accessions</strong></a></strong></dt>
+<dd>
+<p>Confirms that all declared protocols in the Protocol section
+have accession numbers.</p>
+</dd>
+<dt><strong><a name="item_protocol_text_length" id=
+"item_protocol_text_length"><strong>Protocol text
+length</strong></a></strong></dt>
+<dd>
+<p>Warns if any of the protocol texts seem too brief.</p>
+</dd>
+<dt><strong><a name="item_protocol_presence_and_usage" id=
+"item_protocol_presence_and_usage"><strong>Protocol presence and
+usage</strong></a></strong></dt>
+<dd>
+<p>Checks that a protocol has been attached to each step of the
+experiment (biomaterial treatments, hybridization, scanning and
+normalization).</p>
+</dd>
+<dt><strong><a name="item_parameters" id=
+"item_parameters"><strong>Parameters</strong></a></strong></dt>
+<dd>
+<p>Checks that all parameters referenced in the Hybridization
+spreadsheet section are declared in the Protocol section (and vice
+versa).</p>
+</dd>
+<dt><strong><a name="item_biomaterials" id=
+"item_biomaterials"><strong>BioMaterials</strong></a></strong></dt>
+<dd>
+<p>Creates the links between sample, extract, labeled extract and
+hybridization, and writes a biomaterials log file listing the
+numbers of each. If the Graphviz software is installed
+(http://www.graphviz.org) then the script will use the 'dot'
+program to produce a PNG format graph showing how the various
+components relate to each other.</p>
+</dd>
+<dt><strong><a name="item_publication_journal" id=
+"item_publication_journal"><strong>Publication
+journal</strong></a></strong></dt>
+<dd>
+<p>Warns the user if the specified publication journal name is not
+in the included list of standard Entrez journal abbreviations.</p>
+</dd>
+<dt><strong><a name="item_pubmed_id" id=
+"item_pubmed_id"><strong>PubMed ID</strong></a></strong></dt>
+<dd>
+<p>Warns the user if a non-numeric PubMed ID has been entered.</p>
+</dd>
+<dt><strong><a name="item_submission_and_release_dates" id=
+"item_submission_and_release_dates"><strong>Submission and release
+dates</strong></a></strong></dt>
+<dd>
+<p>Warns the user if submission and/or release dates have not been
+specified.</p>
+</dd>
+<dt><strong><a name="item_submitter" id=
+"item_submitter"><strong>Submitter</strong></a></strong></dt>
+<dd>
+<p>Checks that both submitter name and email have been
+provided.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Curator/Visualize.html b/docs/ArrayExpress/Curator/Visualize.html
new file mode 100644
index 0000000..b496f37
--- /dev/null
+++ b/docs/ArrayExpress/Curator/Visualize.html
@@ -0,0 +1,86 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Curator::Visualize - a module used by
+expt_check.pl</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#functions">FUNCTIONS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: Visualize.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Curator::Visualize - a module used by
+expt_check.pl</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Curator::Visualize qw(dot_and_png);
+ 
+ dot_and_png( $mage_bags, 'my_expt', 'luxisr', 1 );
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module provides a set of subroutines used by the experiment
+checker script to confirm the MIAME metadata supplied in a Tab2MAGE
+spreadsheet.</p>
+<hr />
+<h1><a name="functions" id="functions">FUNCTIONS</a></h1>
+<dl>
+<dt><strong><a name="item_dot_and_png" id=
+"item_dot_and_png"><code>dot_and_png( $mage_bags, $output_base,
+$font, $clobber, $classes )</code></a></strong></dt>
+<dd>
+<p>Takes: a hashref of MAGE containers, as generated by Tab2MAGE,
+MAGE and MAGE-TAB classes; a string for the output filenames (minus
+extension, so e.g. ``out'' becomes ``out.dot'' and ``out.png''); an
+optional font name; an optional flag indicating whether files
+should be silently overwritten; an optional hashref of classes to
+draw. This last argument will usually be omitted. Generates the dot
+file and passes it to the Graphviz dot for drawing. Returns a new
+$clobber argument.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2008.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile.html b/docs/ArrayExpress/Datafile.html
new file mode 100644
index 0000000..e0c5b84
--- /dev/null
+++ b/docs/ArrayExpress/Datafile.html
@@ -0,0 +1,478 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile.pm - an OO module providing methods
+for parsing data files.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <ul>
+
+                <li><a href="#accessor_methods">Accessor methods</a></li>
+        </ul>
+
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: Datafile.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile.pm - an OO module providing methods for
+parsing data files.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile;
+ my $file = ArrayExpress::Datafile->new({
+     name            => 'data.txt',
+     data_type       => 'raw',
+     array_design_id => 'A-MEXP-123',
+ });
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This is a module providing methods for data file parsing. See
+also <a href="Datafile/Parser.html">the
+ArrayExpress::Datafile::Parser manpage</a> for Datafile handler
+objects.</p>
+<h2><a name="accessor_methods" id="accessor_methods">Accessor
+methods</a></h2>
+<dl>
+<dt><strong><a name="item_set_row_count" id=
+"item_set_row_count">set_row_count</a></strong></dt>
+<dd>
+<p>Setter method for the total row count.</p>
+</dd>
+<dt><strong><a name="item_get_row_count" id=
+"item_get_row_count">get_row_count</a></strong></dt>
+<dd>
+<p>Getter method for the total row count.</p>
+</dd>
+<dt><strong><a name="item_increment_row_count" id=
+"item_increment_row_count">increment_row_count</a></strong></dt>
+<dd>
+<p>Adds one (or the passed value) to the overall row count. Returns
+the new row count.</p>
+</dd>
+<dt><strong><a name="item_get_parse_errors" id=
+"item_get_parse_errors">get_parse_errors</a></strong></dt>
+<dd>
+<p>Getter method for the total number of parse errors.</p>
+</dd>
+<dt><strong><a name="item_increment_parse_errors" id=
+"item_increment_parse_errors">increment_parse_errors</a></strong></dt>
+<dd>
+<p>Adds one (or the passed value) to the overall parsing error
+count. Returns the new error count.</p>
+</dd>
+<dt><strong><a name="item_get_not_null" id=
+"item_get_not_null">get_not_null</a></strong></dt>
+<dd>
+<p>The number of data file cells which are not null relating to
+known QTs.</p>
+</dd>
+<dt><strong><a name="item_increment_not_null" id=
+"item_increment_not_null">increment_not_null</a></strong></dt>
+<dd>
+<p>This is an incremental counter for the number of data file cells
+which are not null relating to known QTs. Returns the new ``not
+null'' count.</p>
+</dd>
+<dt><strong><a name="item_set_hyb_identifier" id=
+"item_set_hyb_identifier">set_hyb_identifier</a></strong></dt>
+<dd>
+<p>Setter method for the Tab2MAGE or MIAMExpress hybridization
+identifier associated with a raw or normalized data file. Does not
+apply to FGEM files.</p>
+</dd>
+<dt><strong><a name="item_get_hyb_identifier" id=
+"item_get_hyb_identifier">get_hyb_identifier</a></strong></dt>
+<dd>
+<p>Getter method for the Tab2MAGE or MIAMExpress hybridization
+identifier associated with a raw or normalized data file. Does not
+apply to FGEM files.</p>
+</dd>
+<dt><strong><a name="item_set_hyb_sysuid" id=
+"item_set_hyb_sysuid">set_hyb_sysuid</a></strong></dt>
+<dd>
+<p>Setter method for the internal MIAMExpress SYSUID value
+associated with a raw or normalized data file. Does not apply to
+FGEM files.</p>
+</dd>
+<dt><strong><a name="item_get_hyb_sysuid" id=
+"item_get_hyb_sysuid">get_hyb_sysuid</a></strong></dt>
+<dd>
+<p>Getter method for the internal MIAMExpress SYSUID value
+associated with a raw or normalized data file. Does not apply to
+FGEM files.</p>
+</dd>
+<dt><strong><a name="item_set_array_design_id" id=
+"item_set_array_design_id">set_array_design_id</a></strong></dt>
+<dd>
+<p>Setter method for an identifier linking the data file to an
+array design. For Tab2MAGE this identifier is the ArrayExpress
+array accession number. For MIAMExpress this identifier is the
+internal ArrayExpress Oracle database identifier for the array
+design.</p>
+</dd>
+<dt><strong><a name="item_get_array_design_id" id=
+"item_get_array_design_id">get_array_design_id</a></strong></dt>
+<dd>
+<p>Getter method for an identifier linking the data file to an
+array design. For Tab2MAGE this identifier is the ArrayExpress
+array accession number. For MIAMExpress this identifier is the
+internal ArrayExpress Oracle database identifier for the array
+design.</p>
+</dd>
+<dt><strong><a name="item_set_ded_identifier" id=
+"item_set_ded_identifier">set_ded_identifier</a></strong></dt>
+<dd>
+<p>Setter method for the MAGE identifier string representing the
+DesignElementDimension which has been associated with the file.</p>
+</dd>
+<dt><strong><a name="item_get_ded_identifier" id=
+"item_get_ded_identifier">get_ded_identifier</a></strong></dt>
+<dd>
+<p>Getter method for the MAGE identifier string representing the
+DesignElementDimension which has been associated with the file.</p>
+</dd>
+<dt><strong><a name="item_set_array_design" id=
+"item_set_array_design">set_array_design</a></strong></dt>
+<dd>
+<p>Setter method for the ArrayDesign object associated with this
+data file. See <a href="Datafile/ArrayDesign.html">the
+ArrayExpress::Datafile::ArrayDesign manpage</a> for information on
+this class.</p>
+</dd>
+<dt><strong><a name="item_get_array_design" id=
+"item_get_array_design">get_array_design</a></strong></dt>
+<dd>
+<p>Getter method for the Array Design object associated with this
+data file.</p>
+</dd>
+<dt><strong><a name="item_set_data_metrics" id=
+"item_set_data_metrics">set_data_metrics</a></strong></dt>
+<dd>
+<p>The set_data_metrics method is a setter method for a hashref
+which relates actual column heading to datatype, scale and
+subclass. The keys are QT names as defined in <a href=
+"Datafile/QT_list.html">the ArrayExpress::Datafile::QT_list
+manpage</a>. Note that the returned hashref should only have
+daughter hashrefs as values, and so if no datatype,scale or
+subclass info is available for a column heading (e.g. MetaRow,
+MetaColumn) then that coumn should not be represented. In practice,
+this method should only return information on the QTs for a single
+software type (see $self->check_column_headings).</p>
+</dd>
+<dt><strong><a name="item_get_data_metrics" id=
+"item_get_data_metrics">get_data_metrics</a></strong></dt>
+<dd>
+<p>Getter method for data metrics hashref (see
+set_data_metrics).</p>
+</dd>
+<dt><strong><a name="item_set_intensity_vector" id=
+"item_set_intensity_vector">set_intensity_vector</a></strong></dt>
+<dd>
+<p>Setter method for a hashref linking a datafile row identifier
+(e.g. ``1.1.4.1'') to a measured data value. Used in Pearson
+correlation coefficient calculation.</p>
+</dd>
+<dt><strong><a name="item_get_intensity_vector" id=
+"item_get_intensity_vector">get_intensity_vector</a></strong></dt>
+<dd>
+<p>Getter method for a hashref linking a datafile row identifier
+(e.g. ``1.1.4.1'') to a measured data value. Used in Pearson
+correlation coefficient calculation.</p>
+</dd>
+<dt><strong><a name="item_set_index_columns" id=
+"item_set_index_columns">set_index_columns</a></strong></dt>
+<dd>
+<p>Setter method for an arrayref describing the array indices of
+the coordinate columns (MetaColumn, MetaRow etc.) in
+$self->get_column_headings.</p>
+</dd>
+<dt><strong><a name="item_get_index_columns" id=
+"item_get_index_columns">get_index_columns</a></strong></dt>
+<dd>
+<p>Getter method for an arrayref describing the array indices of
+the coordinate columns (MetaColumn, MetaRow etc.) in
+$self->get_column_headings.</p>
+</dd>
+<dt><strong><a name="item_set_column_headings" id=
+"item_set_column_headings">set_column_headings</a></strong></dt>
+<dd>
+<p>Setter method for the actual column headings found in the data
+file (arrayref).</p>
+</dd>
+<dt><strong><a name="item_get_column_headings" id=
+"item_get_column_headings">get_column_headings</a></strong></dt>
+<dd>
+<p>Getter method for the actual column headings found in the data
+file (arrayref).</p>
+</dd>
+<dt><strong><a name="item_set_heading_qts" id=
+"item_set_heading_qts">set_heading_qts</a></strong></dt>
+<dd>
+<p>Setter method for an arrayref containing a list of the actual
+recognized QTs in the file. This is not a uniqued list - a repeated
+QT will appear multiple times (as for example, in a FGEM data
+file).</p>
+</dd>
+<dt><strong><a name="item_get_heading_qts" id=
+"item_get_heading_qts">get_heading_qts</a></strong></dt>
+<dd>
+<p>Getter method for an arrayref containing a list of the actual
+recognized QTs in the file. This is not a uniqued list - a repeated
+QT will appear multiple times (as for example, in a FGEM data
+file).</p>
+</dd>
+<dt><strong><a name="item_set_heading_hybs" id=
+"item_set_heading_hybs">set_heading_hybs</a></strong></dt>
+<dd>
+<p>Setter method for an arrayref containing a list of (potential)
+hyb ids derived from the column headings of a FGEM file. These are
+checked elsewhere.</p>
+</dd>
+<dt><strong><a name="item_get_heading_hybs" id=
+"item_get_heading_hybs">get_heading_hybs</a></strong></dt>
+<dd>
+<p>Getter method for an arrayref containing a list of (potential)
+hyb ids derived from the column headings of a FGEM file. These are
+checked elsewhere.</p>
+</dd>
+<dt><strong><a name="item_add_fail_columns" id=
+"item_add_fail_columns">add_fail_columns</a></strong></dt>
+<dd>
+<p>Method which adds the passed argument to a list of column
+headings which are unrecognized.</p>
+</dd>
+<dt><strong><a name="item_get_fail_columns" id=
+"item_get_fail_columns">get_fail_columns</a></strong></dt>
+<dd>
+<p>Returns an arrayref listing the unrecognized column headings
+(uniqued and sorted).</p>
+</dd>
+<dt><strong><a name="item_add_fail_hybs" id=
+"item_add_fail_hybs">add_fail_hybs</a></strong></dt>
+<dd>
+<p>Method which adds the passed argument to a list of unrecognized
+hybridization identifiers parsed from FGEM column headings which
+are unrecognized. Hybridization identifiers are either the Tab2MAGE
+or MIAMExpress user-supplied names for the hybridizations.</p>
+</dd>
+<dt><strong><a name="item_get_fail_hybs" id=
+"item_get_fail_hybs">get_fail_hybs</a></strong></dt>
+<dd>
+<p>Returns an arrayref listing the unrecognised hyb identifiers
+(uniqued and sorted).</p>
+</dd>
+<dt><strong><a name="item_set_is_exp" id=
+"item_set_is_exp">set_is_exp</a></strong></dt>
+<dd>
+<p>Setter method for boolean flag indicating whether the file is an
+EXP file or not.</p>
+</dd>
+<dt><strong><a name="item_get_is_exp" id=
+"item_get_is_exp">get_is_exp</a></strong></dt>
+<dd>
+<p>Getter method for EXP file flag.</p>
+</dd>
+<dt><strong><a name="item_get_is_binary" id=
+"item_get_is_binary">get_is_binary</a></strong></dt>
+<dd>
+<p>Getter method for binary file flag.</p>
+</dd>
+<dt><strong><a name="item_set_is_miamexpress" id=
+"item_set_is_miamexpress">set_is_miamexpress</a></strong></dt>
+<dd>
+<p>Setter method for boolean flag indicating whether the file is
+part of a MIAMExpress submission, for which slightly different
+validation rules are used. More typically the is_miamexpress
+argument to <code>new()</code> is used.</p>
+</dd>
+<dt><strong><a name="item_get_is_miamexpress" id=
+"item_get_is_miamexpress">get_is_miamexpress</a></strong></dt>
+<dd>
+<p>Getter method for MIAMExpress file flag.</p>
+</dd>
+<dt><strong><a name="item_add_factor_value" id=
+"item_add_factor_value">add_factor_value</a></strong></dt>
+<dd>
+<p>Mutator method for the experimental factor values associated
+with the file. Takes (category, value) as an argument, adds them to
+the list.</p>
+</dd>
+<dt><strong><a name="item_get_factor_value" id=
+"item_get_factor_value">get_factor_value</a></strong></dt>
+<dd>
+<p>Getter method for factor values associated with the file;
+returns a hashref in the form:</p>
+</dd>
+<dd>
+<pre>
+ {category => [value1, value2, ...], ...}
+</pre></dd>
+<dt><strong><a name="item_set_ded_type" id=
+"item_set_ded_type">set_ded_type</a></strong></dt>
+<dd>
+<p>Setter method for the type of DesignElementDimension associated
+with the file (Feature, Reporter or CompositeSequence).</p>
+</dd>
+<dt><strong><a name="item_get_ded_type" id=
+"item_get_ded_type">get_ded_type</a></strong></dt>
+<dd>
+<p>Getter method for the type of DesignElementDimension associated
+with the file.</p>
+</dd>
+<dt><strong><a name="item_set_format_type" id=
+"item_set_format_type">set_format_type</a></strong></dt>
+<dd>
+<p>Setter method for the format type (e.g., Affymetrix, GenePix,
+BlueFuse etc.). See <a href="Curator/Config.html">the
+ArrayExpress::Curator::Config manpage</a> for the enumerated
+types.</p>
+</dd>
+<dt><strong><a name="item_get_format_type" id=
+"item_get_format_type">get_format_type</a></strong></dt>
+<dd>
+<p>Getter method for the format type.</p>
+</dd>
+<dt><strong><a name="item_set_data_type" id=
+"item_set_data_type">set_data_type</a></strong></dt>
+<dd>
+<p>Setter method for the data type (e.g., raw, normalized,
+transformed). See <a href="Curator/Config.html">the
+ArrayExpress::Curator::Config manpage</a> for the enumerated types.
+Also allowed is the 'EXP' file type (Affymetrix).</p>
+</dd>
+<dt><strong><a name="item_get_data_type" id=
+"item_get_data_type">get_data_type</a></strong></dt>
+<dd>
+<p>Getter method for the data type.</p>
+</dd>
+<dt><strong><a name="item_set_qt_type" id=
+"item_set_qt_type">set_qt_type</a></strong></dt>
+<dd>
+<p>Setter method for the QT type associated with the file. This is
+derived from the software names defined in <a href=
+"Datafile/QT_list.html">the ArrayExpress::Datafile::QT_list
+manpage</a>.</p>
+</dd>
+<dt><strong><a name="item_get_qt_type" id=
+"item_get_qt_type">get_qt_type</a></strong></dt>
+<dd>
+<p>Getter method for the QT type associated with the file.</p>
+</dd>
+<dt><strong><a name="item_set_path" id=
+"item_set_path">set_path</a></strong></dt>
+<dd>
+<p>Setter method for the full filesystem path of the data file.</p>
+</dd>
+<dt><strong><a name="item_get_path" id=
+"item_get_path">get_path</a></strong></dt>
+<dd>
+<p>Getter method for the full filesystem path of the data file.</p>
+</dd>
+<dt><strong><a name="item_set_name" id=
+"item_set_name">set_name</a></strong></dt>
+<dd>
+<p>Setter method for the name of the file. Also sets
+$self->set_path if it has not been otherwise set. Note that this
+is not the same behaviour as the ``name'' argument to the object
+constructor, which does not change the path at all.</p>
+</dd>
+<dt><strong><a name="item_get_name" id=
+"item_get_name">get_name</a></strong></dt>
+<dd>
+<p>Getter method for the name of the file.</p>
+</dd>
+<dt><strong><a name="item_set_target_filename" id=
+"item_set_target_filename">set_target_filename</a></strong></dt>
+<dd>
+<p>Setter method for the name of the output stripped data file.</p>
+</dd>
+<dt><strong><a name="item_get_target_filename" id=
+"item_get_target_filename">get_target_filename</a></strong></dt>
+<dd>
+<p>Getter method for the name of the output stripped data file.</p>
+</dd>
+<dt><strong><a name="item_get_linebreak_type" id=
+"item_get_linebreak_type">get_linebreak_type</a></strong></dt>
+<dd>
+<p>Getter method for the line-ending character(s). Can be
+<code>\n</code>, <code>\r\n</code>, <code>\r</code> or
+<code>Unknown</code>.</p>
+</dd>
+<dt><strong><a name="item_get_line_format" id=
+"item_get_line_format">get_line_format</a></strong></dt>
+<dd>
+<p>Getter method for the line-ending format (Mac, Unix, DOS or
+Unknown). Used in reports.</p>
+</dd>
+<dt><strong><a name="item_set_exp_data" id=
+"item_set_exp_data">set_exp_data</a></strong></dt>
+<dd>
+<p>Setter method for storing the output of the
+$self->parse_exp_file method.</p>
+</dd>
+<dt><strong><a name="item_get_exp_data" id=
+"item_get_exp_data">get_exp_data</a></strong></dt>
+<dd>
+<p>Getter method for retrieving the output of the
+$self->parse_exp_file method.</p>
+</dd>
+<dt><strong><a name="item_set_mage_qtd" id=
+"item_set_mage_qtd">set_mage_qtd</a></strong></dt>
+<dd>
+<p>Setter method for the
+Bio::MAGE::BioAssayData::QuantitationTypeDimension object
+associated with the file.</p>
+</dd>
+<dt><strong><a name="item_get_mage_qtd" id=
+"item_get_mage_qtd">get_mage_qtd</a></strong></dt>
+<dd>
+<p>Getter method for the
+Bio::MAGE::BioAssayData::QuantitationTypeDimension object
+associated with the file.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2004.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix.html b/docs/ArrayExpress/Datafile/Affymetrix.html
new file mode 100644
index 0000000..52f5a6b
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix.html
@@ -0,0 +1,96 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix - a factory class for
+generating Affymetrix data file parsing objects.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#methods">METHODS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: Affymetrix.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix - a factory class for
+generating Affymetrix data file parsing objects.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix;
+</pre>
+<pre>
+ my $fac = ArrayExpress::Datafile::Affymetrix->new();
+</pre>
+<pre>
+ my $cel = $fac->make_parser( 'Data1.CEL' );
+</pre>
+<pre>
+ $cel->parse();
+</pre>
+<pre>
+ $cel->export($output_filehandle);
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module is a factory class used in to create data file
+parsers for Affymetrix file formats. CEL, CHP and EXP formats are
+supported, with limited CDF parsing. Both old (GDAC) and new
+(GCOS/XDA) file formats can be parsed.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_new" id=
+"item_new"><code>new()</code></a></strong></dt>
+<dd>
+<p>The class constructor.</p>
+</dd>
+<dt><strong><a name="item_make_parser" id=
+"item_make_parser"><code>make_parser($file)</code></a></strong></dt>
+<dd>
+<p>This method takes a filename argument and returns a parser
+object that can then be used to process the data file and return
+interesting values.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CDF.html b/docs/ArrayExpress/Datafile/Affymetrix/CDF.html
new file mode 100644
index 0000000..9df5728
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CDF.html
@@ -0,0 +1,79 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CDF.pm - CDF data file
+parsing.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#methods">METHODS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: CDF.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CDF.pm - CDF data file
+parsing.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use base qw( ArrayExpress::Datafile::Affymetrix::CDF );
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module is an abstract superclass used in parsing and export
+of data from Affymetrix CDF files.</p>
+<p>Please see <a href="./Parser.html">the
+ArrayExpress::Datafile::Affymetrix::Parser manpage</a> for methods
+common to all the Affymetrix parser classes.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_get_num_qc_cells" id=
+"item_get_num_qc_cells"><code>get_num_qc_cells()</code></a></strong></dt>
+<dd>
+<p>The number of cells on the chip dedicated to QC
+measurements.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CDF/GDAC_CDF.html b/docs/ArrayExpress/Datafile/Affymetrix/CDF/GDAC_CDF.html
new file mode 100644
index 0000000..5789271
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CDF/GDAC_CDF.html
@@ -0,0 +1,80 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::GDAC_CDF.pm - GDAC CDF
+data file parsing.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#methods">METHODS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: GDAC_CDF.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::GDAC_CDF.pm - GDAC CDF data
+file parsing.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::CDF::GDAC_CDF;
+</pre>
+<pre>
+ my $cdf = ArrayExpress::Datafile::Affymetrix::CDF::GDAC_CDF->new({
+     input => 'HG-U133A.cdf',
+ });
+ $cdf->parse();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module implements parsing and export of data from
+Affymetrix GDAC CDF files.</p>
+<p>Please see <a href="../Parser.html">the
+ArrayExpress::Datafile::Affymetrix::Parser manpage</a> for methods
+common to all the Affymetrix parser classes.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<p>Most accessors and methods are implemented in the superclass;
+see <a href="../CDF.html">the
+ArrayExpress::Datafile::Affymetrix::CDF manpage</a>.</p>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CDF/XDA_CDF.html b/docs/ArrayExpress/Datafile/Affymetrix/CDF/XDA_CDF.html
new file mode 100644
index 0000000..683a7f9
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CDF/XDA_CDF.html
@@ -0,0 +1,81 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CDF::XDA_CDF.pm - XDA
+CDF data file parsing.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#methods">METHODS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: XDA_CDF.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CDF::XDA_CDF.pm - XDA CDF
+data file parsing.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::CDF::XDA_CDF;
+</pre>
+<pre>
+ my $cdf = ArrayExpress::Datafile::Affymetrix::CDF::XDA_CDF->new({
+     input => 'HG-U133A.cdf',
+ });
+ $cdf->parse();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module implements parsing and export of data from
+Affymetrix XDA CDF files.</p>
+<p>Please see <a href="../Parser.html">the
+ArrayExpress::Datafile::Affymetrix::Parser manpage</a> for methods
+common to all the Affymetrix parser classes.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<p>Most parsing methods and accessors are implemented in the
+superclasses. See <a href="./GDAC_CDF.html">the
+ArrayExpress::Datafile::Affymetrix::CDF::GDAC_CDF manpage</a> for
+information.</p>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CDFFactory.html b/docs/ArrayExpress/Datafile/Affymetrix/CDFFactory.html
new file mode 100644
index 0000000..987a2c8
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CDFFactory.html
@@ -0,0 +1,95 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CDFFactory - a factory
+class for generating Affymetrix CDF file parsing objects.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#methods">METHODS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: CDFFactory.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CDFFactory - a factory class
+for generating Affymetrix CDF file parsing objects.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::CDFFactory;
+</pre>
+<pre>
+ my $fac = ArrayExpress::Datafile::Affymetrix::CDFFactory->new();
+</pre>
+<pre>
+ my $cdf = $fac->make_parser( 'Data1.CDF' );
+</pre>
+<pre>
+ $cdf->parse();
+</pre>
+<pre>
+ $cdf->export($output_filehandle);
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module is a factory class used in to create data file
+parsers for Affymetrix CDF files. Both old (GDAC) and new
+(GCOS/XDA) file formats can be parsed.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_new" id=
+"item_new"><code>new()</code></a></strong></dt>
+<dd>
+<p>The class constructor.</p>
+</dd>
+<dt><strong><a name="item_make_parser" id=
+"item_make_parser"><code>make_parser($file)</code></a></strong></dt>
+<dd>
+<p>This method takes a filename argument and returns a parser
+object that can then be used to process the data file and return
+interesting values.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CEL.html b/docs/ArrayExpress/Datafile/Affymetrix/CEL.html
new file mode 100644
index 0000000..026a053
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CEL.html
@@ -0,0 +1,128 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CEL.pm - CEL data file
+parsing</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#methods">METHODS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: CEL.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CEL.pm - CEL data file
+parsing</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::CEL;
+</pre>
+<pre>
+ my $cel = ArrayExpress::Datafile::Affymetrix::CEL->new({
+     input => 'mydatafile.CEL',
+ });
+ $cel->parse();
+ $cel->export($output_fh);
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module implements an abstract superclass used in parsing
+and export of data from Affymetrix CEL files.</p>
+<p>Please see <a href="./Parser.html">the
+ArrayExpress::Datafile::Affymetrix::Parser manpage</a> for methods
+common to all the Affymetrix parser classes.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_parse_header" id=
+"item_parse_header"><code>parse_header()</code></a></strong></dt>
+<dd>
+<p>This method will take the <code>input</code> attribute and parse
+only the header metadata. Note that for older CEL file formats, the
+numbers of masked, outlier or modified cells are not set by this
+method, since these values are embedded in the main body of the
+data.</p>
+</dd>
+<dt><strong><a name="item_export" id=
+"item_export"><code>export($fh)</code></a></strong></dt>
+<dd>
+<p>This method takes a filehandle and prints the parsed expression
+data out to it. The QuantitationTypeDimension and
+DesignElementDimension of the resulting matrix are given by the
+<code>get_qtd</code> and <a href=
+"#item_get_ded"><code>get_ded</code></a> methods, respectively.</p>
+</dd>
+<dt><strong><a name="item_get_ded" id=
+"item_get_ded"><code>get_ded($chip_type)</code></a></strong></dt>
+<dd>
+<p>This method takes an optional string argument representing the
+chip type (e.g., ``HG-U133A'') and uses it to generate a reference
+to an array of Feature identifiers arranged in the order that the
+<strong>export</strong> method outputs them (i.e., a
+DesignElementDimension). The chip type can be derived in a number
+of ways; if EXP files are available, for instance, the
+$exp-><code>get_chip_type()</code> method should return an
+appropriate string. If this argument is not supplied then the
+$cel-><code>get_chip_type()</code> method is used to derive a
+value; note however that that method relies on parsing an
+undocumented tag and as such it may fail.</p>
+</dd>
+<dt><strong><a name="item_get_num_masked" id=
+"item_get_num_masked"><code>get_num_masked()</code></a></strong></dt>
+<dd>
+<p>The number of masked cells.</p>
+</dd>
+<dt><strong><a name="item_get_num_outliers" id=
+"item_get_num_outliers"><code>get_num_outliers()</code></a></strong></dt>
+<dd>
+<p>The number of outlier cells.</p>
+</dd>
+<dt><strong><a name="item_get_num_modified" id=
+"item_get_num_modified"><code>get_num_modified()</code></a></strong></dt>
+<dd>
+<p>The number of modified cells (this should always be zero).</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CEL/CELv3.html b/docs/ArrayExpress/Datafile/Affymetrix/CEL/CELv3.html
new file mode 100644
index 0000000..e21830e
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CEL/CELv3.html
@@ -0,0 +1,81 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CEL::CELv3.pm - CELv3
+data file parsing</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#methods">METHODS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: CELv3.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CEL::CELv3.pm - CELv3 data
+file parsing</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::CEL::CELv3;
+</pre>
+<pre>
+ my $cel = ArrayExpress::Datafile::Affymetrix::CEL::CELv3->new({
+     input => 'mydatafile.CEL',
+ });
+ $cel->parse();
+ $cel->export($output_fh);
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module implements parsing and export of data from
+Affymetrix GDAC CEL (v3) files.</p>
+<p>Please see <a href="../Parser.html">the
+ArrayExpress::Datafile::Affymetrix::Parser manpage</a> for methods
+common to all the Affymetrix parser classes.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<p>Most methods are implemented in the superclass. See <a href=
+"../CEL.html">the ArrayExpress::Datafile::Affymetrix::CEL
+manpage</a> for details.</p>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CEL/CELv4.html b/docs/ArrayExpress/Datafile/Affymetrix/CEL/CELv4.html
new file mode 100644
index 0000000..ee00cc8
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CEL/CELv4.html
@@ -0,0 +1,102 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CEL::CELv4.pm - CELv4
+data file parsing</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#methods">METHODS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: CELv4.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CEL::CELv4.pm - CELv4 data
+file parsing</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::CEL::CELv4;
+</pre>
+<pre>
+ my $cel = ArrayExpress::Datafile::Affymetrix::CEL::CELv4->new({
+     input => 'mydatafile.CEL',
+ });
+ $cel->parse();
+ $cel->export($output_fh);
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module implements parsing and export of data from
+Affymetrix XDA CEL (v4) files.</p>
+<p>Please see <a href="../Parser.html">the
+ArrayExpress::Datafile::Affymetrix::Parser manpage</a> for methods
+common to all the Affymetrix parser classes.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<p>Most parsing methods and accessors are implemented in the
+superclasses. See <a href="./CELv3.html">the
+ArrayExpress::Datafile::Affymetrix::CEL::CELv3 manpage</a> for
+information. The following attributes are specific to CELv4
+files:</p>
+<dl>
+<dt><strong><a name="item_get_cell_margin" id=
+"item_get_cell_margin"><code>get_cell_margin()</code></a></strong></dt>
+<dd>
+<p>The cell margin.</p>
+</dd>
+<dt><strong><a name="item_get_num_subgrids" id=
+"item_get_num_subgrids"><code>get_num_subgrids()</code></a></strong></dt>
+<dd>
+<p>The number of subgrids used on the chip (N.B. this is currently
+UNTESTED).</p>
+</dd>
+<dt><strong><a name="item_get_subgrids" id=
+"item_get_subgrids"><code>get_subgrids()</code></a></strong></dt>
+<dd>
+<p>A reference to a hash containing subgrid information (N.B. this
+is currently UNTESTED).</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CELFactory.html b/docs/ArrayExpress/Datafile/Affymetrix/CELFactory.html
new file mode 100644
index 0000000..46438f8
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CELFactory.html
@@ -0,0 +1,95 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CELFactory - a factory
+class for generating Affymetrix CEL file parsing objects.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#methods">METHODS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: CELFactory.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CELFactory - a factory class
+for generating Affymetrix CEL file parsing objects.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::CELFactory;
+</pre>
+<pre>
+ my $fac = ArrayExpress::Datafile::Affymetrix::CELFactory->new();
+</pre>
+<pre>
+ my $cel = $fac->make_parser( 'Data1.CEL' );
+</pre>
+<pre>
+ $cel->parse();
+</pre>
+<pre>
+ $cel->export($output_filehandle);
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module is a factory class used in to create data file
+parsers for Affymetrix CEL files. Both old (GDAC) and new
+(GCOS/XDA) file formats can be parsed.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_new" id=
+"item_new"><code>new()</code></a></strong></dt>
+<dd>
+<p>The class constructor.</p>
+</dd>
+<dt><strong><a name="item_make_parser" id=
+"item_make_parser"><code>make_parser($file)</code></a></strong></dt>
+<dd>
+<p>This method takes a filename argument and returns a parser
+object that can then be used to process the data file and return
+interesting values.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CHP.html b/docs/ArrayExpress/Datafile/Affymetrix/CHP.html
new file mode 100644
index 0000000..7b02819
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CHP.html
@@ -0,0 +1,106 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CHP - GDAC CHP data file
+parsing.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#methods">METHODS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: CHP.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CHP - GDAC CHP data file
+parsing.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use base qw( ArrayExpress::Datafile::Affymetrix::CHP );
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>Abstract superclass for parsing and export of data from
+Affymetrix CHP files.</p>
+<p>Please see the <a href="./Parser.html">the
+ArrayExpress::Datafile::Affymetrix::Parser manpage</a>
+documentation for methods common to all the Affymetrix file
+classes.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_parse_header" id=
+"item_parse_header"><code>parse_header()</code></a></strong></dt>
+<dd>
+<p>This method will take the <code>input</code> attribute and parse
+only the header metadata.</p>
+</dd>
+<dt><strong><a name="item_export" id=
+"item_export"><code>export($fh, $cdf)</code></a></strong></dt>
+<dd>
+<p>This method takes a filehandle and prints the parsed expression
+data out to it. The method also requires a pre-parsed CDF object to
+be passed to it. The QuantitationTypeDimension and
+DesignElementDimension of the resulting matrix are given by the
+<code>get_qtd</code> and <a href=
+"#item_get_ded"><code>get_ded</code></a> methods, respectively.</p>
+</dd>
+<dt><strong><a name="item_get_ded" id=
+"item_get_ded"><code>get_ded($cdf,
+$chip_type)</code></a></strong></dt>
+<dd>
+<p>This method takes a pre-parsed CDF object, and an optional
+string argument representing the chip type (e.g., ``HG-U133A'').
+The method uses these arguments to generate a reference to an array
+of CompositeSequence identifiers, arranged in the order that the
+<strong>export</strong> method outputs them (i.e., a
+DesignElementDimension). The chip type can be derived in a number
+of ways; if EXP files are available, for instance, the
+$exp-><code>get_chip_type()</code> method should return an
+appropriate string. If this argument is not supplied then the
+<code>chip_type()</code> methods for the CHP and CDF objects are
+inspected.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CHP/CHPv12.html b/docs/ArrayExpress/Datafile/Affymetrix/CHP/CHPv12.html
new file mode 100644
index 0000000..21775b1
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CHP/CHPv12.html
@@ -0,0 +1,84 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CHP::CHPv12 - CHPv12
+data file parsing.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#methods">METHODS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: CHPv12.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CHP::CHPv12 - CHPv12 data
+file parsing.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::CHP::CHPv12;
+</pre>
+<pre>
+ my $chp = ArrayExpress::Datafile::Affymetrix::CHP::CHPv12->new({
+     input => 'mydata.CHP',
+ });
+</pre>
+<pre>
+ $chp->parse();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module implements parsing and export of data from
+Affymetrix GDAC CHP (v12) files.</p>
+<p>Please see the <a href="../Parser.html">the
+ArrayExpress::Datafile::Affymetrix::Parser manpage</a>
+documentation for methods common to all the Affymetrix file
+classes.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<p>Most methods are implemented in the superclass. Please see
+<a href="./GDAC_CHP.html">the
+ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP manpage</a> for
+details.</p>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CHP/CHPv13.html b/docs/ArrayExpress/Datafile/Affymetrix/CHP/CHPv13.html
new file mode 100644
index 0000000..42c8920
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CHP/CHPv13.html
@@ -0,0 +1,84 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CHP::CHPv13 - CHPv13
+data file parsing.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#methods">METHODS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: CHPv13.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CHP::CHPv13 - CHPv13 data
+file parsing.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::CHP::CHPv13;
+</pre>
+<pre>
+ my $chp = ArrayExpress::Datafile::Affymetrix::CHP::CHPv13->new({
+     input => 'mydata.CHP',
+ });
+</pre>
+<pre>
+ $chp->parse();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module implements parsing and export of data from
+Affymetrix GDAC CHP (v12) files.</p>
+<p>Please see the <a href="../Parser.html">the
+ArrayExpress::Datafile::Affymetrix::Parser manpage</a>
+documentation for methods common to all the Affymetrix file
+classes.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<p>Most methods are implemented in the superclass. Please see
+<a href="./GDAC_CHP.html">the
+ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP manpage</a> for
+details.</p>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CHP/CHPv8.html b/docs/ArrayExpress/Datafile/Affymetrix/CHP/CHPv8.html
new file mode 100644
index 0000000..45b6010
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CHP/CHPv8.html
@@ -0,0 +1,85 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CHP::CHPv8 - CHPv8 data
+file parsing.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#methods">METHODS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: CHPv8.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CHP::CHPv8 - CHPv8 data file
+parsing.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::CHP::CHPv8;
+</pre>
+<pre>
+ my $chp = ArrayExpress::Datafile::Affymetrix::CHP::CHPv8->new({
+     input => 'mydata.CHP',
+ });
+</pre>
+<pre>
+ $chp->parse_header();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module implements parsing and export of data from
+Affymetrix GDAC CHP (v8) files. Note that only the data file header
+can be parsed using this module.</p>
+<p>Please see the <a href="../Parser.html">the
+ArrayExpress::Datafile::Affymetrix::Parser manpage</a>
+documentation for methods common to all the Affymetrix file
+classes.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<p>Most methods are implemented in the superclass. Please see
+<a href="./GDAC_CHP.html">the
+ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP manpage</a> for
+details.</p>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CHP/GDAC_CHP.html b/docs/ArrayExpress/Datafile/Affymetrix/CHP/GDAC_CHP.html
new file mode 100644
index 0000000..5395fd8
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CHP/GDAC_CHP.html
@@ -0,0 +1,108 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP - GDAC CHP
+data file parsing.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#methods">METHODS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: GDAC_CHP.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP - GDAC CHP
+data file parsing.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use base qw( ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP );
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>Abstract superclass for parsing and export of data from
+Affymetrix CHP files. For older GDAC CHP files this class also
+implements a file header parsing method, which can be used to
+determine the version of the file (e.g. v8, v12, v13).</p>
+<p>Please see the <a href="../Parser.html">the
+ArrayExpress::Datafile::Affymetrix::Parser manpage</a>
+documentation for methods common to all the Affymetrix file
+classes.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_parse_header" id=
+"item_parse_header"><code>parse_header()</code></a></strong></dt>
+<dd>
+<p>This method will take the <code>input</code> attribute and parse
+only the header metadata.</p>
+</dd>
+<dt><strong><a name="item_export" id=
+"item_export"><code>export($fh, $cdf)</code></a></strong></dt>
+<dd>
+<p>This method takes a filehandle and prints the parsed expression
+data out to it. The method also requires a pre-parsed CDF object to
+be passed to it. The QuantitationTypeDimension and
+DesignElementDimension of the resulting matrix are given by the
+<code>get_qtd</code> and <a href=
+"#item_get_ded"><code>get_ded</code></a> methods, respectively.</p>
+</dd>
+<dt><strong><a name="item_get_ded" id=
+"item_get_ded"><code>get_ded($cdf,
+$chip_type)</code></a></strong></dt>
+<dd>
+<p>This method takes a pre-parsed CDF object, and an optional
+string argument representing the chip type (e.g., ``HG-U133A'').
+The method uses these arguments to generate a reference to an array
+of CompositeSequence identifiers, arranged in the order that the
+<strong>export</strong> method outputs them (i.e., a
+DesignElementDimension). The chip type can be derived in a number
+of ways; if EXP files are available, for instance, the
+$exp-><code>get_chip_type()</code> method should return an
+appropriate string. If this argument is not supplied then the
+<code>chip_type()</code> methods for the CHP and CDF objects are
+inspected.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CHP/XDA_CHP.html b/docs/ArrayExpress/Datafile/Affymetrix/CHP/XDA_CHP.html
new file mode 100644
index 0000000..4d54e2c
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CHP/XDA_CHP.html
@@ -0,0 +1,84 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CHP::XDA_CHP - XDA CHP
+data file parsing.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#methods">METHODS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: XDA_CHP.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CHP::XDA_CHP - XDA CHP data
+file parsing.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::CHP::XDA_CHP;
+</pre>
+<pre>
+ my $chp = ArrayExpress::Datafile::Affymetrix::CHP::XDA_CHP->new({
+     input => 'mydata.CHP',
+ });
+</pre>
+<pre>
+ $chp->parse();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module implements parsing and export of data from
+Affymetrix XDA CHP files.</p>
+<p>Please see the <a href="../Parser.html">the
+ArrayExpress::Datafile::Affymetrix::Parser manpage</a>
+documentation for methods common to all the Affymetrix file
+classes.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<p>Most methods are implemented in the superclass. Please see
+<a href="./GDAC_CHP.html">the
+ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP manpage</a> for
+details.</p>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CHPFactory.html b/docs/ArrayExpress/Datafile/Affymetrix/CHPFactory.html
new file mode 100644
index 0000000..b2edf5a
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CHPFactory.html
@@ -0,0 +1,95 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CHPFactory - a factory
+class for generating Affymetrix CHP file parsing objects.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#methods">METHODS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: CHPFactory.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CHPFactory - a factory class
+for generating Affymetrix CHP file parsing objects.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::CHPFactory;
+</pre>
+<pre>
+ my $fac = ArrayExpress::Datafile::Affymetrix::CHPFactory->new();
+</pre>
+<pre>
+ my $cel = $fac->make_parser( 'Data1.CHP' );
+</pre>
+<pre>
+ $cel->parse();
+</pre>
+<pre>
+ $cel->export($output_filehandle);
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module is a factory class used in to create data file
+parsers for Affymetrix CHP files. Both old (GDAC) and new
+(GCOS/XDA) file formats can be parsed.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_new" id=
+"item_new"><code>new()</code></a></strong></dt>
+<dd>
+<p>The class constructor.</p>
+</dd>
+<dt><strong><a name="item_make_parser" id=
+"item_make_parser"><code>make_parser($file)</code></a></strong></dt>
+<dd>
+<p>This method takes a filename argument and returns a parser
+object that can then be used to process the data file and return
+interesting values.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/EXP.html b/docs/ArrayExpress/Datafile/Affymetrix/EXP.html
new file mode 100644
index 0000000..e5e17f4
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/EXP.html
@@ -0,0 +1,165 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::EXP - EXP data file
+parsing</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#methods">METHODS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: CDF.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::EXP - EXP data file
+parsing</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::EXP;
+</pre>
+<pre>
+ my $exp = ArrayExpress::Datafile::Affymetrix::EXP->new({
+     input => 'data1.EXP',
+ });
+ $exp->parse();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module implements parsing and export of data from
+Affymetrix EXP files.</p>
+<p>Please see <a href="./Parser.html">the
+ArrayExpress::Datafile::Affymetrix::Parser manpage</a> for methods
+common to all the Affymetrix parser classes.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_export" id=
+"item_export"><code>export($fh)</code></a></strong></dt>
+<dd>
+<p>This method takes a filehandle and prints out the EXP file in
+its original format. This method is not particularly useful and is
+really for testing purposes only.</p>
+</dd>
+<dt><strong><a name="item_get_chip_lot" id=
+"item_get_chip_lot"><code>get_chip_lot()</code></a></strong></dt>
+<dd>
+<p>The lot number of the chip.</p>
+</dd>
+<dt><strong><a name="item_get_operator" id=
+"item_get_operator"><code>get_operator()</code></a></strong></dt>
+<dd>
+<p>The person who performed the procedure.</p>
+</dd>
+<dt><strong><a name="item_get_protocol" id=
+"item_get_protocol"><code>get_protocol()</code></a></strong></dt>
+<dd>
+<p>The name of the hybridization protocol used (e.g.
+EukGE-WS2v4).</p>
+</dd>
+<dt><strong><a name="item_get_station" id=
+"item_get_station"><code>get_station()</code></a></strong></dt>
+<dd>
+<p>The station number.</p>
+</dd>
+<dt><strong><a name="item_get_module" id=
+"item_get_module"><code>get_module()</code></a></strong></dt>
+<dd>
+<p>The module number.</p>
+</dd>
+<dt><strong><a name="item_get_hyb_date" id=
+"item_get_hyb_date"><code>get_hyb_date()</code></a></strong></dt>
+<dd>
+<p>The date on which the hybridization was performed. This is
+returned in the same format as in the EXP file; no sanitization is
+performed.</p>
+</dd>
+<dt><strong><a name="item_get_pixel_size" id=
+"item_get_pixel_size"><code>get_pixel_size()</code></a></strong></dt>
+<dd>
+<p>Pixel size (integer).</p>
+</dd>
+<dt><strong><a name="item_get_filter" id=
+"item_get_filter"><code>get_filter()</code></a></strong></dt>
+<dd>
+<p>Filter (570nm).</p>
+</dd>
+<dt><strong><a name="item_get_scan_temp" id=
+"item_get_scan_temp"><code>get_scan_temp()</code></a></strong></dt>
+<dd>
+<p>Scan temperature.</p>
+</dd>
+<dt><strong><a name="item_get_scan_date" id=
+"item_get_scan_date"><code>get_scan_date()</code></a></strong></dt>
+<dd>
+<p>The date on which the scanning was performed. This is returned
+in the same format as in the EXP file; no sanitization is
+performed.</p>
+</dd>
+<dt><strong><a name="item_get_scanner_id" id=
+"item_get_scanner_id"><code>get_scanner_id()</code></a></strong></dt>
+<dd>
+<p>Scanner ID.</p>
+</dd>
+<dt><strong><a name="item_get_num_scans" id=
+"item_get_num_scans"><code>get_num_scans()</code></a></strong></dt>
+<dd>
+<p>Number of scans performed.</p>
+</dd>
+<dt><strong><a name="item_get_scanner_type" id=
+"item_get_scanner_type"><code>get_scanner_type()</code></a></strong></dt>
+<dd>
+<p>Scanner type.</p>
+</dd>
+<dt><strong><a name="item_get_hyb_parameters" id=
+"item_get_hyb_parameters"><code>get_hyb_parameters()</code></a></strong></dt>
+<dd>
+<p>A reference to an array of named hybridization parameters. Each
+parameter is coded as a separate hash with a single {name =>
+value} pair. Typically this method is not very useful; you should
+probably be using the <strong>parameters</strong> or
+<strong>add_parameters</strong> methods instead.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/EXPFactory.html b/docs/ArrayExpress/Datafile/Affymetrix/EXPFactory.html
new file mode 100644
index 0000000..4e2575a
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/EXPFactory.html
@@ -0,0 +1,94 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::EXPFactory - a factory
+class for generating Affymetrix EXP file parsing objects.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#methods">METHODS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: EXPFactory.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::EXPFactory - a factory class
+for generating Affymetrix EXP file parsing objects.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::EXPFactory;
+</pre>
+<pre>
+ my $fac = ArrayExpress::Datafile::Affymetrix::EXPFactory->new();
+</pre>
+<pre>
+ my $exp = $fac->make_parser( 'Data1.EXP' );
+</pre>
+<pre>
+ $exp->parse();
+</pre>
+<pre>
+ $exp->export($output_filehandle);
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module is a factory class used in to create data file
+parsers for Affymetrix EXP files.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_new" id=
+"item_new"><code>new()</code></a></strong></dt>
+<dd>
+<p>The class constructor.</p>
+</dd>
+<dt><strong><a name="item_make_parser" id=
+"item_make_parser"><code>make_parser($file)</code></a></strong></dt>
+<dd>
+<p>This method takes a filename argument and returns a parser
+object that can then be used to process the data file and return
+interesting values.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/Parser.html b/docs/ArrayExpress/Datafile/Affymetrix/Parser.html
new file mode 100644
index 0000000..8639c17
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/Parser.html
@@ -0,0 +1,183 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::Parser - an Affymetrix
+data file parsing module.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#methods">METHODS</a></li>
+        <ul>
+
+                <li><a href="#accessor_methods">Accessor methods</a></li>
+        </ul>
+
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: Affymetrix/Parser.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::Parser - an Affymetrix data
+file parsing module.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use base qw( ArrayExpress::Datafile::Affymetrix::Parser );
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module is an abstract superclass used in the parsing and
+export of data from Affymetrix file formats. CEL, CHP and EXP
+formats are supported, with limited CDF parsing. Both old (GDAC)
+and new (GCOS/XDA) file formats can be parsed.</p>
+<p>There is a set of methods common to all the Affymetrix file
+classes, listed below. There are additional methods, specific to
+each class, which are documented in the relevant pages.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<p>The following methods are common to all classes.</p>
+<dl>
+<dt><strong><a name="item_new" id="item_new">new({ input =>
+'myfile.CEL' })</a></strong></dt>
+<dd>
+<p>The class constructor. This method returns a an object of the
+appropriate class, without performing any additional
+processing.</p>
+</dd>
+<dt><strong><a name="item_parse" id=
+"item_parse"><code>parse()</code></a></strong></dt>
+<dd>
+<p>This method will take the value for the <code>input</code>
+attribute and parse the data into memory so that it can be
+interrogated using the methods below.</p>
+</dd>
+</dl>
+<h2><a name="accessor_methods" id="accessor_methods">Accessor
+methods</a></h2>
+<p>Each of these methods acts as both setter and getter for the
+attributes in question. Typically these will be used to access the
+data and metadata which was extracted using the
+<strong>parse</strong> method. Please see the respective subclass
+documentation for information on the <strong>export</strong> and
+<strong>get_ded</strong> methods. Note that many of the following
+will have no meaning for EXP file metadata.</p>
+<dl>
+<dt><strong><a name="item_get_version" id=
+"item_get_version"><code>get_version()</code></a></strong></dt>
+<dd>
+<p>The version number or string associated with the file. For CEL
+and CHP files this should be either 3 or 4.</p>
+</dd>
+<dt><strong><a name="item_get_num_columns" id=
+"item_get_num_columns"><code>get_num_columns()</code></a></strong></dt>
+<dd>
+<p>The number of columns on the array.</p>
+</dd>
+<dt><strong><a name="item_get_num_rows" id=
+"item_get_num_rows"><code>get_num_rows()</code></a></strong></dt>
+<dd>
+<p>The number of rows on the array.</p>
+</dd>
+<dt><strong><a name="item_get_num_cells" id=
+"item_get_num_cells"><code>get_num_cells()</code></a></strong></dt>
+<dd>
+<p>The number of cells on the array. For CEL files this corresponds
+to the number of columns multiplied by the number of rows.</p>
+</dd>
+<dt><strong><a name="item_get_algorithm" id=
+"item_get_algorithm"><code>get_algorithm()</code></a></strong></dt>
+<dd>
+<p>The name of the algorithm used to produce the data (e.g.
+``Percentile'', ``ExpressionStat'').</p>
+</dd>
+<dt><strong><a name="item_get_chip_type" id=
+"item_get_chip_type"><code>get_chip_type()</code></a></strong></dt>
+<dd>
+<p>The type of chip used (e.g., HG-U133A). This is supported for
+EXP files, CEL files, CHP files and GDAC format CDF files. Note
+that for CEL files this relies on parsing a header tag which is not
+actually documented by Affymetrix, and so it is possible that this
+method is not to be trusted in such cases.</p>
+</dd>
+<dt><strong><a name="item_get_parameters" id=
+"item_get_parameters"><code>get_parameters()</code></a></strong></dt>
+<dd>
+<p>A reference to a hash with parameter {name => value} pairs.
+Parameters are grouped as follows:</p>
+</dd>
+<dd>
+<pre>
+ CEL: Feature extraction parameters
+ CHP: Normalization parameters
+ EXP: Hybridization parameters (numbered).
+</pre></dd>
+<dt><strong><a name="item_get_stats" id=
+"item_get_stats"><code>get_stats()</code></a></strong></dt>
+<dd>
+<p>A reference to a hash with statistic {name => value} pairs.
+Statistics are grouped as follows:</p>
+</dd>
+<dd>
+<pre>
+ CEL: Feature extraction summary statistics
+ CHP: Normalization summary statistics
+</pre></dd>
+<dt><strong><a name="item_get_qtd" id=
+"item_get_qtd"><code>get_qtd()</code></a></strong></dt>
+<dd>
+<p>A reference to an array listing the QuantitationType identifiers
+(long form) in the column order that they are output by the
+<strong>export</strong> method. See also the
+<strong>headings</strong> method below.</p>
+</dd>
+<dt><strong><a name="item_get_headings" id=
+"item_get_headings"><code>get_headings()</code></a></strong></dt>
+<dd>
+<p>A reference to an array listing the QuantitationType names
+(short form) in the column order that they are output by the
+<strong>export</strong> method. Using this method also populates
+the <strong>qtd</strong> method data structure, but not vice
+versa.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/ArrayDesign.html b/docs/ArrayExpress/Datafile/ArrayDesign.html
new file mode 100644
index 0000000..7b26568
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/ArrayDesign.html
@@ -0,0 +1,126 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::ArrayDesign - memory-efficient
+handling of array design information.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <ul>
+
+                <li><a href="#accessor_methods">Accessor methods</a></li>
+        </ul>
+
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: ExperimentChecker.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::ArrayDesign - memory-efficient handling
+of array design information.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile:::ArrayDesign
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>ArrayDesign objects are used during experiment checking to
+handle array design elements in a memory-efficient manner.</p>
+<h2><a name="accessor_methods" id="accessor_methods">Accessor
+methods</a></h2>
+<dl>
+<dt><strong><a name="item_set_accession" id=
+"item_set_accession">set_accession</a></strong></dt>
+<dd>
+<p>Setter method for the array accession number relating to this
+design.</p>
+</dd>
+<dt><strong><a name="item_get_accession" id=
+"item_get_accession">get_accession</a></strong></dt>
+<dd>
+<p>Getter method for the array accession number relating to this
+design.</p>
+</dd>
+<dt><strong><a name="item_set_adf_features" id=
+"item_set_adf_features">set_adf_features</a></strong></dt>
+<dd>
+<p>Setter method for a hashref with keys which are period-delim
+strings of the feature coordinates found in the array design
+associated with the file.</p>
+</dd>
+<dt><strong><a name="item_get_adf_features" id=
+"item_get_adf_features">get_adf_features</a></strong></dt>
+<dd>
+<p>Getter method for a hashref with keys which are period-delim
+strings of the feature coordinates found in the array design
+associated with the file.</p>
+</dd>
+<dt><strong><a name="item_set_adf_reporters" id=
+"item_set_adf_reporters">set_adf_reporters</a></strong></dt>
+<dd>
+<p>Setter method for a hashref with keys which are the reporter
+identifiers found in the array design associated with the file.</p>
+</dd>
+<dt><strong><a name="item_get_adf_reporters" id=
+"item_get_adf_reporters">get_adf_reporters</a></strong></dt>
+<dd>
+<p>Getter method for a hashref with keys which are the reporter
+identifiers found in the array design associated with the file.</p>
+</dd>
+<dt><strong><a name="item_set_adf_compseqs" id=
+"item_set_adf_compseqs">set_adf_compseqs</a></strong></dt>
+<dd>
+<p>Setter method for a hashref with keys which are the composite
+sequence identifiers found in the array design associated with the
+file.</p>
+</dd>
+<dt><strong><a name="item_get_adf_compseqs" id=
+"item_get_adf_compseqs">get_adf_compseqs</a></strong></dt>
+<dd>
+<p>Getter method for a hashref with keys which are the composite
+sequence identifiers found in the array design associated with the
+file.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2004.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Parser.html b/docs/ArrayExpress/Datafile/Parser.html
new file mode 100644
index 0000000..dbbc093
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Parser.html
@@ -0,0 +1,67 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Parser - a module handling Datafile
+parsing</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: ExperimentChecker.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Parser - a module handling Datafile
+parsing</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Parser;
+ my $parser = ArrayExpress::Datafile::Parser->new;
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>The ArrayExpress::Datafile module provides methods for parsing
+individual data files. This module provides a global handler for
+Datafile objects.</p>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/QT_list.html b/docs/ArrayExpress/Datafile/QT_list.html
new file mode 100644
index 0000000..377aa0e
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/QT_list.html
@@ -0,0 +1,248 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::QT_list.pm - a module handling
+QuantitationType information.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#quantitation_types">QUANTITATION TYPES</a></li>
+        <ul>
+
+                <li><a href="#qt_file_columns">QT file columns</a></li>
+        </ul>
+
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: QT_list.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::QT_list.pm - a module handling
+QuantitationType information.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::QT_list qw(get_QTs writeQTs)
+ my $QT_hash = get_QTs();
+ write_QTs($QT_hash);
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This is a module providing QuantitationType support information
+and subroutines for the Tab2MAGE and experiment checker
+package.</p>
+<hr />
+<h1><a name="quantitation_types" id=
+"quantitation_types">QUANTITATION TYPES</a></h1>
+<p>Known QuantitationTypes are listed in the QT_list.pm module
+included with this distribution. This file includes a simple
+tab-delimited text section with subsections specific to a given
+software/manufacturer type. Each subsection begins with the tag
+``<code>>>></code>''. There are several columns, but only
+the first is required:</p>
+<table class="example">
+<tr>
+<th>>>>QT software 1</th>
+</tr>
+<tr>
+<td>QT1 name</td>
+<td>QT1 datatype</td>
+<td>QT1 scale</td>
+<td>QT1 subclass</td>
+<td>QT1 is_background</td>
+<td>QT1 target (confidence indicators)</td>
+<td>QT1 channel</td>
+<td>QT1 description</td>
+</tr>
+<tr>
+<td>QT2 name</td>
+<td>QT2 datatype</td>
+<td>QT2 scale</td>
+<td>QT2 subclass</td>
+<td>QT2 is_background</td>
+<td>QT2 target (confidence indicators)</td>
+<td>QT2 channel</td>
+<td>QT2 description</td>
+</tr>
+<tr>
+<td>QT3 name</td>
+<td>QT3 datatype</td>
+<td>QT3 scale</td>
+<td>QT3 subclass</td>
+<td>QT3 is_background</td>
+<td>QT3 target (confidence indicators)</td>
+<td>QT3 channel</td>
+<td>QT3 description</td>
+</tr>
+<tr>
+<td>QT4 name...</td>
+<td> </td>
+<td> </td>
+<td> </td>
+<td> </td>
+<td> </td>
+<td> </td>
+<td> </td>
+</tr>
+<tr>
+<th>>>>QT software 2</th>
+</tr>
+<tr>
+<td>QT5 name</td>
+<td>QT5 datatype</td>
+<td>QT5 scale</td>
+<td>QT5 subclass</td>
+<td>QT5 is_background</td>
+<td>QT5 target (confidence indicators)</td>
+<td>QT5 channel</td>
+<td>QT5 description</td>
+</tr>
+<tr>
+<td>QT6 name...</td>
+<td> </td>
+<td> </td>
+<td> </td>
+<td> </td>
+<td> </td>
+<td> </td>
+<td> </td>
+</tr>
+</table>
+<p>where QT1, QT2 and so on are the quantitation types. Note that
+for the resulting MAGE identifiers to have the correct namespace,
+the ``>>>QT software'' strings should contain the
+namespace in square brackets, for example:</p>
+<pre>
+ >>>Feature Extraction Software[Agilent Technologies]
+</pre>
+<p>Note also that to add to a pre-existing set of quantitation
+types from a given software, this initial string
+<strong>must</strong> match that included in the Tab2MAGE package.
+Currently included software types are as follows:</p>
+<pre>
+ AIDA[Raytest]
+ Affymetrix[Affymetrix]
+ AppliedBiosystems
+ ArrayGauge[FUJIFILM]
+ ArrayVision[Imaging Research]
+ BZScan[TAGC ERM206]
+ BlueFuse[BlueGnome]
+ ChipSkipper[EMBL]
+ CodeLink[Motorola Life Sciences]
+ CodeLink Expression Analysis[GE Healthcare]
+ Feature Extraction Software[Agilent Technologies]
+ GEMTools[Incyte Genomics]
+ GLEAMS[NuTec Sciences]
+ GenePix[Axon Instruments]
+ GeneTAC
+ ImaGene[BioDiscovery]
+ NimbleScan[NimbleGen Systems]
+ QuantArray[PerkinElmer]
+ ScanAlyze[Stanford University]
+ ScanArray Express[PerkinElmer]
+ SpotFinder[TIGR]
+ UCSF Spot
+ Uni of Toronto in-house analysis software
+ Tab2MAGE_FGEM[ebi.ac.uk]
+</pre>
+<p>Each file may contain QT information from any number of software
+vendors. The contents of each column are described below:</p>
+<h2><a name="qt_file_columns" id="qt_file_columns">QT file
+columns</a></h2>
+<dl>
+<dt><strong><a name="item_name" id=
+"item_name">name</a></strong></dt>
+<dd>
+<p>The name of the QuantitationType. This corresponds to the exact
+string found in the data files.</p>
+</dd>
+<dt><strong><a name="item_datatype" id=
+"item_datatype">datatype</a></strong></dt>
+<dd>
+<p>The DataType OntologyEntry to be used (MGED Ontology). Default:
+float</p>
+</dd>
+<dt><strong><a name="item_scale" id=
+"item_scale">scale</a></strong></dt>
+<dd>
+<p>The Scale OntologyEntry to be used (MGED Ontology). Default:
+linear_scale</p>
+</dd>
+<dt><strong><a name="item_subclass" id=
+"item_subclass">subclass</a></strong></dt>
+<dd>
+<p>The QuantitationType subclass to use (e.g., MeasuredSignal,
+Error etc.). Default: SpecializedQuantitationType</p>
+</dd>
+<dt><strong><a name="item_is_background" id=
+"item_is_background">is_background</a></strong></dt>
+<dd>
+<p>Boolean flag (0 or 1) indicating whether the QT is background.
+Default: 0</p>
+</dd>
+<dt><strong><a name="item_confidence_indicator_target" id=
+"item_confidence_indicator_target">confidence indicator
+target</a></strong></dt>
+<dd>
+<p>Only used for ConfidenceIndicator QTs (Error, PValue, and
+ExpectedValue). Indicates the target QT name to which the
+ConfidenceIndicator applies. Defaults to undefined.</p>
+</dd>
+<dt><strong><a name="item_channel" id=
+"item_channel">channel</a></strong></dt>
+<dd>
+<p>The channel to be associated with the QT. Note that QTs can only
+have one channel; this is a current limitation of the MAGE object
+model. QTs linked to more than one channel in the real world (e.g.
+ratios) should omit the channel value here.</p>
+</dd>
+<dt><strong><a name="item_description" id=
+"item_description">description</a></strong></dt>
+<dd>
+<p>A plain-text description of the QT.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.
+Particular credit goes to Ele Holloway, who was responsible for
+curating the lists of QuantitationTypes included with this
+script.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/MAGETAB.html b/docs/ArrayExpress/MAGETAB.html
new file mode 100644
index 0000000..5b6cb6f
--- /dev/null
+++ b/docs/ArrayExpress/MAGETAB.html
@@ -0,0 +1,286 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::MAGETAB - A parser class for MAGE-TAB
+documents.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#methods">METHODS</a></li>
+        <li><a href="#protocol_accessions">PROTOCOL_ACCESSIONS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: MAGETAB.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::MAGETAB - A parser class for MAGE-TAB
+documents.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::MAGETAB;
+ my $magetab = ArrayExpress::MAGETAB->new({
+     idf              => $idf_file,
+     target_directory => $dir,
+     expt_accession   => $accn,
+ });
+</pre>
+<pre>
+ $magetab->write_mageml();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module acts as a front end to the MAGE-TAB parsing API.
+Parser objects are instantiated with a number of attributes to
+control how the MAGE-TAB document is parsed. Support is only
+provided for IDF and SDRF documents at present; it is anticipated
+that the parser will be extended to support ADF at a later
+date.</p>
+<p>Currently the parser is built using a MAGEv1.1 object model to
+store the MAGE-TAB metadata. It is envisioned that this dependence
+on the Bio::MAGE modules may be removed once a full MAGE-TAB object
+model is agreed upon by the community.</p>
+<p>To simplify the process of data submission, ArrayExpress has
+introduced a new flavour of MAGE-TAB in which the IDF and SDRF
+sections are combined into a single worksheet. This parser supports
+both MAGE-TAB v1.1 documents (with separate IDF and SDRF) and these
+combined documents.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_new" id=
+"item_new"><code>new</code></a></strong></dt>
+<dd>
+<p>Object constructor. This recognises the following
+attributes:</p>
+</dd>
+<dd>
+<dl>
+<dt><strong><a name="item_idf" id=
+"item_idf"><code>idf</code></a></strong></dt>
+<dd>
+<p>The path of the IDF file with which to start parsing.</p>
+</dd>
+<dt><strong><a name="item_magetab_doc" id=
+"item_magetab_doc"><code>magetab_doc</code></a></strong></dt>
+<dd>
+<p>The path of a combined IDF+SDRF file to parse.</p>
+</dd>
+<dt><strong><a name="item_output_file" id=
+"item_output_file"><code>output_file</code></a></strong></dt>
+<dd>
+<p>The name of the output MAGE-ML file.</p>
+</dd>
+<dt><strong><a name="item_namespace" id=
+"item_namespace"><code>namespace</code></a></strong></dt>
+<dd>
+<p>The namespace to use in MAGE identifier creation.</p>
+</dd>
+<dt><strong><a name="item_authority" id=
+"item_authority"><code>authority</code></a></strong></dt>
+<dd>
+<p>The authority to use in MAGE identifier creation.</p>
+</dd>
+<dt><strong><a name="item_expt_accession" id=
+"item_expt_accession"><code>expt_accession</code></a></strong></dt>
+<dd>
+<p>The accession number assigned to the experiment.</p>
+</dd>
+<dt><strong><a name="item_target_directory" id=
+"item_target_directory"><code>target_directory</code></a></strong></dt>
+<dd>
+<p>The directory into which to write the output files.</p>
+</dd>
+<dt><strong><a name="item_source_directory" id=
+"item_source_directory"><code>source_directory</code></a></strong></dt>
+<dd>
+<p>The directory which contains all data and SDRF files.</p>
+</dd>
+<dt><strong><a name="item_is_standalone" id=
+"item_is_standalone"><code>is_standalone</code></a></strong></dt>
+<dd>
+<p>A flag indicating whether the script is able to connect to
+ArrayExpress to retrieve array design information. It is sometimes
+desirable to skip these downloads, which can be quite large.</p>
+</dd>
+<dt><strong><a name="item_qt_filename" id=
+"item_qt_filename"><code>qt_filename</code></a></strong></dt>
+<dd>
+<p>QuantitationType file. This option allows you to specify a
+custom QuantitationType definition file to override those defined
+as part of the Tab2MAGE package. See <a href=
+"Datafile/QT_list.html">the ArrayExpress::Datafile::QT_list
+manpage</a> for more information.</p>
+</dd>
+<dt><strong><a name="item_include_default_qts" id=
+"item_include_default_qts"><code>include_default_qts</code></a></strong></dt>
+<dd>
+<p>This option can be used in conjunction with <a href=
+"#item_qt_filename"><code>qt_filename</code></a> to indicate that
+the QuantitationType listing from the Tab2MAGE package itself
+should be included in the lists of known QuantitationTypes used in
+data file parsing. The default behaviour is to deactivate these
+known QTs if a custom QT file is to be used.</p>
+</dd>
+<dt><strong><a name="item_keep_all_qts" id=
+"item_keep_all_qts"><code>keep_all_qts</code></a></strong></dt>
+<dd>
+<p>A flag indicating whether unrecognised QuantitationTypes in data
+files should be kept or not. The default behaviour is to strip
+unrecognised columns out of the data files.</p>
+</dd>
+<dt><strong><a name="item_reporter_prefix" id=
+"item_reporter_prefix"><code>reporter_prefix</code></a></strong></dt>
+<dd>
+<p>The prefix to be used during Reporter identifier construction.
+This prefix is prepended to the identifiers listed in the data
+files.</p>
+</dd>
+<dt><strong><a name="item_compseq_prefix" id=
+"item_compseq_prefix"><code>compseq_prefix</code></a></strong></dt>
+<dd>
+<p>The prefix to be used during CompositeElement
+(CompositeSequence) identifier construction. This prefix is
+prepended to the identifiers listed in the data files.</p>
+</dd>
+<dt><strong><a name="item_protocol_accession_service" id=
+"item_protocol_accession_service"><code>protocol_accession_service</code></a></strong></dt>
+<dd>
+<p>A code reference used to reassign protocol accessions. See
+<em>PROTOCOL_ACCESSIONS</em>, below.</p>
+</dd>
+<dt><strong><a name="item_protocol_accession_prefix" id=
+"item_protocol_accession_prefix"><code>protocol_accession_prefix</code></a></strong></dt>
+<dd>
+<p>The prefix to be used for protocol accession creation, when the
+autosubmissions system is in use. See <em>PROTOCOL_ACCESSIONS</em>,
+below.</p>
+</dd>
+<dt><strong><a name="item_keep_protocol_accns" id=
+"item_keep_protocol_accns"><code>keep_protocol_accns</code></a></strong></dt>
+<dd>
+<p>A flag indicating whether the protocols in the IDF should be
+assigned new accession numbers. This option overrides <a href=
+"#item_protocol_accession_service"><code>protocol_accession_service</code></a>.</p>
+</dd>
+<dt><strong><a name="item_use_plain_text" id=
+"item_use_plain_text"><code>use_plain_text</code></a></strong></dt>
+<dd>
+<p>Some file formats are only supported in their native forms by
+ArrayExpress. Nonetheless, this package can parse some of these
+data formats into tab-delimited representations fully encoded in
+the MAGE-ML document (examples include Nimblegen data, Affymetrix
+CEL files).</p>
+</dd>
+<dt><strong><a name="item_skip_datafiles" id=
+"item_skip_datafiles"><code>skip_datafiles</code></a></strong></dt>
+<dd>
+<p>This option tells the parser to skip attempting to read the data
+files referenced by a given MAGE-TAB document, and instead attempts
+to generate MAGE in their absence. This option is particularly
+useful for unsupported data file formats.</p>
+</dd>
+<dt><strong><a name="item_ignore_size_limits" id=
+"item_ignore_size_limits"><code>ignore_size_limits</code></a></strong></dt>
+<dd>
+<p>The Tab2MAGE configuration file allows the user to set maximum
+data file sizes for parsing and web download, to provide some
+protection from overloading the system in a production pipeline
+setting. To temporarily ignore these size limits, use this
+option.</p>
+</dd>
+<dt><strong><a name="item_in_relaxed_mode" id=
+"item_in_relaxed_mode"><code>in_relaxed_mode</code></a></strong></dt>
+<dd>
+<p>A flag indicating whether to allow minor errors during parsing.
+At the moment the only errors which are ignored by this option are
+<code>Term Source REF</code>, <code>Protocol REF</code>,
+<code>Parameter Value []</code> and <code>Factor Value []</code>
+columns which reference Names which have not been defined in the
+IDF.</p>
+</dd>
+<dt><strong><a name="item_clobber" id=
+"item_clobber"><code>clobber</code></a></strong></dt>
+<dd>
+<p>Flag indicating whether or not to overwrite existing files
+without prompting the user.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="item_parse" id=
+"item_parse"><code>parse</code></a></strong></dt>
+<dd>
+<p>Starts the MAGE-TAB parse and loads the document into
+memory.</p>
+</dd>
+<dt><strong><a name="item_write_mageml" id=
+"item_write_mageml"><code>write_mageml</code></a></strong></dt>
+<dd>
+<p>Writes out MAGE-ML corresponding to the input MAGE-TAB document.
+If the MAGE-TAB has not yet been parsed, <a href=
+"#item_parse"><code>parse()</code></a> is called automatically.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="protocol_accessions" id=
+"protocol_accessions">PROTOCOL_ACCESSIONS</a></h1>
+<p>The parser provides a set of callbacks which can be used to
+assign MAGE-TAB Protocol Names to unique accessions at the point of
+parsing. If the autosubmissions system has been set up and
+configured, then the parser will default to using that mechanism to
+assign protocol accessions. If you wish to use your own service,
+you may use the <a href=
+"#item_protocol_accession_service"><code>protocol_accession_service</code></a>
+and <a href=
+"#item_protocol_accession_prefix"><code>protocol_accession_prefix</code></a>
+attributes to control this. The <a href=
+"#item_protocol_accession_service"><code>protocol_accession_service</code></a>
+should point to a code reference which will accept two arguments:
+(a) the Protocol Name as given in the IDF, and (b) the experiment
+accession. The code reference should return a unique accession
+which will then be assigned to the protocol in the output
+MAGE-ML.</p>
+<p>If the autosubmissions system is to be used, the <a href=
+"#item_protocol_accession_prefix"><code>protocol_accession_prefix</code></a>
+attribute must be set, e.g. to ``P-MTAB-''.</p>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2008.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/MAGETAB/Checker.html b/docs/ArrayExpress/MAGETAB/Checker.html
new file mode 100644
index 0000000..ac251cd
--- /dev/null
+++ b/docs/ArrayExpress/MAGETAB/Checker.html
@@ -0,0 +1,215 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::MAGETAB::Checker - MAGE-TAB experiment
+checking.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#options">OPTIONS</a></li>
+        <li><a href="#tests">TESTS</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../MAGETAB_logo.png" border="0"
+height="50" alt="MAGE-TAB logo" /></a></td>
+<td class="pagetitle">Module detail: Checker.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::MAGETAB::Checker - MAGE-TAB experiment
+checking.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::MAGETAB::Checker;
+ 
+ my $checker = ArrayExpress::MAGETAB::Checker->new({
+     idf => $magetab_idf,
+ });
+ 
+ $checker->check();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module provides the basic operations needed for checking a
+MAGE-TAB document and associated data files.</p>
+<hr />
+<h1><a name="options" id="options">OPTIONS</a></h1>
+<p>The following options must be used in addition to those provided
+by the parent class (see <a href=
+"../Curator/ExperimentChecker.html">the
+ArrayExpress::Curator::ExperimentChecker manpage</a>):</p>
+<dl>
+<dt><strong><a name="item_idf" id=
+"item_idf"><code>idf</code></a></strong></dt>
+<dd>
+<p>The MAGE-TAB IDF filename to be checked.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="tests" id="tests">TESTS</a></h1>
+<p>The following tests are performed by this module, with output
+printed to the error and/or report logs:</p>
+<dl>
+<dt><strong><a name="item_idf_contents" id=
+"item_idf_contents"><strong>IDF Contents</strong></a></strong></dt>
+<dd>
+<p>Checks that at least one of each of the following items are
+present in the IDF, warning the user if any are missing:</p>
+</dd>
+<dd>
+<pre>
+ Experimental Factor
+ Contact
+ Publication
+ Protocol
+ SDRF
+</pre></dd>
+<dt><strong><a name="item_experiment_info" id=
+"item_experiment_info"><strong>Experiment
+info</strong></a></strong></dt>
+<dd>
+<p>Warns the user if any of the following pieces of experiment
+annotation are missing:</p>
+</dd>
+<dd>
+<pre>
+ Experiment Design
+ Release Date
+ Title
+ Description
+</pre></dd>
+<dt><strong><a name="item_term_sources" id=
+"item_term_sources"><strong>Term Sources</strong></a></strong></dt>
+<dd>
+<p>Checks all instances of Term Source REFs, and warns the user if
+an undeclared Term Source Name has been used. Also notifies the
+user if a Term Source declared in the IDF has not been used in the
+SDRF.</p>
+</dd>
+<dt><strong><a name="item_experimental_factors" id=
+"item_experimental_factors"><strong>Experimental
+Factors</strong></a></strong></dt>
+<dd>
+<p>Checks all SDRF Factor Value columns, and warns the user if an
+undeclared Experimental Factor Name has been used. Also notifies
+the user if a Experimental Factor declared in the IDF has not been
+used in the SDRF.</p>
+</dd>
+<dt><strong><a name="item_undeclared_experimental_factors" id=
+"item_undeclared_experimental_factors"><strong>Undeclared
+Experimental Factors</strong></a></strong></dt>
+<dd>
+<p>Checks all material characteristics against the factors
+described by the document, alerting the user if any such
+characteristics vary during the experiment without having been
+declared as an experimental factor.</p>
+</dd>
+<dt><strong><a name="item_source_characteristics" id=
+"item_source_characteristics"><strong>Source
+Characteristics</strong></a></strong></dt>
+<dd>
+<p>Checks that each Source material has at least some annotation.
+This is not, however, a full test of MIAME compliance.</p>
+</dd>
+<dt><strong><a name="item_pubmed_id" id=
+"item_pubmed_id"><strong>PubMed ID</strong></a></strong></dt>
+<dd>
+<p>Warns the user if a non-numeric PubMed ID has been entered.</p>
+</dd>
+<dt><strong><a name="item_submission_and_release_dates" id=
+"item_submission_and_release_dates"><strong>Submission and release
+dates</strong></a></strong></dt>
+<dd>
+<p>Warns the user if experiment or release dates are in an
+incorrect format.</p>
+</dd>
+<dt><strong><a name="item_submitter" id=
+"item_submitter"><strong>Submitter</strong></a></strong></dt>
+<dd>
+<p>Checks that a submitter contact has been provided, and that
+their last name and email address have both been included in the
+IDF.</p>
+</dd>
+<dt><strong><a name="item_protocols" id=
+"item_protocols"><strong>Protocols</strong></a></strong></dt>
+<dd>
+<p>Checks that all protocols referenced in the Hybridization
+spreadsheet section are declared in the Protocol section (and vice
+versa).</p>
+</dd>
+<dt><strong><a name="item_protocol_description_length" id=
+"item_protocol_description_length"><strong>Protocol description
+length</strong></a></strong></dt>
+<dd>
+<p>Warns if any of the protocol texts seem too brief.</p>
+</dd>
+<dt><strong><a name="item_protocol_usage" id=
+"item_protocol_usage"><strong>Protocol
+usage</strong></a></strong></dt>
+<dd>
+<p>Checks that a protocol has been attached to each step of the
+experiment (biomaterial treatments, hybridization, scanning and
+normalization).</p>
+</dd>
+<dt><strong><a name="item_parameters" id=
+"item_parameters"><strong>Parameters</strong></a></strong></dt>
+<dd>
+<p>Checks that all parameters referenced in the Hybridization
+spreadsheet section are declared in the Protocol section (and vice
+versa).</p>
+</dd>
+<dt><strong><a name="item_files" id=
+"item_files"><strong>Files</strong></a></strong></dt>
+<dd>
+<p>Confirms that each data file is associated with an array design
+(i.e., Array Design REF or Array Design File), and at least one
+experimental factor value.</p>
+</dd>
+<dt><strong><a name="item_experiment_design_graph" id=
+"item_experiment_design_graph"><strong>Experiment Design
+Graph</strong></a></strong></dt>
+<dd>
+<p>Creates the links between sample, extract, labeled extract and
+hybridization, and writes a biomaterials log file listing the
+numbers of each. If the Graphviz software is installed
+(http://www.graphviz.org) then the script will use the 'dot'
+program to produce a PNG format graph showing how the various
+components relate to each other.</p>
+</dd>
+</dl>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2007.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/MAGETAB_logo.png b/docs/MAGETAB_logo.png
new file mode 100644
index 0000000..dbc8baa
Binary files /dev/null and b/docs/MAGETAB_logo.png differ
diff --git a/docs/MAGETAB_logo_small.png b/docs/MAGETAB_logo_small.png
new file mode 100644
index 0000000..9f19870
Binary files /dev/null and b/docs/MAGETAB_logo_small.png differ
diff --git a/docs/T2M_logo.png b/docs/T2M_logo.png
new file mode 100644
index 0000000..72d844b
Binary files /dev/null and b/docs/T2M_logo.png differ
diff --git a/docs/T2M_logo_small.png b/docs/T2M_logo_small.png
new file mode 100644
index 0000000..35f704f
Binary files /dev/null and b/docs/T2M_logo_small.png differ
diff --git a/docs/aelogo.png b/docs/aelogo.png
new file mode 100644
index 0000000..29f140c
Binary files /dev/null and b/docs/aelogo.png differ
diff --git a/docs/aesubs.html b/docs/aesubs.html
new file mode 100644
index 0000000..dd64d2d
--- /dev/null
+++ b/docs/aesubs.html
@@ -0,0 +1,182 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+  <head>
+    <title>ArrayExpress submissions</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+    <link rel="stylesheet" href="style.css">
+  </head>
+
+  <body>
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100"><a href="../index.html"><img src="T2M_logo.png" border="0" height="50" alt="Tab2MAGE logo"></a></td>
+	    <td class="pagetitle">ArrayExpress submissions</td>
+	  </tr>
+      </table>
+    </div>
+
+    <div CLASS="section">
+      <table class="layout">
+	<tr>
+	  <td class="layout">
+	    
+	    <ul>
+	      <li><a href="#routes">Submission routes</a></li>
+	      <li><a href="#spreadsheet">Spreadsheet details</a></li>
+	      <li><a href="#submission">Data upload instructions</a></li>
+	    </ul>
+
+	    <p class="subhead">See also:</p>
+
+	    <ul>
+	      <li><a href="spreadsheet.html">Constructing a Tab2MAGE spreadsheet</a></li>
+	      <li><a href="datafiles.html">Supported data file formats</a></li>
+	      <li><a href="http://www.ebi.ac.uk/miamexpress/help/tab2mage_help.html">ArrayExpress spreadsheet submission help</a>
+	    </ul>
+	  </td>
+	  <td class="layout" valign="top">
+	    <div CLASS="imghead">
+	      <a href="http://www.ebi.ac.uk/arrayexpress/">
+		<img src="aelogo.png" border="0" alt="ArrayExpress logo">
+	      </a>
+	    </div>
+	  </td>
+	</tr>
+      </table>
+    </div>
+
+    <p class="text">For further help with data submissions to ArrayExpress, please contact the
+      <script type="text/javascript">
+	<!--
+	var aname = "miamexpress@ebi.ac.uk";
+	document.write("<a href='" + "mail" + "to:" + aname + "'>ArrayExpress curators<" + "/a>")
+	//-->
+      </script>
+    </p>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="routes">Submission routes</a></div>
+      
+      <p class="text">Tab2MAGE can be used to submit microarray data
+      to ArrayExpress in one of two ways. For advanced users with a
+      need to submit large volumes of data, Tab2MAGE can be used to
+      set up a MAGE-ML pipeline to ArrayExpress. For more casual use,
+      it is recommended that submitters upload a completed Tab2MAGE
+      spreadsheet alongside their data files, and the ArrayExpress
+      curators will process it for you.</p>
+
+      <p class="text">Submitters wishing to set up a MAGE-ML pipeline
+      to ArrayExpress should contact the
+	<script type="text/javascript">
+	<!--
+	var aname = "miamexpress@ebi.ac.uk";
+	document.write("<a href='" + "mail" + "to:" + aname + "'>curators<" + "/a>")
+	//-->
+      </script>
+	to discuss their requirements, and read this web page: <a
+	href="http://www.ebi.ac.uk/~ele/ext/submitter.html">Instructions
+	for submitters of MAGE-ML</a>.</p>
+
+      <p class="text"><em>For data submissions using the spreadsheet,
+      please read the rest of this page.</em></p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="spreadsheet">Spreadsheet details</a></div>
+      
+      <p class="text">There are a few things to bear in mind
+      when using the spreadsheet submission route to ArrayExpress:</p>
+
+      <ul>
+
+	<li class="text">Please leave the experiment <a
+	href="detail.html#accession">accession</a> field
+	blank. <em>The ArrayExpress curators will assign your
+	experiment an accession once they are satisfied with your
+	submission.</em> The experiment <a
+	href="detail.html#domain">domain</a> field should contain the
+	text "ebi.ac.uk" or be left blank.</li>
+	
+	<li class="text">You may re-use previously submitted protocols
+	by entering their accession numbers directly into the
+	spreadsheet. You may retrieve these accessions using the
+	ArrayExpress web page. If your protocols have not yet been
+	loaded into ArrayExpress, please contact the
+	  <script type="text/javascript">
+	    <!--
+	    var aname = "miamexpress@ebi.ac.uk";
+	    document.write("<a href='" + "mail" + "to:" + aname + "'>curators<" + "/a>")
+	    //-->
+	  </script>
+	  to obtain the accession numbers.
+	</li>
+	
+	<li class="text">For new protocols, please
+	generate your own protocol accession numbers in the form
+	"P-TABM-{yourname}{number}", for example P-TABM-rayner1,
+	P-TABM-rayner2 and so on. These accessions will be reassigned
+	during curation to fit the standard format.</li>
+
+    </ul>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="submission">Upload instructions</a></div>
+
+      <p class="text">Once you have finished preparing your
+      spreadsheet, please upload it with your data files to the
+      ArrayExpress <a
+      href="http://www.ebi.ac.uk/cgi-bin/microarray/tab2mage.cgi">Tab2MAGE
+      submissions website</a>.</p>
+
+      <p class="text">To submit data using this website:
+      <ol>
+	
+	<li class="text">Create an account for yourself and log in.</li>
+
+	<li class="text">Click 'Create experiment' to start your submission.</li>
+
+	<li class="text">Enter your experiment name. If you want to generate a
+	template spreadsheet then select one or more terms from each
+	of the lists provided to describe your experiment
+	(optional). Click 'Save experiment'.</li>
+
+	<li class="text">Click 'Generate template' if you want to use the template
+	(optional).</li>
+
+	<li class="text">Click 'Upload files' then upload your completed
+	spreadsheet and one or more zipped archives containing all
+	your data files.</li>
+
+	<li class="text">Click 'Submit experiment' to send this submission to us
+	for curation. We will contact you with any questions and will
+	email you an accession number when your spreadsheet has been
+	processed and loaded into ArrayExpress.</li>
+
+      </ol>
+    </p>
+
+      <p class="text">If you have any questions please email the curation team at
+      <a href="mailto:miamexpress at ebi.ac.uk?subject=Tab2MAGE submission">miamexpress at ebi.ac.uk</a>.
+	If you are emailing about a specific submission tell us your
+	username and experiment name in the email.</p>
+
+    </div>
+
+    <hr>
+    <a href="http://sourceforge.net">
+      <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" />
+    </a>
+    <br>
+<!-- Created: Thu Nov 24 14:18:20 GMT 2005 -->
+<!-- hhmts start -->
+Last modified: Wed Sep 19 15:55:53 BST 2007
+<!-- hhmts end -->
+  </body>
+</html>
diff --git a/docs/datafiles.html b/docs/datafiles.html
new file mode 100644
index 0000000..3e87eac
--- /dev/null
+++ b/docs/datafiles.html
@@ -0,0 +1,465 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+  <head>
+    <title>Supported data files</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+    <link rel="stylesheet" href="style.css">
+  </head>
+
+  <body>
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100"><a href="../index.html"><img src="T2M_logo.png" border="0" height="50" alt="Tab2MAGE logo"></a></td>
+	    <td class="pagetitle">Supported data files</td>
+	  </tr>
+      </table>
+    </div>
+
+    <p class="text">Data file support in Tab2MAGE can be divided into
+    two categories: Affymetrix data files, and everything
+    else. Non-Affymetrix data must be supplied as plain ASCII
+    tab-delimited text, and the column headings must be left
+    intact. Ideally, you should be able to use the raw unprocessed
+    data files from any of the supported software types without having
+    to edit the data files in any way. Tab2MAGE uses the column
+    headings within the file to identify which kind of file it is
+    dealing with, and what the quantitation types are. The script will
+    reformat recognized file types into MetaColumn-MetaRow format, and
+    then strip the feature coordinates (or reporter identifiers) and
+    the column headings, in the process generating the necessary MAGE
+    objects to describe the data.</p>
+
+    <p class="text">The following formats are supported:</p>
+
+    <ul>
+      <li><a href="#raw" class="subhead">Unprocessed (raw) data files</a></li>
+      <ul>
+	<li><a href="#generic">Generic</a></li>
+	<li><a href="#affymetrix">Affymetrix</a></li>
+	<li><a href="#genepix">GenePix</a></li>
+	<li><a href="#agilent">Agilent</a></li>
+	<li><a href="#scanalyze">ScanAlyze</a></li>
+	<li><a href="#scanarray">ScanArray</a></li>
+	<li><a href="#scanarray">QuantArray</a></li>
+	<li><a href="#arrayvision">Arrayvision</a></li>
+	<li><a href="#spotfinder">Spotfinder</a></li>
+	<li><a href="#bluefuse">BlueFuse</a></li>
+	<li><a href="#ucsfspot">UCSF Spot</a></li>
+	<li><a href="#nimblescan">NimbleScan</a></li>
+	<li><a href="#appliedbiosystems">Applied Biosystems</a></li>
+	<li><a href="#codelink">CodeLink</a></li>
+	<li><a href="#illumina">Illumina</a></li>
+	<li><a href="#imagene">Imagene</a></li>
+	<li><a href="#csirospot">CSIRO Spot</a></li>
+      </ul>
+      <li><a href="#normalized" class="subhead">Normalized data files</a></li>
+      <ul>
+	<li><a href="#reporter">Generic</a></li>
+	<li><a href="#affynorm">Affymetrix</a></li>
+	<li><a href="#illumina">Illumina</a></li>
+      </ul>
+      <li><a href="#final_data_matrix" class="subhead">Final data matrix files</a></li>
+    </ul>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="raw">Unprocessed (raw) data files</a></div>
+      
+      <p class="text">The following list gives a brief overview of how
+      Tab2MAGE recognizes different file formats. In each case, the
+      data file row containing the column headings is identified by
+      matching it to these sets of known column headings.</p>
+
+      <p class="text"><a name="generic" class="subhead">Generic</a>
+      <br><a
+      href="http://www.ebi.ac.uk/miamexpress/help/datafile_help.html">
+      MetaColumn/MetaRow</a> format files are recognized using the
+      following column headings:</p>
+
+      <table class="leftexample">
+	  <tr><td>MetaColumn</td><td>MetaRow</td><td>Column</td><td>Row</td></tr>
+      </table>
+
+      <p class="text"><a name="affymetrix"
+      class="subhead">Affymetrix</a> <br>Tab2MAGE recognizes and
+      parses CEL and EXP files using both the old GDAC formats and the
+      newer GCOS/XDA formats. These file formats are detected using
+      the Affymetrix data file parser incorporated into the Tab2MAGE
+      package. See below for notes on <a href="#affynorm">Affymetrix
+      normalized data file formats</a>.</p>
+
+      <p class="text"><a name="genepix" class="subhead">GenePix</a>
+      <br>GenePix format files are recognized using the following column headings:</p>
+
+      <table class="leftexample">
+	  <tr><td>Block</td><td>Column</td><td>Row</td><td>X</td><td>Y</td></tr>
+      </table>
+
+      <p class="text"><a name="agilent" class="subhead">Agilent</a>
+      <br>A file containing these headings is recognized as Agilent format file:</p>
+
+      <table class="leftexample">
+	  <tr><td>Row</td><td>Col</td><td>PositionX</td><td>PositionY</td></tr>
+      </table>
+
+      <p class="text"><a name="scanalyze"
+      class="subhead">ScanAlyze</a> <br>The following column headings
+      are recognized as being from a ScanAlyze format file:</p>
+
+      <table class="leftexample">
+	  <tr><td>GRID</td><td>COL</td><td>ROW</td><td>LEFT</td><td>TOP</td><td>RIGHT</td><td>BOT</td></tr>
+      </table>
+
+      <p class="text"><a name="scanarray"
+      class="subhead">ScanArray/QuantArray</a> <br>ScanArray Express
+      files are recognized from the following headings:</p>
+
+      <table class="leftexample">
+	  <tr><td>Array Column</td><td>Array Row</td><td>Spot Column</td><td>Spot Row</td><td>X</td><td>Y</td></tr>
+      </table>
+
+      <p class="text">while the older QuantArray format has these headings:</p>
+
+      <table class="leftexample">
+	  <tr><td>Array Column</td><td>Array Row</td><td>Column</td><td>Row</td></tr>
+      </table>
+
+      <p class="text"><a name="arrayvision"
+      class="subhead">ArrayVision</a> <br>The following column
+      headings are recognized as indicating an ArrayVision format
+      file:</p>
+
+      <table class="leftexample">
+	  <tr><td>Primary</td><td>Secondary</td></tr>
+      </table>
+
+      <p class="text">Newer "lg2" ArrayVision files are identified by
+      the following column headings:</p>
+
+      <table class="leftexample">
+	  <tr><td>Spot labels</td></tr>
+      </table>
+
+      <p class="text"><a name="spotfinder"
+      class="subhead">Spotfinder</a> <br>Spotfinder files are
+      recognized by the following column headings:</p>
+
+      <table class="leftexample">
+	  <tr><td>MC</td><td>MR</td><td>SC</td><td>SR</td></tr>
+      </table>
+
+      <p class="text"><a name="bluefuse" class="subhead">BlueFuse</a>
+      <br>A file containing the following headings is recognized as a
+      BlueFuse file:</p>
+
+      <table class="leftexample">
+	  <tr><td>COL</td><td>ROW</td><td>SUBGRIDCOL</td><td>SUBGRIDROW</td></tr>
+      </table>
+
+      <p class="text"><a name="ucsfspot"
+      class="subhead">UCSF Spot</a> <br>UCSF Spot files are
+      recognized by the following column headings:</p>
+
+      <table class="leftexample">
+	  <tr><td>Arr-colx</td><td>Arr-coly</td><td>Spot-colx</td><td>Spot-coly</td></tr>
+      </table>
+
+      <p class="text"><a name="nimblescan"
+      class="subhead">NimbleScan</a> <br>NimbleScan files (Feature,
+      Probe and Pair) all contain the following headings:</p>
+
+      <table class="leftexample">
+	  <tr><td>PROBE_ID</td><td>X</td><td>Y</td></tr>
+      </table>
+
+      <p class="text"><a name="appliedbiosystems"
+      class="subhead">Applied Biosystems</a> <br>Files generated by
+      Applied Biosystems software have the following headings:</p>
+
+      <table class="leftexample">
+	  <tr><td>Probe_ID</td><td>Gene_ID</td></tr>
+      </table>
+
+      <p class="text"><a name="codelink" class="subhead">CodeLink</a>
+      <br>CodeLink Expression Analysis files are identified using the
+      following:</p>
+
+      <table class="leftexample">
+	  <tr><td>Logical_row</td><td>Logical_col</td><td>Center_X</td><td>Center_Y</td></tr>
+      </table>
+
+      <p class="text"><a name="imagene" class="subhead">ImaGene</a>
+      <br>ImaGene files are recognized using the following columns:</p>
+
+      <table class="leftexample">
+	<tr><td>Meta Column</td><td>Meta Row</td><td>Column</td><td>Row</td><td>Field</td><td>Gene ID</td></tr>
+      </table>
+
+      <p class="text">The ImaGene 3.0 format is also supported:</p>
+
+      <table class="leftexample">
+	<tr><td>Meta_col</td><td>Meta_row</td><td>Sub_col</td><td>Sub_row</td><td>Name</td><td>Selected</td></tr>
+      </table>
+
+      <p class="text"><a name="csirospot" class="subhead">CSIRO Spot</a>
+      <br>CSIRO Spot files contain the following columns:</p>
+
+      <table class="leftexample">
+	<tr><td>grid_c</td><td>grid_r</td><td>spot_c</td><td>spot_r</td><td>indexs</td></tr>
+      </table>
+
+      <p class="text">Obviously, this method of determining which file type is being processed is not infallible. You are therefore encouraged to test your data files with Tab2MAGE and report any problems to
+	<script type="text/javascript">
+	  <!--
+	  var uname = "rayner";
+	  var dname = "ebi";
+	  var hname = "ac.uk"
+	  var linktext = "the author";
+	  document.write("<a href='" + "mail" + "to:" + uname + "@" + dname
+	  + "." + hname + "'>" + linktext + "<" + "/a>")
+	  //-->
+	</script>.
+      </p>
+
+      <p class="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="normalized">Normalized data files</a></div>
+      
+      <p class="text">Normalized data files may be submitted in any of
+      the above formats. In addition, files may be parsed using a
+      number of special column headings which can be used to designate
+      a column containing reporter or composite sequence
+      identifiers:</p>
+
+      <p class="text"><a name="reporter" class="subhead">Generic
+      normalized data </a> <br>If you have normalized data mapped to
+      the identifiers used in your array design, you can simply use a
+      single column containing those identifiers to include your data
+      in the final MAGE-ML. Tab2MAGE supports the use of either
+      Reporter Identifiers or CompositeSequence Identifiers for this
+      purpose. Please see these <a
+      href="http://www.ebi.ac.uk/miamexpress/help/adf/">ADF help
+      notes</a> for a discussion on these identifier types. Thus,
+      either of the following sets of column headers may be used:</p>
+
+      <table class="leftexample">
+	<tr><td>Reporter Identifier</td><td><<i>QT1</i>></td><td><<i>QT2</i>></td><td><<i>QT3</i>></td></tr>
+      </table>
+      <p>
+      <table class="leftexample">
+	<tr><td>CompositeSequence Identifier</td><td><<i>QT1</i>></td><td><<i>QT2</i>></td><td><<i>QT3</i>></td></tr>
+      </table>
+
+      <p class="text">where <<i>QT1</i>>, <<i>QT2</i>> etc. are the names of your
+      quantitation types (see the <a href="usage.html">usage notes</a>
+      for more information on including novel quantitation types).</p>
+
+      <p class="text"><a name="affynorm" class="subhead">Affymetrix
+      normalized data</a> <br>Tab2MAGE recognizes and parses CHP files
+      using both the old GDAC formats and the newer GCOS/XDA
+      formats. In addition, Affymetrix data normalized by
+      non-Affymetrix methods (e.g. RMA normalization) can be
+      parsed. Either CompositeSequence identifiers (<a
+      href="#reporter">above</a>) or either of the following sets of
+      column headers may be used:</p>
+
+      <table class="leftexample">
+	  <tr><td>ProbeSet ID</td><td><<i>QT1</i>></td><td><<i>QT2</i>></td><td><<i>QT3</i>></td></tr>
+      </table>
+      <p>
+      <table class="leftexample">
+	  <tr><td>ProbeSet Name</td><td><<i>QT1</i>></td><td><<i>QT2</i>></td><td><<i>QT3</i>></td></tr>
+      </table>
+
+      <p class="text">Again, <<i>QT1</i>>, <<i>QT2</i>> etc. are the names of your
+      quantitation types (see the <a href="usage.html">usage notes</a>
+      for more information on including novel quantitation types).</p>
+
+      <p class="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="final_data_matrix">Final data matrix files</a></div>
+
+      <p class="text"><em>(Tab2MAGE only)</em> Often it is desirable to store the normalized
+      data from all your hybridizations in a single file. This file
+      must then contain information on the quantitation types measured
+      (log ratio, RMA values etc.) and the hybridizations to which
+      these values apply. This information must be encoded in the data
+      file column headings, for example:</p>
+      
+      <table class="example">
+	<tr><td>Reporter Identifier</td><td>RMA(Hyb1)(Hyb2)</td><td>RMA(Hyb3)(Hyb4)</td></tr>
+	<tr><td>A102340</td><td>0.147</td><td>0.473</td></tr>
+	<tr><td>A102341</td><td>0.53</td><td>0.484</td></tr>
+	<tr><td>A102342</td><td>0.169</td><td>0.188</td></tr>
+	<tr><td>A102343</td><td>0.742</td><td>0.684</td></tr>
+	<tr><td>A102344</td><td>0.479</td><td>0.514</td></tr>
+      </table>
+
+      <p class="text">Any of the identifier headings described in the
+      <a href="#normalized">normalized data section</a> can be used in
+      the final data matrix file. <em>Please note</em> that the identifier
+      column <em>must</em> be the first column in the file.</p>
+
+      <p class="text">In
+      addition, for Tab2MAGE to correctly construct the mapping the
+      hybridization names must also be included in the annotation
+      spreadsheet, using the "<a href="detail.html#hyb">Hybridization</a>"
+      column (in the Hybridization section):</p>
+
+      <table class="example">
+	<tr><td><a class="plain" href="detail.html#hybsection">Hybridization section</a></td><td> </td><td> </td><td> </td></tr>
+	<tr>
+	  <td><a class="plain" href="detail.html#fileraw">File[raw]</a></td>
+	  <td><a class="plain" href="detail.html#filefgem">File[transformed]</a></td>
+	  <td><a class="plain" href="detail.html#arrayacc">Array[accession]</a></td>
+	  <td><a class="plain" href="detail.html#hyb">Hybridization</a></td></tr>
+	<tr><td>Data1.txt</td><td>FinalDataMatrix.txt</td><td>A-EXML-1</td><td>Hyb1</td></tr>
+	<tr><td>Data2.txt</td><td>FinalDataMatrix.txt</td><td>A-EXML-1</td><td>Hyb2</td></tr>
+	<tr><td>Data3.txt</td><td>FinalDataMatrix.txt</td><td>A-EXML-1</td><td>Hyb3</td></tr>
+	<tr><td>Data4.txt</td><td>FinalDataMatrix.txt</td><td>A-EXML-1</td><td>Hyb4</td></tr>
+      </table>
+
+      <p class="text"><em>Please note:</em> There are currently some
+      limitations imposed by Tab2MAGE when parsing final data matrix
+      files. Firstly, each file must correspond to a single array
+      design. In experiments where multiple array designs have been
+      used, separate final data matrix files should be included for
+      each. For example:</p>
+
+      <table class="example">
+	<tr><td><a class="plain" href="detail.html#hybsection">Hybridization section</a></td><td> </td><td> </td><td> </td></tr>
+	<tr>
+	  <td><a class="plain" href="detail.html#fileraw">File[raw]</a></td>
+	  <td><a class="plain" href="detail.html#filefgem">File[transformed]</a></td>
+	  <td><a class="plain" href="detail.html#arrayacc">Array[accession]</a></td>
+	  <td><a class="plain" href="detail.html#hyb">Hybridization</a></td></tr>
+	<tr><td>Data1.txt</td><td>FinalDataMatrix1.txt</td><td>A-EXML-1</td><td>Hyb1</td></tr>
+	<tr><td>Data2.txt</td><td>FinalDataMatrix1.txt</td><td>A-EXML-1</td><td>Hyb2</td></tr>
+	<tr><td>Data3.txt</td><td>FinalDataMatrix2.txt</td><td>A-EXML-2</td><td>Hyb3</td></tr>
+	<tr><td>Data4.txt</td><td>FinalDataMatrix2.txt</td><td>A-EXML-2</td><td>Hyb4</td></tr>
+      </table>
+
+      <p class="text">Secondly, the columns in the final data matrix
+      are currently mapped to hybridizations, rather than scanning
+      events. As a result, this system does not readily support
+      experiment designs where hybridizations are scanned multiple
+      times, e.g., for quality control purposes. To accurately reflect
+      such treatments, please include your normalized data as one file
+      per scan, and add a "<a
+      href="ArrayExpress/Curator/MAGE/Definitions.html#item_scan">Scan</a>"
+      column to explicitly specify which scan events belong to which
+      hybridization, as in the following example:</p>
+
+      <table class="example">
+	<tr><td><a class="plain" href="detail.html#hybsection">Hybridization section</a></td><td> </td><td> </td><td> </td></tr>
+	<tr>
+	  <td><a class="plain" href="detail.html#hyb">Hybridization</a></td>
+	  <td><a class="plain" href="detail.html#scan">Scan</a></td>
+	  <td><a class="plain" href="detail.html#fileraw">File[raw]</a></td>
+	  <td><a class="plain" href="detail.html#filenorm">File[normalized]</a></td></tr>
+	<tr><td>Hyb1</td><td>Scan1a</td><td>Data1a.txt</td><td>NormData1a.txt</td></tr>
+	<tr><td>Hyb1</td><td>Scan1b</td><td>Data1b.txt</td><td>NormData1b.txt</td></tr>
+	<tr><td>Hyb2</td><td>Scan2a</td><td>Data2a.txt</td><td>NormData2a.txt</td></tr>
+	<tr><td>Hyb2</td><td>Scan2b</td><td>Data2b.txt</td><td>NormData2b.txt</td></tr>
+      </table>
+
+      <p class="text">Please see the MIAMExpress data file help notes
+      (<a
+      href="http://www.ebi.ac.uk/miamexpress/help/datafile_help.html#affy_gem">Affymetrix</a>,
+      <a
+      href="http://www.ebi.ac.uk/miamexpress/help/datafile_help.html#fgedm">two-channel</a>)
+      for further discussion of final data matrix files. There is also
+      an <a href="../examples/final_data_matrix/">example</a> included
+      with the Tab2MAGE package of how to incorporate a final data
+      matrix into your MAGE-ML output.</p>
+
+      <p class="text"><em>For MAGE-TAB users:</em> This
+      Tab2MAGE/MIAMExpress final data matrix format is not supported
+      by MAGE-TAB, which defines its own data matrix format: <a
+      href="magetab_docs.html#datamatrix">MAGE-TAB Data Matrix
+      notes</a></p>
+
+      <p class="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="illumina">Illumina data files</a></div>
+
+      <p class="text">The Illumina BeadStudio software (see <a
+      href="http://www.illumina.com">www.illumina.com</a>) generates
+      data files in several closely related formats. Tab2MAGE only
+      supports such files reporting Probe-level (PROBE_ID) data,
+      exported from BeadStudio v3 and above, in tab-delimited
+      format. These files are characterised by having PROBE_ID as the
+      first column, with subsequent column headers following the
+      pattern "Hybridization name.QuantitationType name".</p>
+
+      <p class="text">Illumina have recently released the
+      'ArrayExpress Data Submission Report Plug-in' for BeadStudio
+      which will generate a raw data file, plus a basic Tab2MAGE
+      spreadsheet for you to fill in with your experiment and sample
+      information. This plug-in is available from the downloads
+      section of the <a href="https://icom.illumina.com/">Illumina
+      iCom website</a>.</p>
+
+      <p class="text">BeadStudio files may contain data columns
+      arranged per sample, or per group of samples (so-called
+      "Grouped data"). In contrast to the other scanning softwares
+      noted above, Illumina files may contain data from single or
+      multiple hybridizations. </p>
+
+      <p class="text">Sample-level data is supported in "File[raw]",
+      "File[normalized]" or "File[transformed]" columns of the Tab2MAGE
+      spreadsheet Hybridization section. The file name should be included
+      in all of the rows corresponding to the hybridizations it
+      covers. The array designator for each hybridization (found in
+      the data file column headings) must be entered into the
+      "Hybridization" column of the spreadsheet in each appropriate row.</p>
+
+      <p class="text">Grouped data is supported only as
+      "File[normalized]" or "File[transformed]". The column headings in
+      these data files contain user-defined tags for each group, and
+      these tags must be included in the "Normalization" column of the
+      spreadsheet. This allows the Tab2MAGE software to map the grouped
+      data back to the original hybridizations.</p>
+
+      <p class="text">There is one further data file type exported by
+      BeadStudio, which is not yet supported by Tab2MAGE: the
+      "differential expression" file. This file contains either
+      grouped or ungrouped data reported at the gene level, but with
+      extra columns reporting on "DiffScore" and "Concordance" between
+      samples or groups. Such data need to be split into multiple data
+      cubes when coding in MAGE, and it is this capability which
+      Tab2MAGE does not currently support.</p>
+
+      <p class="text">An example of a Tab2MAGE spreadsheet describing
+      sample data at the probe level and grouped data at the gene
+      level can be found in the <a
+      href="spreadsheet.html#examples">examples section</a>.</p>
+
+      <p class="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <hr>
+    <a href="http://sourceforge.net">
+      <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" />
+    </a>
+    <br>
+<!-- Created: Mon Jan 17 09:45:42 GMT 2005 -->
+<!-- hhmts start -->
+Last modified: Tue Jun 10 15:14:04 BST 2008
+<!-- hhmts end -->
+  </body>
+</html>
diff --git a/docs/detail.html b/docs/detail.html
new file mode 100644
index 0000000..c87684e
--- /dev/null
+++ b/docs/detail.html
@@ -0,0 +1,392 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+  <head>
+    <title>Spreadsheet construction - notes</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+    <link rel="stylesheet" href="style.css">
+  </head>
+
+  <body>
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100"><a href="../index.html"><img src="T2M_logo.png" border="0" height="50" alt="Tab2MAGE logo"></a></td>
+	    <td class="pagetitle">Creating your spreadsheet - notes</td>
+	  </tr>
+      </table>
+    </div>
+
+    <ul>
+	<li><a href="#experiment">Experiment section</a></li>
+	<li><a href="#protocol">Protocol section</a></li>
+	<li><a href="#hybridization">Hybridization section</a></li>
+    </ul>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="experiment">Experiment section</a></div>
+      
+      <p CLASS="text"><a name="exptsection" class="subhead">Experiment
+      section</a> <br> This is a required tag indicating the start
+      of the Experiment section.</p>
+
+      <p CLASS="text"><a name="domain" class="subhead">domain</a> <br>
+      The domain tag provides information on the originator of the
+      output MAGE-ML document. This field can contain any suitable
+      string, such as the originating internet domain name (e.g.,
+      "ebi.ac.uk").</p>
+
+      <p CLASS="text"><a name="accession" class="subhead">accession</a> <br> The
+      experiment accession number is a unique identifier assigned to
+      each experiment. Accession numbers for experiments submitted to
+      ArrayExpress have the format E-XXXX-n.</p>
+
+      <p CLASS="text"><a name="quality_control"
+      class="subhead">quality_control</a> <br> This is a
+      comma-separated list of terms taken from the <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php">MGED
+      ontology</a>. The terms should be instances of <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php#QualityControlDescriptionType">QualityControlDescriptionType</a>. Typical
+      terms which have been used here include "biological_replicate",
+      "technical_replicate" and "dye_swap_quality_control".</p>
+
+      <p CLASS="text"><a name="experiment_design_type"
+      class="subhead">experiment_design_type</a> <br> This is a
+      comma-separated list of terms taken from the <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php">MGED
+      ontology</a>. The terms should be instances of <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php#ExperimentDesignType">ExperimentDesignType</a>. Examples
+      of terms which have been used here include
+      "disease_state_design", "stimulus_or_stress_design" and
+      "compound_treatment_design".</p>
+
+      <p CLASS="text"><a name="name" class="subhead">name</a> <br> The name of the experiment.</p>
+
+      <p CLASS="text"><a name="description" class="subhead">description</a>
+      <br> A short paragraph (contained within this single spreadsheet
+      cell) describing the purpose of the experiment.</p>
+
+      <p CLASS="text"><a name="release_date" class="subhead">release_date</a>
+      <br> Date for public release, in the format YYYY-MM-DD.</p>
+
+      <p CLASS="text"><a
+      name="submission_date" class="subhead">submission_date</a> <br> Date of
+      submission, in the format YYYY-MM-DD.</p>
+
+      <p CLASS="text"><a name="submitter" class="subhead">submitter</a> <br>
+      The name of the person responsible for submitting the experiment
+      to the database.</p>
+
+      <p CLASS="text"><a name="organization" class="subhead">organization</a> <br>
+      The organization to which the submitter is affiliated.</p>
+
+      <p CLASS="text"><a name="publication"
+      class="subhead">publication_title, authors, journal, volume,
+      issue, pages, year, pubmed_id</a> <br> Publication details for any
+      manuscript associated with the experiment. The <b>journal</b>
+      field should contain a standard Pubmed journal abbreviated
+      name. The <b>authors</b> list is a semicolon-delimited list of
+      names.</p>
+
+      <p CLASS="sectionfoot"> [ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="protocol">Protocol section</a></div>
+      
+      <p CLASS="text"><a name="protosection" class="subhead">Protocol
+      section</a> <br> This is a required tag indicating the start of
+      the Protocol section.</p>
+
+      <p CLASS="text"><a name="protoaccession"
+      class="subhead">accession</a> <br> The protocol accession number
+      is a unique identifier assigned to each protocol. Accession
+      numbers for protocols submitted to ArrayExpress have the format
+      P-XXXX-n.</p>
+
+      <p CLASS="text"><a name="prototext" class="subhead">text</a>
+      <br> The full text of the protocol, <em>inserted into a single
+      spreadsheet cell</em>.  Note that as of version 1.8.1, Tab2MAGE
+      now supports line breaks in protocol text when exported from MS
+      Excel or OpenOffice.org. For this to work correctly, the
+      spreadsheet should be exported using the default settings for
+      tab-delimited text, i.e., enclosing all text fields in double
+      quotes ("). These breaks will then be automatically converted
+      into <br> tags for correct HTML formatting. Similarly, the
+      ampersand (&) character is now automatically quoted
+      correctly as an HTML entity. Protocol text may also be formatted
+      using standard HTML tags such as <br>. Inequality signs
+      < and > should be represented by the HTML entities
+      &lt; and &gt; respectively. Please be aware when using
+      MS Excel that this application may truncate long text in a
+      spreadsheet cell, without warning the user.</p>
+
+      <p CLASS="text"><a name="protoname" class="subhead">name</a> <br> The
+      name of the protocol.</p>
+
+      <p CLASS="text"><a name="prototype" class="subhead">type</a>
+      <br>This (very) optional column should contain terms taken from the <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php">MGED
+      ontology</a>. The term should be an instance of <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php#ExperimentalProtocolType">ExperimentalProtocolType</a>
+      or <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php#DataTransformationProtocolType">DataTransformationProtocolType</a>. Examples
+      of terms which have been used here include "grow",
+      "compound_based_treatment" and "hybridization". <em>Note:</em>
+      use of this column is not recommended in the majority of
+      cases. If this column is not present, suitable defaults will be
+      used based on how the protocols are referenced in the
+      Hybridization section.</p>
+
+      <p CLASS="text"><a name="protoparams"
+      class="subhead">parameters</a> <br> A list of parameters,
+      separated by semicolons, in the format "name(units)". Each
+      parameter name should be unique within the spreadsheet. Provided
+      that the "units" string matches one of those which are
+      hard-coded into the MAGE object model (e.g., "ug" for microgram
+      MassUnit, "degree_C" for degrees Celsius TemperatureUnit etc.), the
+      correct unit subclass will be created. Otherwise the script will
+      create a generic QuantityUnit object. If no unit is given, no
+      Unit object is created in the output MAGE-ML. <em>Note</em>: There 
+      is a clash between using "m" for minutes and meters. Tab2MAGE only 
+      supports using "m" for minutes in this context.</p>
+
+      <p CLASS="sectionfoot"> [ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="hybridization">Hybridization section</a></div>
+      
+      <p CLASS="text"><a name="hybsection"
+      class="subhead">Hybridization section</a> <br> This is a
+      required tag indicating the start of the Hybridization
+      section.</p>
+
+      <p CLASS="text"><a name="biosource"
+      class="subhead">BioSource</a> <br> The name of the
+      biosource. This is typically the biological material as it was
+      originally brought into the lab.</p>
+
+      <p CLASS="text"><a name="sample" class="subhead">Sample</a> <br>
+      The name of the sample derived from the biosource.</p>
+
+      <p CLASS="text"><a name="extract" class="subhead">Extract</a>
+      <br> The name of the extract made from the sample.</p>
+
+      <p CLASS="text"><a name="label"
+      class="subhead">LabeledExtract</a> <br> The name of the labeled
+      extract.</p>
+
+      <p CLASS="text"><a name="dye" class="subhead">Dye</a> <br> The
+      name of the dye used in labeling the extract. </p>
+
+      <p CLASS="text"><a name="hyb" class="subhead">Hybridization</a>
+      <br> The name of the hybridization event. <em>Note</em>: This is
+      the same name as must be used in the <a
+      href="datafiles.html#final_data_matrix">final data matrix
+      file</a>, where used.</p>
+
+      <p CLASS="text"><a name="scan" class="subhead">Scan</a>
+      <br> The name of the array scanning event.</p>
+
+      <p CLASS="text"><a name="norm" class="subhead">Normalization</a>
+      <br> The name of the normalization event.</p>
+
+      <p CLASS="text"><a name="materialtype"
+      class="subhead">BioSourceMaterial, SampleMaterial,
+      ExtractMaterial, LabeledExtractMaterial</a> <br> The types of
+      material used in the experiment. The terms should be taken from
+      the <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php">MGED
+      ontology</a>, and should be instances of the <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php#MaterialType">MaterialType</a>
+      class.</p>
+
+      <p CLASS="text"><a name="bmc"
+      class="subhead">BioMaterialCharacteristics[<i><category></i>]</a>
+      <br> These columns contain annotation for each of your <a
+      href="#biosource">biosources</a>. The terms in these columns
+      should be from one or more standard ontologies or other
+      controlled vocabularies. Examples of suitable ontologies and/or
+      vocabularies may be found on the following web sites:
+
+      <ul>
+	<li><a href="http://obo.sourceforge.net/main.html">Open Biomedical Ontologies</a></li>
+	<li><a href="http://ncimeta.nci.nih.gov/indexMetaphrase.html">NCI Metathesaurus</a></li>
+	<li><a href="http://www.informatics.jax.org/searches/anatdict_form.shtml">Mouse Anatomical Dictionary</a></li>
+	<li><a href="http://www.arabidopsis.org/info/growth.jsp">Arabidopsis Growth Stages</a></li>
+      </ul>
+      
+      <p class="text">The <i><category></i> field should contain
+      any of the <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php#BioMaterialCharacteristics">BioMaterialCharacteristics</a>
+      category terms from the <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php">MGED
+      ontology</a>, such as "DiseaseState", "Genotype", "Age" or
+      "Histology". The spreadsheet may contain as many
+      BioMaterialCharacteristics columns as required to fully describe
+      your biosources.</p>
+
+      <p CLASS="text"><a name="pgrow"
+      class="subhead">Protocol[grow]</a> <br>The accession number of
+      the protocol used to propagate the biosource.</p>
+
+      <p CLASS="text"><a name="ptreat"
+      class="subhead">Protocol[treatment]</a> <br>This is a
+      general-purpose protocol type describing any treatments of the
+      sample prior to extraction (e.g., administration of drugs,
+      tissue dissection etc.). </p>
+
+      <p CLASS="text"><a name="pextract"
+      class="subhead">Protocol[extraction]</a> <br>The method by which
+      nucleic acid is extracted from the sample.</p>
+
+      <p CLASS="text"><a name="plabel"
+      class="subhead">Protocol[labeling]</a> <br>The protocol used to
+      derive the labeled extract from the label.</p>
+
+      <p CLASS="text"><a name="phyb"
+      class="subhead">Protocol[hybridization]</a> <br>The protocol
+      used to hybridize the labeled extract to the array.</p>
+
+      <p CLASS="text"><a name="pscan"
+      class="subhead">Protocol[scanning]</a> <br>The protocol by which
+      the hybridized array is imaged.</p>
+
+      <p CLASS="text"><a name="pfext"
+      class="subhead">Protocol[image_analysis]</a> <br>The protocol
+      used in the feature extraction step in which the scanned image
+      is converted into raw numerical data.</p>
+
+      <p CLASS="text"><a name="pnorm"
+      class="subhead">Protocol[normalization]</a> <br>The procedure
+      used to derive normalized data from the raw data.</p>
+
+      <p CLASS="text"><a name="param"
+      class="subhead">Parameter[<i><parameter name></i>]</a>
+      <br>This class of column allows you to specify the values for
+      each of your protocols on a hyb-by-hyb basis. The
+      <i><parameter name></i> field must refer to a predeclared
+      <a href="#protoparams">parameter</a> defined in the <a
+      href="#protocol">Protocol section</a>. You should use as many
+      Parameter[] columns as you have predefined parameters.</p>
+
+      <p CLASS="text"><a name="fv"
+      class="subhead">FactorValue[<i><category></i>]</a>
+      <br>
+	These columns are used to indicate which of your experimental
+	factors apply to each hybridization. The terms in these
+	columns should be from one or more standard ontologies or
+	other controlled vocabularies. Examples of suitable ontologies
+	and/or vocabularies may be found in the notes on the <a
+	href="#bmc">BioMaterialCharacteristics[]</a> columns.  The
+	<i><category></i> field should contain a subclass of the
+	<a
+	href="http://mged.sourceforge.net/ontologies/MGEDontology.php#ExperimentalFactorCategory">ExperimentalFactorCategory</a>
+	class from the <a
+	href="http://mged.sourceforge.net/ontologies/MGEDontology.php">MGED
+	ontology</a>. The relevant categories may be taken from any
+	depth of the ExperimentalFactorCategory heirarchy, and you may
+	find that you have to descend several levels to obtain a
+	biologically useful category. Examples of suitable categories
+	include "DiseaseState", "OrganismPart", "Compound" or
+	"EnvironmentalHistory". The spreadsheet may contain as many
+	FactorValue columns as required to fully describe your
+	experiment.</p>
+
+      <p class="text"><em>FactorValues may be linked to either
+      "Values" or "Measurements"</em>. As a rule of thumb, Tab2MAGE
+      currently assumes that a purely numerical value in a FactorValue
+      column should be encoded as a Measurement. In such situations
+      the <i><category></i> field should indicate the class and
+      name of the measurement unit, in the form:</p>
+
+      <pre>FactorValue[<unit class>(<unit name>)]</pre>
+
+      <p class="text">For example:</p>
+
+      <pre>FactorValue[Temperature(degree_C)]</pre>
+
+      <p class="text">In each case the unit class and name should be
+      derived from the values defined by the MAGE object model. If a
+      match is not found, Tab2MAGE will generate a generic
+      QuantityUnit as a placeholder. <em>Note</em>: There 
+      is a clash between using "m" for minutes and meters. Tab2MAGE only 
+      supports using "m" for minutes in this context.</p>
+
+      <p CLASS="text"><a name="fileraw" class="subhead">File[raw]</a>
+      <br>The name of the file containing the raw data. For Affymetrix
+      submissions this should be the name of the CEL file. A single
+      hybridization may be linked to multiple raw data files (e.g.,
+      derived from different scanning events) using multiple rows
+      within the Tab2MAGE spreadsheet.</p>
+
+      <p CLASS="text"><a name="filenorm"
+      class="subhead">File[normalized]</a> <br>The name of the file
+      containing the normalized data. For Affymetrix submissions this
+      should be the name of the CHP file. A single hybridization may
+      be linked to multiple normalized data files (e.g., derived using
+      different normalization protocols) using multiple rows within
+      the Tab2MAGE spreadsheet.</p>
+
+      <p CLASS="text"><a name="filefgem"
+      class="subhead">File[transformed]</a> <br>The name of the combined
+      transformed final data matrix file. The format of this file is described in
+      the <a href="datafiles.html">data file notes</a>. The column
+      headings in this file must contain the name(s) of the
+      hybridization(s) with which each column is associated.</p>
+
+      <p CLASS="text"><a name="fileexp" class="subhead">File[exp]</a>
+      <br>(Affymetrix submissions only). The name of the EXP file for
+      a given hybridization.</p>
+
+      <p CLASS="text"><a name="filecdf" class="subhead">File[cdf]</a>
+      <br>(Affymetrix submissions only). The name of the library CDF
+      file provided by Affymetrix. The actual CDF file(s) need only be
+      supplied to ArrayExpress if you are using custom arrays.</p>
+
+      <p CLASS="text"><a name="arrayacc"
+      class="subhead">Array[accession]</a> <br>The ArrayExpress
+      accession number for the array design used in the
+      hybridization. <em>Please note</em> that if your array design has not
+      yet been submitted to ArrayExpress, then you should be prepared
+      to submit it yourself using the <a
+      href="http://www.ebi.ac.uk/miamexpress/">MIAMExpress submission
+      tool</a>. In cases where you have used commercial arrays, please
+      contact the 
+	<script type="text/javascript">
+	  <!--
+	  var aname = "arraysubs@ebi.ac.uk";
+	  document.write("<a href='" + "mail" + "to:" + aname + "'>ArrayExpress curators<" + "/a>")
+	  //-->
+	</script> to ask about adding the array
+      design to the database.</p>
+
+      <p CLASS="text"><a name="arrayser"
+      class="subhead">Array[serial]</a> <br>The serial number of the
+      array. For Affymetrix submissions this should be the Chip Lot
+      number.</p>
+
+      <p CLASS="sectionfoot"> [ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <p class="text">Please see the <a href="ArrayExpress/Curator/MAGE/Definitions.html#supported_headings">
+	MAGE mappings</a> for other supported column headings, and further
+    information on how the spreadsheet information is used to create the MAGE-ML document.</p>
+
+    <hr>
+    <a href="http://sourceforge.net">
+      <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" />
+    </a>
+    <br>
+<!-- Created: Mon Jan 17 09:45:42 GMT 2005 -->
+<!-- hhmts start -->
+Last modified: Wed Oct  4 14:06:05 BST 2006
+<!-- hhmts end -->
+  </body>
+</html>
diff --git a/docs/expt_check.html b/docs/expt_check.html
new file mode 100644
index 0000000..8e2281a
--- /dev/null
+++ b/docs/expt_check.html
@@ -0,0 +1,347 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>expt_check.pl - a script to check experiment data files
+submitted to MIAMExpress</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#quantitation_types">QUANTITATION TYPES</a></li>
+        <li><a href="#options">OPTIONS</a></li>
+        <li><a href="#tests">TESTS</a></li>
+        <li><a href="#local_miamexpress_installations">LOCAL MIAMEXPRESS INSTALLATIONS</a></li>
+        <li><a href="#see_also">SEE ALSO</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+        <li><a href="#bugs">BUGS</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../index.html"><img src="T2M_logo.png" border="0" height="50" alt=
+"Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Script detail: expt_check.pl</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>expt_check.pl - a script to check experiment data files
+submitted to MIAMExpress</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<dl>
+<dt><strong><a name="item_tab2mage_mode_3a" id=
+"item_tab2mage_mode_3a"><strong>Tab2MAGE
+mode:</strong></a></strong></dt>
+<dd>
+<pre>
+   expt_check.pl -e <Tab2MAGE spreadsheet>
+</pre></dd>
+<dt><strong><a name="item_mage_2dtab_mode_3a" id=
+"item_mage_2dtab_mode_3a"><strong>MAGE-TAB
+mode:</strong></a></strong></dt>
+<dd>
+<pre>
+   expt_check.pl -i <IDF file>
+</pre></dd>
+<dt><strong><a name="item_miamexpress_mode_3a" id=
+"item_miamexpress_mode_3a"><strong>MIAMExpress
+mode:</strong></a></strong></dt>
+<dd>
+<pre>
+   expt_check.pl -l <login name> -t <experiment title>
+</pre></dd>
+<dt><strong><a name="item_standalone_mode_3a" id=
+"item_standalone_mode_3a"><strong>Standalone
+mode:</strong></a></strong></dt>
+<dd>
+<pre>
+   expt_check.pl -s <list of data files>
+</pre></dd>
+<dd>
+<pre>
+   (use -A or -a to check files against an array design in standalone mode).
+</pre></dd>
+</dl>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This script can be used to check experimental data for
+submission to ArrayExpress in a few different ways. For Tab2MAGE
+submissions it parses the Tab2MAGE spreadsheet format and reports
+on problems with data files and MIAME metadata. Used in conjunction
+with a local MIAMExpress installation the script takes the
+experiment title and the submitter login name, and checks the
+submitted data files for errors. Several log files are written to
+the relevant MIAMExpress submission directory unless the
+<strong>-p</strong> option is used to redirect them to the current
+directory. Normally the script will be able to figure out which
+array design to use from the Tab2MAGE spreadsheet or by querying
+the MIAMExpress database. An optional ADF filename argument may
+also be provided using the <strong>-a</strong> option, and
+ArrayExpress accession numbers may be specified using the
+<strong>-A</strong> option. Specifying your own ADFs/accession
+numbers will make the script ignore any array designs pointed to by
+the spreadsheet or database entry for the experiment.</p>
+<hr />
+<h1><a name="quantitation_types" id=
+"quantitation_types">QUANTITATION TYPES</a></h1>
+<p>Known QuantitationTypes are listed in a separate file or files,
+created with a simple tab-delimited format. The layout of these
+files is described in <a href=
+"ArrayExpress/Datafile/QT_list.html">the
+ArrayExpress::Datafile::QT_list manpage</a>.</p>
+<hr />
+<h1><a name="options" id="options">OPTIONS</a></h1>
+<dl>
+<dt><strong><a name="item__2de_spreadsheet_filename" id=
+"item__2de_spreadsheet_filename"><strong>-e</strong>
+<code>spreadsheet filename</code></a></strong></dt>
+<dd>
+<p>The Tab2MAGE spreadsheet to be checked.</p>
+</dd>
+<dt><strong><a name="item__2di_idf_filename" id=
+"item__2di_idf_filename"><strong>-i</strong> <code>IDF
+filename</code></a></strong></dt>
+<dd>
+<p>The MAGE-TAB IDF file to be checked.</p>
+</dd>
+<dt><strong><a name="item__2dl_login_name" id=
+"item__2dl_login_name"><strong>-l</strong> <code>login
+name</code></a></strong></dt>
+<dd>
+<p>The MIAMExpress login name of the experiment submitter</p>
+</dd>
+<dt><strong><a name="item__2dt_experiment_title" id=
+"item__2dt_experiment_title"><strong>-t</strong> <code>experiment
+title</code></a></strong></dt>
+<dd>
+<p>The MIAMExpress title of the experiment, surrounded by quotes if
+the title contains spaces.</p>
+</dd>
+<dt><strong><a name="item__2da_adf_filename" id=
+"item__2da_adf_filename"><strong>-a</strong> <code>ADF
+filename</code></a></strong></dt>
+<dd>
+<p>The <strong>-a</strong> switch designates the ADF filename to be
+used for all the hybridizations in the experiment. This option
+overrides any database links between hybridizations and array
+designs, and is provided initially as a convenience.</p>
+</dd>
+<dt><strong><a name="item_number" id=
+"item_number"><strong>-A</strong> <code>Array accession number
+(ArrayExpress)</code></a></strong></dt>
+<dd>
+<p>Use the <strong>-A</strong> switch to indicate the accession
+number of an ArrayExpress array design to be used for checking the
+data files. Ordinarily this should not be needed, as the script
+should be able to link the MIAMExpress submission with ArrayExpress
+array designs automatically.</p>
+</dd>
+<dt><strong><a name="item__2dc" id=
+"item__2dc"><strong>-c</strong></a></strong></dt>
+<dd>
+<p>Forces overwriting of existing files (``clobber''). If this
+switch is omitted the user will be asked whether to overwrite
+already existing files.</p>
+</dd>
+<dt><strong><a name="item__2dp" id=
+"item__2dp"><strong>-p</strong></a></strong></dt>
+<dd>
+<p>Write to files in present working directory (``pwd''). The
+default is to write to files in the submission directory.</p>
+</dd>
+<dt><strong><a name="item__2ds" id=
+"item__2ds"><strong>-s</strong></a></strong></dt>
+<dd>
+<p>Standalone option. The script will check the files listed on the
+command line rather than connecting to MIAMExpress and
+ArrayExpress. When used with the <strong>-e</strong> or
+<strong>-i</strong> options, the Tab2MAGE or MAGE-TAB document is
+checked but no connection is made to ArrayExpress to retrieve array
+information. To check features and reporter identifiers with this
+option, an ADF must be specified with the <strong>-a</strong>
+option or an ArrayExpress accession number can be used with the
+<strong>-A</strong> option. In the latter case a connection is made
+to ArrayExpress without connecting to MIAMExpress.</p>
+</dd>
+<dt><strong><a name="item__2dd_directory" id=
+"item__2dd_directory"><strong>-d</strong>
+<code>directory</code></a></strong></dt>
+<dd>
+<p>Source directory. This indicates the directory to search for
+data files. This option is only used for Tab2MAGE submissions
+checks, as MIAMExpress defines its own directory structure which is
+automatically searched by this script. If this option is omitted,
+only the current working directory is searched for Tab2MAGE
+submission data files.</p>
+</dd>
+<dt><strong><a name="item__2dx" id=
+"item__2dx"><strong>-x</strong></a></strong></dt>
+<dd>
+<p>Skip data file checking. This option can be used to quickly
+check experiment annotation without having to wait for the script
+to validate all the data files.</p>
+</dd>
+<dt><strong><a name="item__2dq_qt_filename" id=
+"item__2dq_qt_filename"><strong>-q</strong> <code>QT
+filename</code></a></strong></dt>
+<dd>
+<p>QuantitationType file. This option allows you to specify a
+custom QuantitationType definition file to override those defined
+in the ArrayExpress::Curator::Config module. See <a href=
+"ArrayExpress/Datafile/QT_list.html">the
+ArrayExpress::Datafile::QT_list manpage</a> for more
+information.</p>
+</dd>
+<dt><strong><a name="item__2dq_qt_filename" id=
+"item__2dq_qt_filename"><strong>-Q</strong> <code>QT
+filename</code></a></strong></dt>
+<dd>
+<p>QuantitationType file. This option will add the new
+QuantitationType definitions to those included with the Tab2MAGE
+package. See <a href="ArrayExpress/Datafile/QT_list.html">the
+ArrayExpress::Datafile::QT_list manpage</a> for more
+information.</p>
+</dd>
+<dt><strong><a name="item__2dr_namespace" id=
+"item__2dr_namespace"><strong>-R</strong>
+<code>namespace</code></a></strong></dt>
+<dd>
+<p>Prefix of the Reporter identifier to use when checking data
+files against array designs. This prefix is added to each
+``Reporter Identifier'' in the data files prior to comparison with
+the actual identifiers in the ADF. The default is to assume
+MIAMExpress-like identifiers.</p>
+</dd>
+<dt><strong><a name="item__2dc_namespace" id=
+"item__2dc_namespace"><strong>-C</strong>
+<code>namespace</code></a></strong></dt>
+<dd>
+<p>Prefix of the CompositeSequence identifier to use when checking
+data files against array designs. This prefix is added to each
+``CompositeSequence Identifier'' in the data files prior to
+comparison with the actual identifiers in the ADF. The default is
+to assume MIAMExpress-like identifiers.</p>
+</dd>
+<dt><strong><a name="item__2dl" id=
+"item__2dl"><strong>-L</strong></a></strong></dt>
+<dd>
+<p>Ignore the data file size limit as configured in Config.yml
+(i.e., MAX_DATAFILE_SIZE).</p>
+</dd>
+<dt><strong><a name="item__2dm" id=
+"item__2dm"><strong>-m</strong></a></strong></dt>
+<dd>
+<p>The checker will support MAGE-TAB documents in which a single
+IDF and SDRF have been combined (in that order), with the start of
+each section marked by [IDF] and [SDRF] respectively. Note that
+such documents are not compliant with the MAGE-TAB format
+specification; this format is used by ArrayExpress to simplify data
+submissions.</p>
+</dd>
+<dt><strong><a name="item__2dv" id=
+"item__2dv"><strong>-v</strong></a></strong></dt>
+<dd>
+<p>Prints the version number of the script.</p>
+</dd>
+<dt><strong><a name="item__2dh" id=
+"item__2dh"><strong>-h</strong></a></strong></dt>
+<dd>
+<p>Prints a short help text.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="tests" id="tests">TESTS</a></h1>
+<p>There are numerous tests performed by this script. Listed below
+are the tests which are performed on each data file. There is also
+a series of checks which are made on any MIAME metadata supplied to
+the script. This metadata may be in the form of a Tab2MAGE
+spreadsheet, or an experiment submission in a local MIAMExpress
+database. Please see <a href=
+"ArrayExpress/Curator/ExperimentChecker.html#tests">TESTS in the
+ArrayExpress::Curator::ExperimentChecker manpage</a> for a list of
+general tests performed on all submissions. See also <a href=
+"ArrayExpress/Curator/Validate.html#tests">TESTS in the
+ArrayExpress::Curator::Validate manpage</a> for the Tab2MAGE
+spreadsheet tests, <a href=
+"ArrayExpress/MAGETAB/Checker.html#tests">TESTS in the
+ArrayExpress::MAGETAB::Checker manpage</a> for MAGE-TAB document
+checking, and <a href=
+"ArrayExpress/Curator/MIAMExpress.html#tests">TESTS in the
+ArrayExpress::Curator::MIAMExpress manpage</a> for more information
+on the MIAMExpress tests.</p>
+<hr />
+<h1><a name="local_miamexpress_installations" id=
+"local_miamexpress_installations">LOCAL MIAMEXPRESS
+INSTALLATIONS</a></h1>
+<p>Users wishing to use this script with local installations of
+MIAMExpress should check the ArrayExpress::Curator::Config module
+and change whatever parameters are necessary.</p>
+<hr />
+<h1><a name="see_also" id="see_also">SEE ALSO</a></h1>
+<dl>
+<dt><strong><a name=
+"item_arrayexpress_3a_3acurator_3a_3aexperimentchecker" id=
+"item_arrayexpress_3a_3acurator_3a_3aexperimentchecker"></a><a href="ArrayExpress/Curator/ExperimentChecker.html">the
+ArrayExpress::Curator::ExperimentChecker manpage</a></strong></dt>
+<dt><strong><a name="item_arrayexpress_3a_3acurator_3a_3avalidate"
+id="item_arrayexpress_3a_3acurator_3a_3avalidate"></a><a href=
+"ArrayExpress/Curator/Validate.html">the
+ArrayExpress::Curator::Validate manpage</a></strong></dt>
+<dt><strong><a name="item_arrayexpress_3a_3amagetab_3a_3achecker"
+id="item_arrayexpress_3a_3amagetab_3a_3achecker"></a><a href=
+"ArrayExpress/MAGETAB/Checker.html">the
+ArrayExpress::MAGETAB::Checker manpage</a></strong></dt>
+<dt><strong><a name=
+"item_arrayexpress_3a_3acurator_3a_3amiamexpress" id=
+"item_arrayexpress_3a_3acurator_3a_3amiamexpress"></a><a href=
+"ArrayExpress/Curator/MIAMExpress.html">the
+ArrayExpress::Curator::MIAMExpress manpage</a></strong></dt>
+<dt><strong><a name="item_arrayexpress_3a_3adatafile_3a_3aqt_list"
+id="item_arrayexpress_3a_3adatafile_3a_3aqt_list"></a><a href=
+"ArrayExpress/Datafile/QT_list.html">the
+ArrayExpress::Datafile::QT_list manpage</a></strong></dt>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2004.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<h1><a name="bugs" id="bugs">BUGS</a></h1>
+<p>Affymetrix CHP files are not currently supported. Moreover, the
+script will not check the features referred to in CEL files against
+an array design until I can figure out how to retrieve a list of
+valid features in a timely fashion. CompositeSequence IDs in final
+data matrix files from Affymetrix submissions are fully supported
+and checked against the array design. Basic data quality
+calculations (percent null, Benfords law) are made on Affymetrix
+CEL and FGEM data.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/idf.html b/docs/idf.html
new file mode 100644
index 0000000..e68be7d
--- /dev/null
+++ b/docs/idf.html
@@ -0,0 +1,378 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+  <head>
+    <title>IDF notes</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+    <link rel="stylesheet" href="style.css" type="text/css">
+  </head>
+
+  <body>
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100"><a href="../index.html"><img src="MAGETAB_logo.png" border="0" height="50" alt="MAGE-TAB logo"></a></td>
+	    <td class="pagetitle">IDF help notes</td>
+	  </tr>
+      </table>
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="overall">Introduction</a></div>
+
+      <p class="text">This page provides detailed help on the
+      Investigation Design Format (IDF), and lists all the available
+      tags for use in MAGE-TAB IDF documents. For an overview of the
+      MAGE-TAB submission process, and examples of both IDF and SDRF,
+      please see these <a href="magetab_subs.html">submission help
+      notes</a> and <a href="magetab_docs.html">MAGE-TAB
+      overview</a>. For more detail on the MAGE-TAB format, please see
+      the <a href="http://www.mged.org/mage-tab/">MAGE-TAB
+      specification</a>.</p>
+
+      <p class="text">Most of the following fields can be used with
+      more than one value, so that (for example) multiple Protocols,
+      Persons, Experimental Factors and Term Sources can be defined in
+      a single IDF file. In these cases, the different "objects" are
+      separated by tabs. Those fields which can contain only one value
+      are indicated below. In some cases (Person Roles, Protocol
+      Parameters) it is possible to have multiple values within a
+      given "object". For example, one person may have many roles. In
+      such cases the multiple roles should be separated by semicolons
+      (";").</p>
+
+      <p class="text">MAGE-TAB documents may optionally include
+      information on the sources of any controlled terms used,
+      providing users with the ability to link to ontologies,
+      databases or other sources of controlled vocabularies to
+      describe their experiment. These so-called "Term Sources" are
+      defined in the IDF and may be used throughout the document. If
+      they are not used, then all controlled vocabulary terms are
+      assumed to be user-defined.</p>
+	
+      <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="idftags">All valid IDF tags</a></div>
+      
+      <p class="text"><a name="invtitle" class="subhead">Investigation
+      Title</a> <br>The overall title of the investigation. <font
+      color="red">This tag can only have one value.</font></p>
+
+      <p class="text"><a name="exptdesign"
+      class="subhead">Experimental Design</a> <br>The experiment
+      design types which are applicable to this study. Typically these
+      terms should come from the <a
+      href="http://mged.sourceforge.net/ontologies/index.php">MGED
+      Ontology</a>. The <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php#ExperimentDesignType">
+      ExperimentDesignType</a> subclasses are particularly useful
+      here. See for example the list of <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php#BiologicalProperty">BiologicalProperty
+      </a> terms available. <font color="blue">Controlled vocabulary
+      term.</font></p>
+
+      <p class="text"><a name="exptdesigntermsource"
+      class="subhead">Experimental Design Term Source REF</a> <br>The
+      source of the Experimental Design terms; this must reference one
+      of the Term Source Names defined elsewhere in the IDF file (see
+      <a href="#termsourcename">below</a>).</p>
+
+      <p class="text"><a name="exptdesigntermaccno"
+      class="subhead">Experimental Design Term Accession Number</a>
+      <br>The accession number for this term, taken from the indicated
+      Term Source.</p>
+
+      <p class="text"><a name="factorname"
+      class="subhead">Experimental Factor Name</a> <br>A user-defined
+      name for each experimental factor studied by the
+      experiment. These experimental factors represent the variables
+      within the investigation (e.g. growth condition, genotype,
+      organism part, disease state). The actual values of these
+      variables will be listed in the SDRF file, in "Factor Value [<factor name>]" colummns. <font
+      color="green">Used as an identifier within the MAGE-TAB
+      document.</font></p>
+
+      <p class="text"><a name="factortype"
+      class="subhead">Experimental Factor Type</a> <br> A term
+      describing the type of each experimental factor. These terms
+      will usually come from the <a
+      href="http://mged.sourceforge.net/ontologies/index.php">MGED
+      Ontology</a>. The <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php#ExperimentalFactorCategory">
+      ExperimentalFactorCategory</a> subclasses are particularly
+      useful here. See for example the list of <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php#BioMaterialCharacteristicCategory">BioMaterialCharacteristicCategory
+      terms</a> available. <font color="blue">Controlled vocabulary
+      term.</font></p>
+
+      <p class="text"><a name="factortermsource"
+      class="subhead">Experimental Factor Term Source REF</a> <br>The
+      source of the Experimental Factor Type terms; this must
+      reference one of the Term Source Names defined elsewhere in the
+      IDF file (see <a href="#termsourcename">below</a>).</p>
+
+      <p class="text"><a name="factortermaccno"
+      class="subhead">Experimental Factor Term Accession Number</a>
+      <br>The accession number for this term, taken from the indicated
+      Term Source.</p>
+
+      <p class="text"><a name="lastname" class="subhead">Person Last
+      Name</a> <br>The last name of each person associated with the
+      experiment.</p>
+
+      <p class="text"><a name="firstname" class="subhead">Person First
+      Name</a> <br>The first name of each person associated with the
+      experiment.</p>
+
+      <p class="text"><a name="midinitials" class="subhead">Person Mid
+      Initials</a> <br>The middle initials of each person associated
+      with the experiment.</p>
+
+      <p class="text"><a name="email" class="subhead">Person Email</a>
+      <br>The email address of each person associated with the
+      experiment.</p>
+
+      <p class="text"><a name="phone" class="subhead">Person Phone</a>
+      <br>The telephone number of each person associated with the
+      experiment.</p>
+
+      <p class="text"><a name="fax" class="subhead">Person Fax</a>
+      <br>The Fax number of each person associated with the
+      experiment.</p>
+
+      <p class="text"><a name="address" class="subhead">Person
+      Address</a> <br>The street address of each person associated
+      with the experiment.</p>
+      
+      <p class="text"><a name="affiliation" class="subhead">Person
+      Affiliation</a> <br>The organization affiliation for each person
+      associated with the experiment.</p>
+
+      <p class="text"><a name="roles" class="subhead">Person Roles</a>
+      <br>The role(s) performed by each person. Typically these terms
+      should come from the <a
+      href="http://mged.sourceforge.net/ontologies/index.php">MGED
+      Ontology</a>. See for example the list of <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php#Roles">
+      Roles</a> terms. If more than one role is needed per person, the
+      roles should be given as a semicolon (";") delimited list, for
+      example: "submitter;data_coder;investigator". <font
+      color="blue">Controlled vocabulary term.</font></p>
+
+      <p class="text"><a name="rolestermsource" class="subhead">Person
+      Roles Term Source REF</a> <br>The source of the Person Roles
+      terms; this must reference one of the Term Source Names defined
+      elsewhere in the IDF file (see <a href="#termsourcename">below</a>).</p>
+
+      <p class="text"><a name="rolestermaccno"
+      class="subhead">Person Roles Term Accession Number</a>
+      <br>The accession number for this term, taken from the indicated
+      Term Source.</p>
+
+      <p class="text"><a name="qctype" class="subhead">Quality Control
+      Type</a> <br>The quality control procedures used. Typically
+      these terms should come from the <a
+      href="http://mged.sourceforge.net/ontologies/index.php">MGED
+      Ontology</a>. See for example the list of <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php#QualityControlDescriptionType">
+      QualityControlDescriptionType</a> terms. <font
+      color="blue">Controlled vocabulary term.</font></p>
+
+      <p class="text"><a name="qctypetermsource"
+      class="subhead">Quality Control Term Source REF</a> <br>The
+      source of the Quality Control Type terms; this must reference
+      one of the Term Source Names defined elsewhere in the IDF
+      file (see <a href="#termsourcename">below</a>).</p>
+
+      <p class="text"><a name="qctypetermaccno"
+      class="subhead">Quality Control Term Accession Number</a>
+      <br>The accession number for this term, taken from the indicated
+      Term Source.</p>
+
+      <p class="text"><a name="reptype" class="subhead">Replicate
+      Type</a><br> The replicate strategies used. Typically these terms
+      should come from the <a
+      href="http://mged.sourceforge.net/ontologies/index.php">MGED
+      Ontology</a>. See for example the list of <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php#ReplicateDescriptionType">
+      ReplicateDescriptionType</a> terms. <font
+      color="blue">Controlled vocabulary term.</font></p>
+
+      <p class="text"><a name="reptypetermsource"
+      class="subhead">Replicate Type Term Source REF</a> <br>The
+      source of the Replicate Type terms; this must reference one of
+      the Term Source Names defined elsewhere in the IDF file (see <a href="#termsourcename">below</a>).</p>
+      
+      <p class="text"><a name="reptypetermaccno"
+      class="subhead">Replicate Type Term Accession Number</a>
+      <br>The accession number for this term, taken from the indicated
+      Term Source.</p>
+
+      <p class="text"><a name="normtype" class="subhead"> <br>Normalization
+      Type</a> <br>The normalization strategies used. Typically these terms
+      should come from the <a
+      href="http://mged.sourceforge.net/ontologies/index.php">MGED
+      Ontology</a>. See for example the list of <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php#NormalizationDescriptionType">
+      NormalizationDescriptionType</a> terms. <font
+      color="blue">Controlled vocabulary term.</font></p>
+
+      <p class="text"><a name="normtypetermsource"
+      class="subhead">Normalization Type Term Source REF</a> <br>The
+      source of the Normalization Type terms; this must reference one
+      of the Term Source Names defined elsewhere in the IDF file (see <a href="#termsourcename">below</a>).</p>
+      
+      <p class="text"><a name="normtypetermaccno"
+      class="subhead">Normalization Type Term Accession Number</a>
+      <br>The accession number for this term, taken from the indicated
+      Term Source.</p>
+
+      <p class="text"><a name="exptdate" class="subhead">Date of
+      Experiment</a> <br>The date on which the experiment was
+      performed. <font color="red">This tag can only have one
+      value.</font></p>
+
+      <p class="text"><a name="releasedate" class="subhead">Public
+      Release Date</a> <br>The date on which the experimental data
+      will be/was released. <font color="red">This tag can only have
+      one value.</font></p>
+      
+      <p class="text"><a name="pubmedid" class="subhead">PubMed ID</a>
+      <br>The PubMed IDs of the publication(s) associated with this
+      investigation (where available).</p>
+
+      <p class="text"><a name="pubdoi" class="subhead">Publication
+      DOI</a> <br>A Digital Object Identifier (DOI) for each
+      publication (where available).</p>
+      
+      <p class="text"><a name="pubauthors" class="subhead">Publication
+      Author List</a> <br> The list of authors associated with each
+      publication.</p>
+
+      <p class="text"><a name="pubtitle" class="subhead">Publication
+      Title</a> <br>The title of each publication.</p>
+      
+      <p class="text"><a name="pubstatus" class="subhead">Publication
+      Status</a> <br>A term describing the status of each publication
+      (e.g. "submitted", "in preparation", "published"). <font
+      color="blue">Controlled vocabulary term.</font></p>
+
+      <p class="text"><a name="pubstatustermsource"
+      class="subhead">Publication Status Term Source REF</a> <br>The
+      source of the Publication Status terms; this must reference one
+      of the Term Source Names defined elsewhere in the IDF file (see <a href="#termsourcename">below</a>).</p>
+      
+      <p class="text"><a name="pubstatustermaccno"
+      class="subhead">Publication Status Term Accession Number</a>
+      <br>The accession number for this term, taken from the indicated
+      Term Source.</p>
+
+      <p class="text"><a name="exptdesc" class="subhead">Experiment
+      Description</a> <br> A short paragraph describing the experiment
+      as free-text. <font color="red">This tag can only have one
+      value.</font></p>
+
+      <p class="text"><a name="protoname" class="subhead">Protocol
+      Name</a> <br>The names of the protocols used within the MAGE-TAB
+      document. These will be referenced in the SDRF in the
+      "Protocol REF" columns. <font color="green">Used as an
+      identifier within the MAGE-TAB document.</font></p>
+      
+      <p class="text"><a name="prototype" class="subhead">Protocol
+      Type</a> <br>The type of the protocol, taken from a controlled
+      vocabulary. Typically this term should come from the <a
+      href="http://mged.sourceforge.net/ontologies/index.php">MGED
+      Ontology</a>. See for example the list of <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php#ExperimentalProtocolType">
+      ExperimentalProtocolType</a> terms. <font
+      color="blue">Controlled vocabulary term.</font></p>
+
+      <p class="text"><a name="protodesc" class="subhead">Protocol
+      Description</a> <br>A free-text description of the
+      protocol. This text is included in a single tab-delimited
+      field. If you wish to include tab or newline characters as part
+      of this text, you must enclose the whole text within double
+      quotes (").</p>
+      
+      <p class="text"><a name="protoparam" class="subhead">Protocol
+      Parameters</a> <br>A semicolon-delimited list of parameter
+      names; these names are used in the SDRF file (as
+      "Parameter Value [<parameter name>]"
+      headings) to list the values used for each protocol
+      parameter. If more than one parameter was used for a given
+      protocol, they should be separated with semicolons (";"). <font
+      color="green">Used as an identifier within the MAGE-TAB
+      document.</font></p>
+
+      <p class="text"><a name="protohw" class="subhead">Protocol
+      Hardware</a> <br>The hardware used by the protocol.</p>
+
+      <p class="text"><a name="protosw" class="subhead">Protocol
+      Software</a> <br>The software used by the protocol.</p>
+
+      <p class="text"><a name="protocontact" class="subhead">Protocol
+      Contact</a> <br>The name and contact details to be used for
+      enquiries concerning the protocol.</p>
+      
+      <p class="text"><a name="prototypetermsource"
+      class="subhead">Protocol Term Source REF</a> <br>The source of
+      the Protocol Type terms; this must reference one of the Term
+      Source Names defined elsewhere in the IDF file (see <a
+      href="#termsourcename">below</a>). Examples: MGED ontology,
+      OBI.</p>
+
+      <p class="text"><a name="prototypetermaccno"
+      class="subhead">Protocol Term Accession Number</a>
+      <br>The accession number for this term, taken from the indicated
+      Term Source.</p>
+
+      <p class="text"><a name="sdrf" class="subhead">SDRF File</a>
+      <br>The name(s) of the SDRF file(s) accompanying this IDF file.</p>
+      
+      <p class="text"><a name="termsourcename" class="subhead">Term
+      Source Name</a> <br>The names of the Term Sources (ontologies or
+      databases) used within the MAGE-TAB document. This name will be
+      used in all corresponding "Term Source REF"
+      fields. Examples: MGED Ontology, NCI MetaThesaurus,
+      ArrayExpress. <font color="green">Used as an identifier within
+      the MAGE-TAB document.</font></p>
+
+      <p class="text"><a name="termsourcefile" class="subhead">Term
+      Source File</a> <br>A filename or valid URI at which the Term
+      Source may be accessed.</p>
+      
+      <p class="text"><a name="termsourceversion" class="subhead">Term
+      Source Version</a> <br>The version of the Term Source used
+      throughout the MAGE-TAB document.</p>
+
+      <p class="text"><a name="comment"
+      class="subhead">Comment[<user-defined tag>]</a> <br>A
+      user-defined value which is associated with the
+      investigation. For example, ArrayExpress uses
+      "Comment [ArrayExpressSubmissionDate]" to record the date of
+      submission to the database; alternatively, tags such as
+      "Comment [Goal]" might be used to indicate the purpose behind an
+      investigation.</p>
+      
+      <p CLASS="sectionfoot"> [ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <p class="text">Please see the <a href="http://www.mged.org/mage-tab/">
+	MAGE-TAB specification</a> for further information and examples.</p>
+
+    <hr>
+    <a href="http://sourceforge.net">
+      <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" />
+    </a>
+    <br>
+<!-- Created: Mon Jan 17 09:45:42 GMT 2005 -->
+<!-- hhmts start -->
+Last modified: Mon Feb 25 18:11:01 GMT 2008
+<!-- hhmts end -->
+  </body>
+</html>
diff --git a/docs/index.html b/docs/index.html
new file mode 100644
index 0000000..d172687
--- /dev/null
+++ b/docs/index.html
@@ -0,0 +1,101 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+  <head>
+    <title>Tab2MAGE</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+    <link rel="stylesheet" href="style.css">
+  </head>
+
+  <body>
+    <table class="layout">
+	<tr>
+	  <td class="whitetitle" width="270"><a href="../index.html"><img src="T2M_logo.png" border="0" height="50" alt="Tab2MAGE logo"></a> <a href="../index.html#magetab"><img src="MAGETAB_logo.png" border="0" height="50" alt="MAGE-TAB logo"></a></td>
+	  <td class="pagetitle">Documentation</td>
+	</tr>
+    </table>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="toc">Table of contents</a></div>
+      
+      <p class="text">Please note that this documentation describes
+      the <em>development</em> version of Tab2MAGE, as used and tested
+      by the ArrayExpress curation staff. Users of the older <em>stable</em>
+      release should refer to the documentation included in their
+      downloaded package.</p>
+
+      <p class="text"><a name="tab2mage" class="subhead">Tab2MAGE
+      information</a><br> The following pages contain important
+      information on how to use Tab2MAGE to generate MAGE-ML, and data
+      submission to ArrayExpress using this format:</p>
+
+    <ul>
+	<li><a href="aesubs.html">Tab2MAGE ArrayExpress data submission</a></li>
+	<li><a href="spreadsheet.html">Constructing a Tab2MAGE spreadsheet</a></li>
+	<li><a href="datafiles.html">Supported data file formats</a></li>
+	<li><a href="usage.html#columns">Dealing with unknown data file columns</a></li>
+	<li><a href="detail.html">Tab2MAGE spreadsheet detail</a></li>
+    </ul>
+
+      <p class="text"><a name="magetab" class="subhead">MAGE-TAB
+      information</a><br> These pages contain a brief primer on the
+      MAGE-TAB format, and how to submit MAGE-TAB documents to
+      ArrayExpress:</p>
+
+      <ul>
+	<li><a href="magetab_subs.html">MAGE-TAB submission to ArrayExpress</a></li>
+	<li><a href="idf.html">Creating IDF documents - detail</a></li>
+	<li><a href="sdrf.html">Creating SDRF documents - detail</a></li>
+      </ul>
+
+      <p class="text">For more information on the MAGE-TAB format, please
+      refer to the <a href="http://www.mged.org/mage-tab/">MAGE-TAB
+      specification</a>.</p>
+
+      <p class="text">A brief summary of the differences between
+      MAGE-TAB and Tab2MAGE is given <a
+      href="magetab_vs_tab2mage.html">here</a>.</p>
+
+      <p class="text"><a name="technical" class="subhead">Technical
+      details</a><br> Also included here are some notes which have
+      been automatically generated from the Perl code documentation
+      for your convenience. They will usually give reasonably up to
+      date (although not always terribly user-friendly) information on
+      how the scripts work. For the absolute latest up-to-the-minute
+      information, please refer to the "perldoc" documentation embedded
+      in each script or module.</p>
+
+    <ul>
+	<li><a href="tab2mage.html">Tab2MAGE script</a></li>
+	<li><a href="magetab.html">MAGE-TAB script</a></li>
+	<li><a href="expt_check.html">Experiment checker script</a></li>
+	<li><a href="ArrayExpress/Curator/MAGE/Definitions.html#supported_headings">All supported spreadsheet column headings</a></li>
+	<li><a href="ArrayExpress/Datafile/QT_list.html#quantitation_types">QuantitationType definitions file format</a></li>
+	<li><a href="ArrayExpress/Curator/Config.html">Configuration options (not needed for basic operation)</a></li>
+    </ul>
+
+      <p class="text"><a name="developer" class="subhead">Developer
+      documentation</a><br> Below is a set of API documentation for
+      the various modules which are incuded in the Tab2MAGE
+      package. These are likely to be of interest only to
+      developers:</p>
+
+      <ul>
+	  <li><a href="modules.html">Perl modules</a></li>
+      </ul>
+      
+      <p CLASS="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <hr>
+    <a href="http://sourceforge.net">
+      <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" />
+    </a>
+    <br>
+<!-- Created: Mon Jan 10 16:08:12 GMT 2005 -->
+<!-- hhmts start -->
+Last modified: Tue Apr 29 13:05:11 BST 2008
+<!-- hhmts end -->
+  </body>
+</html>
diff --git a/docs/magetab.html b/docs/magetab.html
new file mode 100644
index 0000000..fb16023
--- /dev/null
+++ b/docs/magetab.html
@@ -0,0 +1,235 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>magetab.pl - a script to generate valid MAGE-ML from an
+input MAGE-TAB document.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#options">OPTIONS</a></li>
+        <li><a href="#quantitationtype_file">QUANTITATIONTYPE FILE</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../index.html"><img src="MAGETAB_logo.png" border="0" height="50"
+alt="MAGE-TAB logo" /></a></td>
+<td class="pagetitle">Script detail: magetab.pl</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>magetab.pl - a script to generate valid MAGE-ML from an input
+MAGE-TAB document.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ magetab.pl -i <IDF filename> -n <experiment accession> -t <target directory>
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This script will process a MAGE-TAB document and a set of data
+files to generate valid MAGE-ML. The supported data file formats
+are listed in the accompanying documentation. The MAGE-TAB format
+specification may be downloaded from this link:</p>
+<pre>
+ <a href=
+"http://www.ebi.ac.uk/systems-srv/mp/file-exchange/MAGE-TABv1.0.tar.gz">http://www.ebi.ac.uk/systems-srv/mp/file-exchange/MAGE-TABv1.0.tar.gz</a>
+</pre>
+<p>There is one auxilliary file which can be supplied alongside the
+data files. This file simply defines the QuantitationTypes which
+are to be extracted from the data files. This QuantitationType file
+is optional, however, as the script is supplied with a set of
+defaults determined by a survey of incoming QuantitationTypes by
+ArrayExpress curators. The script generates a log file in the
+target directory which details which columns have been ignored. To
+keep all unrecognized QuantitationTypes, invoke the script with the
+<strong>-k</strong> option.</p>
+<hr />
+<h1><a name="options" id="options">OPTIONS</a></h1>
+<dl>
+<dt><strong><a name="item__2di_idf" id=
+"item__2di_idf"><strong>-i</strong>
+<code>IDF</code></a></strong></dt>
+<dd>
+<p>The MAGE-TAB IDF file to be parsed.</p>
+</dd>
+<dt><strong><a name="item__2dn_accession" id=
+"item__2dn_accession"><strong>-n</strong>
+<code>accession</code></a></strong></dt>
+<dd>
+<p>The experiment accession to be used as the top-level Experiment
+identifier in the generated MAGE-ML.</p>
+</dd>
+<dt><strong><a name="item__2dt_directory" id=
+"item__2dt_directory"><strong>-t</strong>
+<code>directory</code></a></strong></dt>
+<dd>
+<p>The target directory to be created. This directory will contain
+the MAGE-ML file and external data files ready for validation.</p>
+</dd>
+<dt><strong><a name="item__2dq_qt_filename" id=
+"item__2dq_qt_filename"><strong>-q</strong> <code>QT
+filename</code></a></strong></dt>
+<dd>
+<p>QuantitationType file. This option allows you to specify a
+custom QuantitationType definition file to override those defined
+as part of the Tab2MAGE package. See <a href=
+"ArrayExpress/Datafile/QT_list.html">the
+ArrayExpress::Datafile::QT_list manpage</a> for more
+information.</p>
+</dd>
+<dt><strong><a name="item__2dq_qt_filename" id=
+"item__2dq_qt_filename"><strong>-Q</strong> <code>QT
+filename</code></a></strong></dt>
+<dd>
+<p>QuantitationType file. This option will add the new
+QuantitationType definitions to those included with the Tab2MAGE
+package. See <a href="ArrayExpress/Datafile/QT_list.html">the
+ArrayExpress::Datafile::QT_list manpage</a> for more
+information.</p>
+</dd>
+<dt><strong><a name="item__2dk" id=
+"item__2dk"><strong>-k</strong></a></strong></dt>
+<dd>
+<p>Keep all columns in the data files, regardless of whether they
+are recognized or not. Unrecognized QTs will be created as generic
+SpecializedQuantitationTypes in the output MAGE-ML.</p>
+</dd>
+<dt><strong><a name="item__2dk" id=
+"item__2dk"><strong>-K</strong></a></strong></dt>
+<dd>
+<p>If the autosubmissions system is configured, magetab.pl will
+automatically reassign protocol accessions to fit a local
+convention. Use the -K option to suppress this behaviour.</p>
+</dd>
+<dt><strong><a name="item__2ds" id=
+"item__2ds"><strong>-s</strong></a></strong></dt>
+<dd>
+<p>Standalone option. This prevents the script from attempting to
+connect to ArrayExpress to retrieve array information.</p>
+</dd>
+<dt><strong><a name="item__2dx" id=
+"item__2dx"><strong>-x</strong></a></strong></dt>
+<dd>
+<p>Skip data file processing during MAGE-ML generation. The script
+will attempt to generate MAGE-ML for the metadata contained in the
+IDF and SDRF only.</p>
+</dd>
+<dt><strong><a name="item__2dr_namespace" id=
+"item__2dr_namespace"><strong>-R</strong>
+<code>namespace</code></a></strong></dt>
+<dd>
+<p>Reporter identifier prefix. By default the script uses the
+MIAMExpress convention for generating reporter identifiers. This
+option allows you to override this behaviour by supplying an
+alternate prefix for identifiers.</p>
+</dd>
+<dt><strong><a name="item__2dc_namespace" id=
+"item__2dc_namespace"><strong>-C</strong>
+<code>namespace</code></a></strong></dt>
+<dd>
+<p>CompositeSequence identifier prefix. By default the script uses
+the MIAMExpress convention for generating composite sequence
+identifiers. This option allows you to override this behaviour by
+supplying an alternate prefix for identifiers.</p>
+</dd>
+<dt><strong><a name="item__2dd_directory" id=
+"item__2dd_directory"><strong>-d</strong>
+<code>directory</code></a></strong></dt>
+<dd>
+<p>Source directory containing all the data files referenced in the
+SDRF. If this is omitted, the current working directory will be
+searched for data files.</p>
+</dd>
+<dt><strong><a name="item__2dp" id=
+"item__2dp"><strong>-P</strong></a></strong></dt>
+<dd>
+<p>By default, native (usually binary) file formats are used for
+Affymetrix CEL files and all NimbleScan (NimbleGen) files. This
+encoding uses far less overhead and retains the files in their
+original formats; this is often appealing to end-users. When used
+for ArrayExpress submissions, this option allows for such files to
+be directly downloadable from the ArrayExpress web interface.
+However, in unusual circumstances when you might wish to use
+plain-text encoding for these data files, use this option.</p>
+</dd>
+<dt><strong><a name="item__2dl" id=
+"item__2dl"><strong>-L</strong></a></strong></dt>
+<dd>
+<p>Ignore the data file size limit as configured in Config.yml
+(i.e., MAX_DATAFILE_SIZE).</p>
+</dd>
+<dt><strong><a name="item__2dm" id=
+"item__2dm"><strong>-m</strong></a></strong></dt>
+<dd>
+<p>The parser will support MAGE-TAB documents in which a single IDF
+and SDRF have been combined (in that order), with the start of each
+section marked by [IDF] and [SDRF] respectively. Note that such
+documents are not compliant with the MAGE-TAB format specification;
+this format is used by ArrayExpress to simplify data
+submissions.</p>
+</dd>
+<dt><strong><a name="item__2df_font_name" id=
+"item__2df_font_name"><strong>-f</strong> <code>font
+name</code></a></strong></dt>
+<dd>
+<p>Name of the font to be used for Graphviz-generated PNGs.</p>
+</dd>
+<dt><strong><a name="item__2dc" id=
+"item__2dc"><strong>-c</strong></a></strong></dt>
+<dd>
+<p>Overwrite preexisting files (``clobber'' option).</p>
+</dd>
+<dt><strong><a name="item__2dv" id=
+"item__2dv"><strong>-v</strong></a></strong></dt>
+<dd>
+<p>Prints the version number of the script.</p>
+</dd>
+<dt><strong><a name="item__2dh" id=
+"item__2dh"><strong>-h</strong></a></strong></dt>
+<dd>
+<p>Print a short help text summarizing these options.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="quantitationtype_file" id=
+"quantitationtype_file">QUANTITATIONTYPE FILE</a></h1>
+<p>Please see <a href="ArrayExpress/Datafile/QT_list.html">the
+ArrayExpress::Datafile::QT_list manpage</a> for a description of
+the format of this file.</p>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2007.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/magetab_docs.html b/docs/magetab_docs.html
new file mode 100644
index 0000000..fde03e9
--- /dev/null
+++ b/docs/magetab_docs.html
@@ -0,0 +1,429 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+  <head>
+    <title>MAGE-TAB Overview</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+    <link rel="stylesheet" href="style.css" type="text/css">
+  </head>
+
+  <body>
+    <table class="layout">
+	<tr>
+	  <td class="whitetitle" width="100"><a href="../index.html"><img src="MAGETAB_logo.png" border="0" height="50" alt="MAGE-TAB logo"></a></td>
+	  <td class="pagetitle">Overview of the MAGE-TAB format</td>
+	</tr>
+    </table>
+
+    <div CLASS="section">
+
+      <table class="layout">
+	<tr>
+	  <td class="layout">
+	    
+	    <p class="subhead">On this page:</p>
+
+	    <ul>
+	      <li><a href="#introduction">Introduction</a></li>
+	      <li><a href="#overall">Overall structure of MAGE-TAB</a></li>
+	      <li><a href="#idf">IDF overview</a></li>
+	      <li><a href="#sdrf">SDRF overview</a></li>
+	      <li><a href="#datafiles">Data files</a></li>
+	      <li><a href="#datamatrix">Data Matrices</a></li>
+	    </ul>
+
+	    <p class="subhead">See also:</p>
+
+	    <ul>
+	      <li><a href="idf.html">IDF detail</a></li>
+	      <li><a href="sdrf.html">SDRF detail</a></li>
+	      <li><a href="datafiles.html">Supported data file formats</a></li>
+	      <li><a href="magetab_subs.html">Submitting MAGE-TAB to ArrayExpress</a></li>
+	      <li><a href="magetab_subs.html#realexamples">Example MAGE-TAB documents</a></li>
+	    </ul>
+	  </td>
+	  <td class="layout" valign="top">
+	    <div CLASS="imghead">
+	      <a href="http://www.ebi.ac.uk/arrayexpress/">
+		<img src="aelogo.png" border="0" alt="ArrayExpress logo">
+	      </a>
+	    </div>
+	  </td>
+
+	</tr>
+      </table>
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="introduction">Introduction</a></div>
+
+      <p class="text">These notes provide an introduction to the
+      MAGE-TAB format and a brief primer on how to submit microarray
+      data to ArrayExpress in this format. MAGE-TAB is the successor
+      to the Tab2MAGE format, developed by members of the microarray
+      research community to extend the capabilities of the spreadsheet
+      format. A <a href="http://www.mged.org/mage-tab/">full MAGE-TAB
+      specification document</a> is available from the <a
+      href="http://www.mged.org">MGED web site</a>.</p>
+
+      <p class="text">ArrayExpress supports submission of MAGE-TAB
+      documents via a <a
+      href="http://www.ebi.ac.uk/cgi-bin/microarray/magetab.cgi">web-based
+      submission system</a>. This system allows the user to create an
+      experiment submission and download an automatically-generated
+      template MAGE-TAB document. This document can then be completed
+      by the user and uploaded to the web page alongside their data
+      files. For more information, please see the <a
+      href="magetab_subs.html">ArrayExpress submission help
+      notes</a></p>
+	
+      <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="overall">Overall structure of MAGE-TAB</a></div>
+
+      <p class="text">The MAGE-TAB format uses a number of different
+      files to capture information about a microarray experiment:</p>
+      
+      <ul>
+
+	<li><a href="#idf">IDF</a>: Investigation Description Format</li>
+
+	<li><a href="#sdrf">SDRF</a>: Sample and Data Relationship Format</li>
+
+	<li><a href="#adf">ADF</a>: Array Design Format</li>
+
+	<li><a href="#datafiles">Raw and processed data files</a>,
+	including "<a href="#datamatrix">data matrix</a>" files</li>
+
+      </ul>
+
+      <p class="text">The IDF file is used to give an overview of the
+      experiment, including the experimental variables (factors) used,
+      protocols, quality control strategy, publication information and
+      contact details. Also included in the IDF file is an (optional)
+      list of sources from which controlled vocabulary terms may have
+      been used elsewhere in the MAGE-TAB document. These term sources
+      may be fully-fledged ontologies (e.g. the MGED ontology),
+      databases providing queryable accession numbers
+      (e.g. ArrayExpress), or simply a file defining terms for local
+      users.</p>
+
+      <p class="text">The SDRF file describes the relationship between
+      every step in the chain of biological materials used in the
+      experiment through to the hybridization, and the acquisition and
+      normalization of data. It is similar in concept to the <a
+      href="spreadsheet.html#hybridization">Hybridization section</a>
+      found in Tab2MAGE spreadsheets. Experimental factors, protocols,
+      protocol parameters and term sources defined in the IDF are
+      referenced by the SDRF.</p>
+
+      <p class="text"><a name="adf"/>The ADF file provides the
+      array-level annotation for the experiment, relating the
+      row-level identifiers in the data files to biological sequence
+      annotation. Array designs are usually deposited in ArrayExpress
+      as separate submissions to the experimental data, and in the
+      case of commercial arrays may not need to be submitted to
+      ArrayExpress at all.</p>
+
+      <p class="text">Currently, ArrayExpress only supports submission
+      of array designs using ADF files via the <a
+      href="http://www.ebi.ac.uk/miamexpress/">MIAMExpress web
+      submission</a> system. For help with this process, please see
+      the <a
+      href="http://wwwe.ebi.ac.uk/miamexpress/help/adf/index.html">MIAMExpress
+      ADF help documentation</a>. This array design component of
+      MAGE-TAB will not be discussed further here.</p>
+
+      <p class="text">An experimental data submission will usually
+      consist of an IDF file, an SDRF file, and a series of data
+      files. Typically there will be one raw data file per
+      hybridization. Each hybridization may also have a normalized
+      data file, or the final transformed data may be combined into a
+      data matrix file. Note that the ArrayExpress MAGE-TAB generating
+      system combines both the IDF and the SDRF into a single file
+      format, for the sake of convenience.</p>
+
+      <p class="text">For more detail on the MAGE-TAB document format,
+      please see the <a
+      href="http://www.mged.org/mage-tab/">MAGE-TAB
+      specification document,</a> available from the <a
+      href="http://www.mged.org/">MGED web site</a>.</p>
+      
+      <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="idf">IDF overview</a></div>
+
+      <p class="text">The IDF component of a MAGE-TAB document
+      consists of a set of unique tags attached to their corresponding
+      values in a simple tab-delimited text format. For example,
+      "Experiment Description" should be followed by a free-text
+      description of the experiment. Most of the fields in the IDF
+      document can handle multiple values. A full description of every
+      valid IDF tag is given in the <a href="idf.html">detailed IDF
+      help notes</a>. An example of an IDF document is given
+      below:</p>
+
+      <table class="example">
+	<tr><td><a class="plain" href="idf.html#invtitle">Investigation Title</a></td><td>Invasive vs. non-invasive strains of yeast</td><td> </td></tr>
+	<tr><td> </td><td> </td><td> </td></tr>
+	<tr><td><a class="plain" href="idf.html#exptdesign">Experimental Design</a></td><td>individual_genetic_characteristics_design</td><td>growth_condition_design</td></tr>
+	<tr><td><a class="plain" href="idf.html#factorname">Experimental Factor Name</a></td><td>EF_Genotype</td><td>EF_GrowthCond</td></tr>
+	<tr><td><a class="plain" href="idf.html#factortype">Experimental Factor Type</a></td><td>genotype</td><td>growth_condition</td></tr>
+	<tr><td> </td><td> </td><td> </td></tr>
+	<tr><td><a class="plain" href="idf.html#lastname">Person Last Name</a></td><td>Falstaff</td><td>Shakespeare</td></tr>
+	<tr><td><a class="plain" href="idf.html#firstname">Person First Name</a></td><td>John</td><td>Bill</td></tr>
+	<tr><td><a class="plain" href="idf.html#email">Person Email</a></td><td>jfalstaff at wagglespike.com</td><td>bills at wagglespike.com</td></tr>
+	<tr><td><a class="plain" href="idf.html#address">Person Address</a></td><td>Ontario, Canada</td><td>Ontario, Canada</td></tr>
+	<tr><td><a class="plain" href="idf.html#affiliation">Person Affiliation</a></td><td>Windsor Laboratories</td><td>Windsor Laboratories</td></tr>
+	<tr><td><a class="plain" href="idf.html#roles">Person Roles</a></td><td>submitter;investigator</td><td>investigator</td></tr>
+	<tr><td> </td><td> </td><td> </td></tr>
+	<tr><td><a class="plain" href="idf.html#qctype">Quality Control Type</a></td><td>dye_swap_quality_control</td><td> </td></tr>
+	<tr><td> </td><td> </td><td> </td></tr>
+	<tr><td><a class="plain" href="idf.html#releasedate">Public Release Date</a></td><td>2004-08-30</td><td> </td></tr>
+	<tr><td> </td><td> </td><td> </td></tr>
+	<tr><td><a class="plain" href="idf.html#pubmedid">PubMed ID</a></td><td>12345678</td><td>87654321</td></tr>
+	<tr><td><a class="plain" href="idf.html#pubauthors">Publication Author List</a></td><td>Falstaff, J. and Shakespeare, B.</td><td>Goodfellow, R. et al.</td></tr>
+	<tr><td><a class="plain" href="idf.html#pubtitle">Publication Title</a></td><td>Improved yeast flocculation</td><td>Yeast and beer: a retrospective</td></tr>
+	<tr><td><a class="plain" href="idf.html#pubstatus">Publication Status</a></td><td>in preparation</td><td>submitted</td></tr>
+	<tr><td> </td><td> </td><td> </td></tr>
+	<tr><td><a class="plain" href="idf.html#exptdesc">Experiment Description</a></td><td>An experiment was performed to...</td><td> </td></tr>
+	<tr><td> </td><td> </td><td> </td></tr>
+	<tr><td><a class="plain" href="idf.html#protoname">Protocol Name</a></td><td>Yeast Growth</td><td>RNA extraction</td></tr>
+	<tr><td><a class="plain" href="idf.html#prototype">Protocol Type</a></td><td>grow</td><td>nucleic_acid_extraction</td></tr>
+	<tr><td><a class="plain" href="idf.html#protodesc">Protocol Description</a></td><td>S. cerevisiae cultures were grown on...</td><td>Total cellular RNA was extracted...</td></tr>
+	<tr><td><a class="plain" href="idf.html#protoparam">Protocol Parameters</a></td><td>carbon source;temperature</td><td> </td></tr>
+	<tr><td> </td><td> </td><td> </td></tr>
+	<tr><td><a class="plain" href="idf.html#sdrf">SDRF File</a></td><td>my_sdrf_file.txt</td><td> </td></tr>
+      </table>
+
+      <p class="text">Note that this is only a minimal subset of the
+      available IDF tags. Blank lines may be included for
+      legibility. Lines beginning with the "#" symbol are treated as
+      comments and ignored. A full listing of all supported IDF tags
+      can be found in these <a href="idf.html">IDF help notes</a>.</p>
+      
+      <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="sdrf">SDRF overview</a></div>
+
+      <p class="text">The SDRF file consists of a table in which each
+      hybridization channel is represented by a row, and columns
+      represent the steps of the experiment. In contrast to the
+      Tab2MAGE format, the ordering of these columns is important, and
+      should read left-to-right in chronological order. The overall
+      organization of this table is shown below. To get more detail on
+      the properties of each section, click on the relevant box below
+      or read these <a href="sdrf.html">detailed SDRF notes</a>.</p>
+
+      <map name="sdrf_layout_Map">
+	<area shape="rect" alt="Derived Array Data File" coords="557,402,716,493" href="sdrf.html#derivedarraydatafile_section"/>
+	<area shape="rect" alt="Scan" coords="86,402,245,493" href="sdrf.html#scan_section"/>
+	<area shape="rect" alt="Array Data File" coords="243,402,402,493" href="sdrf.html#arraydatafile_section"/>
+        <area shape="rect" alt="Normalization" coords="400,402,559,493" href="sdrf.html#normalization_section"/>
+        <area shape="rect" alt="Labeled Extract" coords="324,0,434,94" href="sdrf.html#labeledextract_section"/>
+        <area shape="rect" alt="Extract" coords="216,0,326,94" href="sdrf.html#extract_section"/>
+        <area shape="rect" alt="Sample" coords="108,0,218,94" href="sdrf.html#sample_section"/>
+        <area shape="rect" alt="Source" coords="0,0,110,94" href="sdrf.html#source_section"/>
+        <area shape="rect" alt="Hybridization" coords="268,168,428,281" href="sdrf.html#hybridization_section"/>
+      </map>
+      <div align="center"><img src="sdrf_layout.png" width="716" height="493" border="0" alt="SDRF layout" usemap="#sdrf_layout_Map"></div>
+
+      <p class="text">Each block in the diagram above starts with a
+      "Name" or "File" column (e.g. "Extract Name", "Array Data
+      File"), followed by a set of attribute columns. Each block is
+      separated from its predecessor by "<a
+      href="sdrf.html#protocol_section">Protocol REF</a>" columns
+      containing references to the "Protocol Name" values defined in
+      the IDF.</p>
+
+      <p class="text">A further set of columns is used to specify the
+      values for the variables ("experimental factors") within the
+      experiment. These <a href="sdrf.html#factorvalue_section">Factor
+      Value[]</a> columns reference the Experimental Factor Names
+      defined in the IDF, and should be placed after the hybridization
+      section (i.e., to the right of it, in or after the scanning,
+      normalization and data section in the image above). The contents
+      of these columns will usually duplicate those in a material <a
+      href="sdrf.html#characteristics">Characteristics</a> or a
+      protocol <a href="sdrf.html#parameter_value">Parameter Value</a>
+      column. See below for an example.</p>
+
+      <p class="text">Below is a very simple example SDRF showing the
+      links between materials and hybridization for a simple
+      Affymetrix-based experiment:</p>
+
+      <table class="example">
+	<tr>
+	  <td><a class="plain" href="sdrf.html#source_name">Source Name</a></td>
+	  <td><a class="plain" href="sdrf.html#characteristics">Characteristics [OrganismPart]</a></td>
+	  <td><a class="plain" href="sdrf.html#labeled_extract_name">Labeled Extract Name</a></td>
+	  <td><a class="plain" href="sdrf.html#label">Label</a></td>
+	  <td><a class="plain" href="sdrf.html#protocol_ref">Protocol REF</a></td>
+	  <td><a class="plain" href="sdrf.html#hybridization_name">Hybridization Name</a></td>
+	  <td><a class="plain" href="sdrf.html#array_design_ref">Array Design REF</a></td>
+	</tr>
+	<tr><td>liver sample 1</td><td>liver</td><td>LE 1</td><td>biotin</td><td>Hyb protocol name</td><td>Hyb 1</td><td>A-AFFY-33</td></tr>
+	<tr><td>liver sample 2</td><td>liver</td><td>LE 2</td><td>biotin</td><td>Hyb protocol name</td><td>Hyb 2</td><td>A-AFFY-33</td></tr>
+	<tr><td>kidney sample 1</td><td>kidney</td><td>LE 3</td><td>biotin</td><td>Hyb protocol name</td><td>Hyb 3</td><td>A-AFFY-33</td></tr>
+	<tr><td>kidney sample 2</td><td>kidney</td><td>LE 4</td><td>biotin</td><td>Hyb protocol name</td><td>Hyb 4</td><td>A-AFFY-33</td></tr>
+      </table>
+      
+      <p class="text">Note that normally, many more
+      "Characteristics[]" columns would be used to fully describe the
+      Source. The SDRF might then continue in the following fashion,
+      linking the Hybridization to data files and experimental factor
+      values:</p>
+      
+      <table class="example">
+	<tr>
+	  <td><a class="plain" href="sdrf.html#hybridization_name">Hybridization Name</a></td>
+	  <td><a class="plain" href="sdrf.html#array_design_ref">Array Design REF</a></td>
+	  <td><a class="plain" href="sdrf.html#scan_name">Scan Name</a></td>
+	  <td><a class="plain" href="sdrf.html#array_data_file">Array Data File</a></td>
+	  <td><a class="plain" href="sdrf.html#protocol_ref">Protocol REF</a></td>
+	  <td><a class="plain" href="sdrf.html#normalization_name">Normalization Name</a></td>
+	  <td><a class="plain" href="sdrf.html#derived_array_data_file">Derived Array Data File</a></td>
+	  <td><a class="plain" href="sdrf.html#derived_array_data_file">Factor Value [FactorOP]</a></td>
+	</tr>
+	<tr><td>Hyb 1</td><td>A-AFFY-33</td><td>Scan 1</td><td>Data1.CEL</td><td>Norm protocol name</td><td>Norm 1</td><td>Data1.CHP</td><td>liver</td></tr>
+	<tr><td>Hyb 2</td><td>A-AFFY-33</td><td>Scan 2</td><td>Data2.CEL</td><td>Norm protocol name</td><td>Norm 2</td><td>Data2.CHP</td><td>liver</td></tr>
+	<tr><td>Hyb 3</td><td>A-AFFY-33</td><td>Scan 3</td><td>Data3.CEL</td><td>Norm protocol name</td><td>Norm 3</td><td>Data3.CHP</td><td>kidney</td></tr>
+	<tr><td>Hyb 4</td><td>A-AFFY-33</td><td>Scan 4</td><td>Data4.CEL</td><td>Norm protocol name</td><td>Norm 4</td><td>Data4.CHP</td><td>kidney</td></tr>
+      </table>
+      
+      <p class="text">In this case, the "Factor Value [FactorOP]"
+      column refers to an Experimental Factor named "FactorOP", with
+      Type "organism_part", from the IDF which would accompany this
+      SDRF. Blank lines may be included for legibility. Lines
+      beginning with the "#" symbol are treated as comments and
+      ignored. Note that the examples on this page only illustrate a
+      very minimal set of the available SDRF columns available. A full
+      listing of all supported column names can be found in these <a
+      href="sdrf.html">SDRF help notes</a>.</p>
+      
+      <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="datafiles">Data Files</a></div>
+
+      <p class="text">A variety of data file formats, produced by
+      several different scanner makes and models, are supported by the
+      ArrayExpress MAGE-TAB parser. A full list of supported formats
+      can be found in the <a href="datafiles.html">Tab2MAGE data file
+      documentation</a>. Note that the MAGE-TAB specification introduces
+      a new file format, that of the <em>data matrix</em>, for files which
+      contain data from multiple hybridizations. This new format is
+      discussed below.</p>
+      
+      <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="datamatrix">Data Matrices</a></div>
+
+      <p class="text">If you wish to represent data from more than one
+      hybridization, scan or normalization in a single data file, you
+      will need to reformat it as a MAGE-TAB Data Matrix. This is a
+      simplified format which allows data columns to be mapped to rows
+      in the SDRF file. The first header line of a Data Matrix file
+      describes this mapping, and the second lists the quantitation
+      types for each column (e.g. "log2 ratio"). The first column is
+      used to map the data rows to identifiers from the array design
+      used. Examples are shown here: </p>
+
+      <div align="center"><h4>Example two-color data matrix</h4></div>
+
+      <table class="example">
+	<tr><td>Hybridization REF</td><td>Hyb1</td><td>Hyb2</td><td>Hyb3</td><td>Hyb4</td><td>Hyb5</td><td>Hyb6</td></tr>
+	<tr><td>Reporter REF</td><td>log2 ratio</td><td>log2 ratio</td><td>log2 ratio</td><td>log2 ratio</td><td>log2 ratio</td><td>log2 ratio</td></tr>
+	<tr><td>Probe 1</td><td>0.27</td><td>0.43</td><td>0.32</td><td>0.12</td><td>0.54</td><td>0.28</td></tr>
+	<tr><td>Probe 2</td><td>1.67</td><td>1.46</td><td>1.91</td><td>1.49</td><td>1.50</td><td>1.89</td></tr>
+	<tr><td>Probe 3</td><td>0.78</td><td>0.69</td><td>0.91</td><td>0.99</td><td>0.75</td><td>0.80</td></tr>
+      </table>
+
+      <p class="text">In this example, six hybridizations from the
+      SDRF (Hyb1 - Hyb6) are being mapped to log2 ratio values. Each row
+      of data is mapped to a Reporter Identifier defined by the array
+      design (itself indicated in the SDRF file).</p>
+
+      <div align="center"><h4>Example Affymetrix data matrix</h4></div>
+
+      <table class="example">
+	<tr><td>Hybridization REF</td><td>Hyb1</td><td>Hyb1</td><td>Hyb2</td><td>Hyb2</td><td>Hyb3</td><td>Hyb3</td></tr>
+	<tr><td>CompositeElement REF</td><td>CELIntensity</td><td>CELStdev</td><td>CELIntensity</td><td>CELStdev</td><td>CELIntensity</td><td>CELStdev</td></tr>
+	<tr><td>Gene 1</td><td>22287.9</td><td>56.8</td><td>3222.9</td><td>111.1</td><td>9984.3</td><td>34.8</td></tr>
+	<tr><td>Gene 2</td><td>267.4</td><td>7.6</td><td>118.1</td><td>7.6</td><td>236.8</td><td>9.0</td></tr>
+	<tr><td>Gene 3</td><td>876.5</td><td>16.7</td><td>936.8</td><td>14.9</td><td>735.6</td><td>8.0</td></tr>
+      </table>
+
+      <p class="text">In this example, three hybridizations from the
+      SDRF file (Hyb1, Hyb2 and Hyb3) are being mapped to data
+      with two different quantitation types (CELIntensity,
+      CELStdev). Each row of data is mapped to a CompositeElement
+      Identifier (equivalent to MAGE CompositeSequence Identifiers)
+      defined by the array design.</p>
+
+      <p class="text">There are some limitations imposed by
+      ArrayExpress when submitting data in this format. Firstly, each
+      data matrix should correspond to hybridizations performed on a
+      single array design. Experiments using multiple array designs
+      should use one data matrix per design. Secondly, we rely on
+      there being an ordered and regular organisation of the columns:
+      first by hybridization, and then by quantitation type:</p>
+
+      <div align="center"><em>Correct:</em></div>
+
+      <table class="example">
+	<tr><td>Hybridization REF</td><td><font color="blue">Hyb1</font></td><td><font color="blue">Hyb1</font></td><td><font color="red">Hyb2</font></td><td><font color="red">Hyb2</font></td></tr>
+	<tr><td>Reporter REF</td><td><font color="blue">QT X</font></td><td><font color="red">QT Y</font></td><td><font color="blue">QT X</font></td><td><font color="red">QT Y</font></td></tr>
+      </table>
+
+      <br/>
+      
+      <div align="center"><em>Wrong:</em></div>
+
+      <table class="example">
+	<tr><td>Hybridization REF</td><td><font color="blue">Hyb1</font></td><td><font color="red">Hyb2</font></td><td><font color="blue">Hyb1</font></td><td><font color="red">Hyb2</font></td></tr>
+	<tr><td>Reporter REF</td><td><font color="blue">QT X</font></td><td><font color="blue">QT X</font></td><td><font color="red">QT Y</font></td><td><font color="red">QT Y</font></td></tr>
+      </table>
+
+      <p class="text">If your processed data does not readily fall
+      into such a structure you may need to break the data up into
+      multiple data matrices, and use the SDRF file to represent the
+      relationships between them.</p>
+      
+      <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <p class="text">Please see the <a href="http://www.mged.org/mage-tab/">
+	MAGE-TAB specification</a> for further information and examples.</p>
+
+    <hr>
+    <a href="http://sourceforge.net">
+      <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" />
+    </a>
+    <br>
+<!-- Created: Tue Jul 10 11:10:10 BST 2007 -->
+<!-- hhmts start -->
+Last modified: Mon Feb 25 18:04:42 GMT 2008
+<!-- hhmts end -->
+  </body>
+</html>
diff --git a/docs/magetab_subs.html b/docs/magetab_subs.html
new file mode 100644
index 0000000..fec7ca9
--- /dev/null
+++ b/docs/magetab_subs.html
@@ -0,0 +1,589 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+  <head>
+    <title>MAGE-TAB ArrayExpress submissions</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+    <link rel="stylesheet" href="style.css" type="text/css">
+  </head>
+
+  <body>
+    <div><a name="top"></a>
+
+      <table class="layout">
+	<tr>
+	  <td class="whitetitle" width="100"><a href="../index.html"><img src="MAGETAB_logo.png" border="0" height="50" alt="MAGE-TAB logo"></a></td>
+	  <td class="pagetitle">Submitting MAGE-TAB to ArrayExpress</td>
+	</tr>
+      </table>
+    </div>
+
+    <div CLASS="section">
+
+      <table class="layout">
+	<tr>
+	  <td class="layout">
+	    
+	    <p class="subhead">On this page:</p>
+
+	    <ul>
+	      <li><a href="#intro">Introduction</a></li>
+	      <li><a href="#preparation">Preparing your submission</a></li>
+	      <li><a href="#supporteddata">Supported data file types</a></li>
+	      <li><a href="#submission">The MAGE-TAB submissions system</a></li>
+	      <li><a href="#postsubs">After completing your experiment submission</a></li>
+	      <li><a href="#magetabtips">Tips on creating a MAGE-TAB spreadsheet</a></li>
+	      <li><a href="#realexamples">Example MAGE-TAB documents</a></li>
+	    </ul>
+
+	    <p class="subhead">See also:</p>
+
+	    <ul>
+	      <li><a href="magetab_docs.html">Constructing a MAGE-TAB document</a>, including:</li>
+	      <ul>
+		<li><a href="idf.html">IDF detail</a></li>
+		<li><a href="sdrf.html">SDRF detail</a></li>
+	      </ul>
+	      <li><a href="datafiles.html">Supported data file formats</a></li>
+	    </ul>
+	  </td>
+	  <td class="layout" valign="top">
+	    <div CLASS="imghead">
+	      <a href="http://www.ebi.ac.uk/arrayexpress/">
+		<img src="aelogo.png" border="0" alt="ArrayExpress logo">
+	      </a>
+	    </div>
+	  </td>
+
+	</tr>
+      </table>
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="intro">Introduction</a></div>
+      
+      <p class="text">MAGE-TAB is a new data format, designed by
+      members of the microarray community as a simplified standard
+      representation format for microarray data. MAGE-TAB documents
+      can be used to provide experimental annotations for data
+      submissions to the ArrayExpress repository database. MAGE-TAB is
+      a tabular format, easily edited in any spreadsheet-based
+      application (e.g., Microsoft Excel). The format is designed to
+      be able to describe the most common experimental designs,
+      including annotation required by the <acronym title="Minimum
+      Information About A Microarray Experiment">MIAME</acronym>
+      standard. MAGE-TAB is suitable for submitting both
+      single-channel (e.g. Affymetrix) and two-channel data. It is
+      somewhat related to the older Tab2MAGE format, but allows for
+      much greater flexibility in describing microarray
+      investigations.</p>
+
+      <p class="text">A typical MAGE-TAB document consists of two
+      parts, the <acronym title="Investigation Design
+      Format">IDF</acronym> and the <acronym title="Sample and Data
+      Relationship Format">SDRF</acronym>. The IDF contains overall
+      information about the investigation, including the title,
+      description, publication and contact details. It also contains
+      information on the protocols used. The SDRF component describes
+      the links between each sample step and the data acquisition and
+      analysis steps, linking each ultimately to the data files.</p>
+      
+      <p class="text">MAGE-TAB can be used to submit microarray data
+      to ArrayExpress in one of two ways. For most users, we recommend
+      using our <a
+      href="http://www.ebi.ac.uk/cgi-bin/microarray/magetab.cgi">online
+      MAGE-TAB submission system</a> to create a template document,
+      which should then be filled in by the user and uploaded to our
+      web site alongside their data files.</p>
+
+      <p class="text">For advanced users who wish to generate MAGE-ML
+      from their MAGE-TAB documents, we provide a set of downloadable
+      scripts to help with document conversion:</p>
+
+      <ul>
+	<li><a href="../index.html#magetab">MAGE-TAB to MAGE-ML</a></li>
+	<li><a href="../index.html#tabconverter">Tab2MAGE to MAGE-TAB</a></li>
+	<li><a href="../index.html#tab2mage">Tab2MAGE to MAGE-ML</a></li>
+      </ul>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="preparation">Preparing your submission</a></div>
+
+      <p class="text">Our MAGE-TAB submissions page is here: <a
+      href="http://www.ebi.ac.uk/cgi-bin/microarray/magetab.cgi">ArrayExpress
+      MAGE-TAB submissions</a>.</p>
+
+      <p class="text">There is general information about ArrayExpress
+      submissions, accession number assignment and data privacy
+      here: <a
+      href="http://www.ebi.ac.uk/microarray/submissions.html">ArrayExpress
+      submissions help page</a> </p>
+
+      <p>To submit data using MAGE-TAB you will need to provide the
+      following information:</p>
+
+      <ul>
+
+	<li class="text">Protocols describing your sample growth/treatment and
+	processing steps.</li>
+
+	<li class="text">The ArrayExpress accession number of the
+	array design (platform) used - <a
+	href="http://www.ebi.ac.uk/miamexpress/help/array_designs.html">more
+	information on array designs here</a>. </li>
+
+	<li class="text">A description of your experiment.</li>
+
+	<li class="text">Details of all the samples used in your experiment.</li>
+	
+	<li class="text">Raw data files (e.g. .CEL, .gpr, .txt) for each
+	hybridization you performed.</li>
+
+	<li class="text">Per-hyb normalized data and/or a <a
+	href="magetab_docs.html#datamatrix">Data Matrix</a> file
+	(submission of processed data is required for full <acronym
+	title="Minimum Information About A Microarray
+	Experiment">MIAME</acronym> compliance, but we will still
+	accept your submission if this is not available).</li>
+
+      </ul>
+
+      <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="supporteddata">Supported data file types</a></div>
+
+      <p class="text">This table shows which files you can
+      submit using MAGE-TAB. Do not edit your raw data files. There
+      are definitions and more information on how to format normalized
+      and combined data here:</p>
+
+      <ul>
+	<li><a href="datafiles.html">Supported raw data file formats</a></li>
+	<li><a href="magetab_docs.html#datamatrix">MAGE-TAB Data Matrix format</a></li>
+      </ul>
+
+      <table class="information" summary="This table shows which files you can submit using MAGE-TAB">
+	<tr>
+	  <th>Technology</th>
+	  <th>Data type </th>
+	  <th>File format</th>
+	  <th>Quantity</th>
+	</tr>
+	<tr>
+	  <td>Affymetrix</td>
+	  <td>Raw</td>
+	  <td>CEL plus EXP </td>
+	  <td>1 CEL per hybridization - REQUIRED*<br />1 EXP per hybridization - optional</td>
+	</tr>
+	<tr>
+	  <td> </td>
+	  <td>Normalized</td>
+	  <td>CHP and/or .txt </td>
+	  <td>1 or more CHP and/or  .txt per hybridization</td>
+	</tr>
+	<tr>
+	  <td> </td>
+	  <td>Combined "Data Matrix" file </td>
+	  <td>.txt</td>
+	  <td>1 or more .txt per experiment</td>
+	</tr>
+	<tr>
+	  <td>Other</td>
+	  <td>Raw</td>
+	  <td>.gpr or .txt </td>
+	  <td>1 or more .gpr or .txt per hybridization - REQUIRED*</td>
+	</tr>
+	<tr>
+	  <td> </td>
+	  <td>Normalized</td>
+	  <td>.txt</td>
+	  <td>1 or more .txt per hybridization</td>
+	</tr>
+	<tr>
+	  <td> </td>
+	  <td>Combined "Data Matrix" file</td>
+	  <td>.txt</td>
+	  <td>1 or more .txt per experiment</td>
+	</tr>
+      </table>
+
+      <p>* Raw files are required for <acronym title="Minimum
+      Information About A Microarray Experiment">MIAME</acronym>
+      compliance, but if you really cannot provide them and you have
+      processed data instead, then we can still accept the MAGE-TAB
+      submission.</p>
+
+      <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="submission">The MAGE-TAB submissions system</a></div>
+
+      <p class="text">Once you have finished preparing your
+      MAGE-TAB document, please upload it with your data files to the
+      ArrayExpress <a
+      href="http://www.ebi.ac.uk/cgi-bin/microarray/magetab.cgi">MAGE-TAB
+      submissions website</a>.</p>
+
+      <p class="text">To submit data using this website:</p>
+
+      <ol>
+	
+	  <li class="text">Login or create a new user account if this
+	  is your first MAGE-TAB submission (note - previous Tab2MAGE
+	  submitters may re-use their old account).</li>
+
+	  <li class="text">Click 'Create experiment' to start your submission.</li>
+
+	  <li class="text">Enter your experiment name. If you want to
+	  generate a template spreadsheet then select one or more
+	  terms from each of the lists provided to describe your
+	  experiment (optional). Click 'Save experiment'.</li>
+
+	  <li class="text">Click 'Generate template' if you want to
+	  use the template (optional).</li>
+
+	  <li class="text">Click 'Upload files' then upload your
+	  completed spreadsheet and one or more zipped archives
+	  containing all your data files. If you are using separate
+	  IDF and SDRF documents, include your SDRF file in your
+	  zipped data file archive.</li>
+
+	  <li class="text">Click 'Submit experiment' to send this submission to us
+	  for curation.</li>
+
+      </ol>
+
+      <p class="text">If you have any questions please email the curation team at
+	<script type="text/javascript">
+	  <!--
+	  var aname = "miamexpress@ebi.ac.uk";
+	  document.write("<a href='" + "mail" + "to:" + aname + "?subject=MAGE-TAB submission'>" + aname + "<" + "/a>")
+	  //-->
+	</script>.
+	If you are emailing about a specific submission tell us your
+	username and experiment name in the email.</p>
+
+      <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="postsubs">After completing your experiment submission</a></div>
+
+      <ul>
+
+	<li class="text"> You will receive a immediate confirmation
+	email from the MAGE-TAB submission system saying that your
+	experiment has been submitted.</li>
+
+	<li class="text">Your submission will be put into curation
+	status and locked so that you cannot edit it. If you need to
+	edit the submission after this time please email us at
+	<script type="text/javascript">
+	  <!--
+	  var aname = "miamexpress@ebi.ac.uk";
+	  document.write("<a href='" + "mail" + "to:" + aname + "?subject=MAGE-TAB submission'>" + aname + "<" + "/a>")
+	  //-->
+	</script>.
+	</li>
+
+	<li class="text"> The curation team will review your
+	submission and will email you with any questions. </li>
+
+	<li class="text">We will send you an accession number when all
+	the required information has been provided.</li>
+
+	<li class="text"> We will load your experiment into
+	ArrayExpress and provide you with a reviewer login for viewing
+	the data before it is made public. </li>
+
+	<li class="text">To check the status of your experiment
+	submission email us at
+	  <script type="text/javascript">
+	    <!--
+	    var aname = "miamexpress@ebi.ac.uk";
+	    document.write("<a href='" + "mail" + "to:" + aname + "?subject=MAGE-TAB submission'>" + aname + "<" + "/a>")
+	    //-->
+	  </script>.
+	  and remember to include your username and experiment name so
+	  that we can identify your submission. </li>
+
+      </ul>
+
+      <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="magetabtips">Tips on creating a MAGE-TAB spreadsheet</a></div>
+
+      <p class="text">In most cases the MAGE-TAB document templates
+      generated by our <a
+      href="http://www.ebi.ac.uk/cgi-bin/microarray/magetab.cgi">MAGE-TAB
+      submissions website</a> should help to get you started. To
+      create a template you will need to create a new user account
+      and start a new experiment submission. The system then asks you
+      for a few basic details concerning your experiment and generates
+      a template document which you can download and edit on your
+      computer. If you have submitted MAGE-TAB to us before you can
+      skip these steps and re-use your old template.</p>
+
+      <p class="text">In some cases our standard templates may be too
+      simple to capture all the detail of your experiment. To describe
+      experiments which do not fit into our templates, the full
+      expressivity of MAGE-TAB can be used instead. Please see <a
+      href="magetab_docs.html">these MAGE-TAB help notes</a> for
+      further information on what is possible with MAGE-TAB, or read
+      the <a href="http://www.mged.org/mage-tab/">MAGE-TAB
+      specification document</a>. It is hoped that our template
+      generation system will suffice for most users.</p>
+
+      <p class="text">Here are some general tips on submitting
+      MAGE-TAB documents to ArrayExpress:</p>
+
+      <ul>
+
+	<li class="text">The templates generated by our system combine
+	the IDF and SDRF components into a single document, to further
+	simplify the process of submitting your data. If you prefer to
+	create separate IDF and SDRF documents, these can also be
+	uploaded to the submission web site.</li>
+
+	<li class="text">In the 'Experiment Description' part of the
+	IDF section please clearly explain what you did in your
+	experiment - this will help the curation team to check and
+	process your spreadsheet. </li>
+
+	<li class="text">Enter an estimated release date in the
+	'Public Release Date' part of the IDF section. We will set your
+	data to be made public on this date but you can ask us to
+	change this later.</li>
+
+	<li class="text">When describing your protocols in the IDF
+	section, give each of your protocols a unique identifier in
+	the 'Protocol Name' row, e.g. 'My protocol 1'. Use these names
+	in the SDRF section to refer to the relevant protocols.</li>
+
+	<li class="text">You may re-use previously submitted protocols
+	by entering their accession numbers directly into the
+	'Protocol Name' IDF row. You may retrieve these accessions
+	using the ArrayExpress web page. If your protocols have not
+	yet been loaded into ArrayExpress, please contact the
+	  <script type="text/javascript">
+	    <!--
+	    var aname = "miamexpress@ebi.ac.uk";
+	    document.write("<a href='" + "mail" + "to:" + aname + "?subject=MAGE-TAB submission'>curators<" + "/a>")
+	    //-->
+	  </script>
+	  to obtain the accession numbers.
+	</li>
+	
+	<li class="text">The SDRF section is a table containing all
+	the information about the samples you used and the
+	hybridizations you performed. It must include the names of the
+	data files that were produced for each hybridization.</li>
+
+	<li class="text">For most two-channel experiments the SDRF
+	section will contain two lines for each hybridization to
+	represent the two channels. Single-channel experiments usually
+	have one line per hybridization.</li>
+
+	<li class="text">The number of 'Characteristics' columns in
+	the SDRF section will depend on how much information you have
+	about each of your samples. If you are using a template
+	spreadsheet some suggested columns will be included but you
+	can add or remove 'Characteristics' and 'Factor Value' columns
+	as required.</li>
+
+      </ul>
+
+      <p class="text">For further help with MAGE-TAB document
+      preparation, including the definitions of all the available
+      fields, please see the following pages:</p>
+
+      <ul>
+	  <li><a href="magetab_docs.html">MAGE-TAB overview</a></li>
+	  <li><a href="idf.html">IDF detail</a></li>
+	  <li><a href="sdrf.html">SDRF detail</a></li>
+      </ul>
+
+      <p class="text">For further help with MAGE-TAB submissions to ArrayExpress, please contact the
+	<script type="text/javascript">
+	  <!--
+	  var aname = "miamexpress@ebi.ac.uk";
+	  document.write("<a href='" + "mail" + "to:" + aname + "?subject=MAGE-TAB submission'>ArrayExpress curators<" + "/a>")
+	  //-->
+	</script>
+      </p>
+
+      <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="realexamples">Real-world Examples</a></div>
+      
+      <p class="text">Below is a selection of some of the real-world
+      curated MAGE-TAB documents, created from Tab2MAGE spreadsheets
+      submitted to ArrayExpress over the past few months:</p>
+
+      <table class="information">
+	
+	  <tr>
+	    <th>IDF</th>
+	    <th>SDRF</th>
+	    <th>Experiment name</th>
+	    <th>Platform</th>
+	    <th>Link to experiment</th>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/magetab/real/E-TABM-16.idf">E-TABM-16.idf</a></td>
+	    <td><a href="../examples/magetab/real/E-TABM-16_sdrf.txt">E-TABM-16_sdrf.txt</a></td>
+	    <td>FDA-CDER MTS RNA reagent cross platform test</td>
+	    <td>Multiple platforms</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-16">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/magetab/real/E-TABM-18.idf">E-TABM-18.idf</a></td>
+	    <td><a href="../examples/magetab/real/E-TABM-18_sdrf.txt">E-TABM-18_sdrf.txt</a></td>
+	    <td>Transcription profiling of 35 different Arabidopsis thaliana ecotypes</td>
+	    <td>Affymetrix</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-18">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/magetab/real/E-TABM-22.idf">E-TABM-22.idf</a></td>
+	    <td><a href="../examples/magetab/real/E-TABM-22_sdrf.txt">E-TABM-22_sdrf.txt</a></td>
+	    <td>Transcription profiling of human lung cancers and lung cancer cell lines miRNA expression</td>
+	    <td>One-channel custom array</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-22">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/magetab/real/E-TABM-33.idf">E-TABM-33.idf</a></td>
+	    <td><a href="../examples/magetab/real/E-TABM-33_sdrf.txt">E-TABM-33_sdrf.txt</a></td>
+	    <td>Transcription profiling of zebrafish development</td>
+	    <td>Affymetrix</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-33">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/magetab/real/E-TABM-35.idf">E-TABM-35.idf</a></td>
+	    <td><a href="../examples/magetab/real/E-TABM-35_sdrf.txt">E-TABM-35_sdrf.txt</a></td>
+	    <td>Comparative genomic hybridization of 25 Coxiella burnetii isolates relative to the Nine Mile (RSA493) reference isolate</td>
+	    <td>Affymetrix</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-35">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/magetab/real/E-TABM-54.idf">E-TABM-54.idf</a></td>
+	    <td><a href="../examples/magetab/real/E-TABM-54_sdrf.txt">E-TABM-54_sdrf.txt</a></td>
+	    <td>Comparative genome hybridization of 137 Bordetella pertussis strains</td>
+	    <td>Two-color custom array</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-54">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/magetab/real/E-TABM-66.idf">E-TABM-66.idf</a></td>
+	    <td><a href="../examples/magetab/real/E-TABM-66_sdrf.txt">E-TABM-66_sdrf.txt</a></td>
+	    <td>Transcription profiling of normal and malignant human breast epithelial cells</td>
+	    <td>Multiple platforms</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-66">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/magetab/real/E-TABM-70.idf">E-TABM-70.idf</a></td>
+	    <td><a href="../examples/magetab/real/E-TABM-70_sdrf.txt">E-TABM-70_sdrf.txt</a></td>
+	    <td>Transcription profiling of human cell lines treated with cytochalasin D and nocodazole with the aim of characterising tetraploid clones</td>
+	    <td>Agilent</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-70">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/magetab/real/E-TABM-102.idf">E-TABM-102.idf</a></td>
+	    <td><a href="../examples/magetab/real/E-TABM-102_sdrf.txt">E-TABM-102_sdrf.txt</a></td>
+	    <td>Transcription profiling of wild type and ATF3 -/-mouse bone marrow macrophages stimulated with lipopolysaccharide over time</td>
+	    <td>Affymetrix</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-102">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/magetab/real/E-TABM-134.idf">E-TABM-134.idf</a></td>
+	    <td><a href="../examples/magetab/real/E-TABM-134_sdrf.txt">E-TABM-134_sdrf.txt</a></td>
+	    <td>WGA-LCM and Genomewide Survey of Lung Cancer</td>
+	    <td>Affymetrix</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-134">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/magetab/real/E-TABM-136.idf">E-TABM-136.idf</a></td>
+	    <td><a href="../examples/magetab/real/E-TABM-136_sdrf.txt">E-TABM-136_sdrf.txt</a></td>
+	    <td>Transcription profiling of human and chimpanzee heart, brain, testis and lymphblastoid cell lines to study functionality of intergenic transcription</td>
+	    <td>Affymetrix</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-136">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/magetab/real/E-TABM-140.idf">E-TABM-140.idf</a></td>
+	    <td><a href="../examples/magetab/real/E-TABM-140_sdrf.txt">E-TABM-140_sdrf.txt</a></td>
+	    <td>Chromatin immunoprecipitation (ChIP-chip) of human erythroleukemia cell line K-562 with anti-histone antibodies using an ENCODE array</td>
+	    <td>Two-color custom array</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-140">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/magetab/real/E-TABM-163.idf">E-TABM-163.idf</a></td>
+	    <td><a href="../examples/magetab/real/E-TABM-163_sdrf.txt">E-TABM-163_sdrf.txt</a></td>
+	    <td>Transcription profiling of murine presomitic mesoderms of 17 samples at various time points to identify cyclic genes of the mouse segmentation clock</td>
+	    <td>Affymetrix</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-163">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/magetab/real/E-MEXP-880.idf">E-MEXP-880.idf</a></td>
+	    <td><a href="../examples/magetab/real/E-MEXP-880_sdrf.txt">E-MEXP-880_sdrf.txt</a></td>
+	    <td>Methylation profiling of normal and cancerous breast cells from human patients and cell lines in a 125 kB region of the HOXA cluster</td>
+	    <td>Two-color custom array</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-MEXP-880">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/magetab/real/solexa_example.idf">solexa_example.idf</a></td>
+	    <td><a href="../examples/magetab/real/solexa_example.sdrf.txt">solexa_example.sdrf.txt</a></td>
+	    <td>Example MAGE-TAB documents for a hypothetical high-throughput sequencing submission (e.g. Solexa, 454)</td>
+	    <td>Solexa</td>
+	    <td>Not available</td>
+	  </tr>
+      </table>
+
+      <p CLASS="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <hr>
+    <a href="http://sourceforge.net">
+
+      <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" />
+    </a>
+    <br>
+<!-- Created: Thu Nov 24 14:18:20 GMT 2005 -->
+<!-- hhmts start -->
+Last modified: Tue Feb 26 14:12:35 GMT 2008
+<!-- hhmts end -->
+  </body>
+</html>
\ No newline at end of file
diff --git a/docs/magetab_vs_tab2mage.html b/docs/magetab_vs_tab2mage.html
new file mode 100644
index 0000000..608243b
--- /dev/null
+++ b/docs/magetab_vs_tab2mage.html
@@ -0,0 +1,202 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+  <head>
+    <title>MAGE-TAB and Tab2MAGE: a comparison</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+    <link rel="stylesheet" href="style.css" type="text/css">
+  </head>
+
+  <body>
+    <div><a name="top"></a>
+
+    <table class="layout">
+	<tr>
+	  <td class="whitetitle" width="270"><a href="../index.html"><img src="T2M_logo.png" border="0" height="50" alt="Tab2MAGE logo"></a> <a href="../index.html#magetab"><img src="MAGETAB_logo.png" border="0" height="50" alt="MAGE-TAB logo"></a></td>
+	  <td class="pagetitle">MAGE-TAB and Tab2MAGE: a comparison</td>
+	</tr>
+    </table>
+    </div>
+
+    <div CLASS="section">
+
+      <table class="layout">
+	<tr>
+	  <td class="layout">
+	    
+	    <p class="subhead">On this page:</p>
+
+	    <ul>
+	      <li><a href="#intro">Introduction</a></li>
+	      <li><a href="#proscons">Pros and Cons of MAGE-TAB and Tab2MAGE</a></li>
+	    </ul>
+
+	    <p class="subhead">See also:</p>
+
+	    <ul>
+	      <li><a href="aesubs.html">Submitting Tab2MAGE to ArrayExpress</a>, including:</li>
+	      <ul>
+  	        <li><a href="spreadsheet.html">Constructing a Tab2MAGE spreadsheet</a></li>
+		<li><a href="detail.html">Tab2MAGE spreadsheet detail</a></li>
+	      </ul>
+	      <li><a href="magetab_subs.html">Submitting MAGE-TAB to ArrayExpress</a>, including:</li>
+	      <ul>
+  	        <li><a href="magetab_docs.html">Constructing a MAGE-TAB document</a></li>
+		<li><a href="idf.html">IDF detail</a></li>
+		<li><a href="sdrf.html">SDRF detail</a></li>
+	      </ul>
+	    </ul>
+	  </td>
+	  <td class="layout" valign="top">
+	    <div CLASS="imghead">
+	      <a href="http://www.ebi.ac.uk/arrayexpress/">
+		<img src="aelogo.png" border="0" alt="ArrayExpress logo">
+	      </a>
+	    </div>
+	  </td>
+
+	</tr>
+      </table>
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="intro">Introduction</a></div>
+      
+      <p class="text">MAGE-TAB is a new data format, designed by
+      members of the microarray community as a simplified standard
+      representation format for microarray data. In contrast, Tab2MAGE
+      is an older, simplified tabular format developed by ArrayExpress
+      some years ago. In most cases these two formats can be used
+      interchangeably, but there are a number of situations in which
+      one or the other may have an advantage. As a rule, anything that
+      can be expressed in Tab2MAGE can also be described in MAGE-TAB,
+      but <em>not</em> vice versa. This is because MAGE-TAB extends
+      the ideas behind Tab2MAGE to give it a far greater flexibility
+      and scope.</p>
+
+      <p class="text">We have created a <a
+      href="http://tab2mage.sourceforge.net/index.html#tabconverter">Tab2MAGE
+      to MAGE-TAB converter tool</a> which is now <a
+      href="http://sourceforge.net/project/showfiles.php?group_id=120325&package_id=226041">available
+      for download here</a>.</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="proscons">Pros and Cons of MAGE-TAB vs. Tab2MAGE</a></div>
+
+      <p class="text">Below is a table summarising the strengths and
+      weaknesses of these two tabular formats:</p>
+
+      <table class="information">
+	  <tr>
+	    <th></th><th>Tab2MAGE</th><th>MAGE-TAB</th>
+	  </tr>
+	  <tr>
+	    <th>Simpler to use for array-based gene expression data <a href="#four">(4)</a></th>
+	    <td><font color="green">Yes</font></td>
+	    <td><font color="red">No</font></td>
+	  </tr>
+	  <tr>
+	    <th>Can be annotated with ontology term accession and source <a href="#one">(1)</a></th>
+	    <td><font color="red">No</font></td>
+	    <td><font color="green">Yes</font></td>
+	  </tr>
+	  <tr>
+	    <th>Supports metabolomic and proteomic data <a href="#two">(2)</a></th>
+	    <td><font color="red">No</font></td>
+	    <td><font color="green">Yes</font></td>
+	  </tr>
+	  <tr>
+	    <th>Supports high-throughput sequencing data <a href="#two">(2)</a></th>
+	    <td><font color="red">No</font></td>
+	    <td><font color="green">Yes</font></td>
+	  </tr>
+	  <tr>
+	    <th>Experiment must follow a standard, predefined scheme <a href="#three">(3)</a></th>
+	    <td><font color="green">Yes</font></td>
+	    <td><font color="red">No</font></td>
+	  </tr>
+	  <tr>
+	    <th>Column ordering is flexible <a href="#four">(4)</a></th>
+	    <td><font color="green">Yes</font></td>
+	    <td><font color="red">No</font></td>
+	  </tr>
+	  <tr>
+	    <th>Columns with the same name can appear more than once <a href="#four">(4)</a></th>
+	    <td><font color="red">No</font></td>
+	    <td><font color="green">Yes</font></td>
+	  </tr>
+	  <tr>
+	    <th>Tries to "Do What You Mean" <a href="#four">(4)</a></th>
+	    <td><font color="green">Yes</font></td>
+	    <td><font color="red">No</font></td>
+	  </tr>
+	  <tr>
+	    <th>Forces consistency of internal references <a href="#four">(4)</a></th>
+	    <td><font color="red">No</font></td>
+	    <td><font color="green">Yes</font></td>
+	  </tr>
+	  <tr>
+	    <th>Supported by other members of the microarray community</th>
+	    <td><font color="red">No</font></td>
+	    <td><font color="green">Yes</font></td>
+	  </tr>
+      </table>
+
+      <p class="text"><a name="one" class="subhead">1.</a> While
+      Tab2MAGE allows (and encourages) the use of ontology terms to
+      annotate your data, it provides no means to include the source
+      of these terms, or formal identifiers/accessions. MAGE-TAB
+      corrects this by adding "Term Source REF" and "Term Accession
+      Number" fields to help capture this information.</p>
+
+      <p class="text"><a name="two" class="subhead">2.</a> Tab2MAGE
+      was developed solely to describe microarray experiments, and as
+      such the concept of "hybridization" is central. MAGE-TAB has
+      been extended to encompass other kinds of biological assay,
+      enabling the user to describe data from multiple technologies
+      (e.g., proteomics, metabolomics, high-throughput sequencing) in
+      a single document.</p>
+
+      <p class="text"><a name="three" class="subhead">3.</a> Tab2MAGE
+      assumes that your experiment is a standard microarray
+      hybridization study, and that your sample processing followed a
+      defined series of steps: Source -> Sample -> Extract -> Labeled
+      Extract -> Hybridization (for ChIP-chip experiments an
+      Immunoprecipitate step may also be used). In contrast, MAGE-TAB
+      allows you to create sample processing schemes of arbitrary
+      complexity.</p>
+
+      <p class="text"><a name="four" class="subhead">4.</a> While
+      increasing the flexibility of the tabular format, MAGE-TAB has
+      sacrificed some ease-of-use. For example, while the columns in a
+      Tab2MAGE spreadsheet can be ordered in whichever way suits you,
+      MAGE-TAB constrains this order to follow the steps performed in
+      the experiment. That is, columns in a MAGE-TAB SDRF must be
+      ordered left-to-right in chronological order of
+      execution. MAGE-TAB also forces you to be explicit about all the
+      steps of your experiment, while Tab2MAGE will attempt to infer
+      what was done from a minimal subset of spreadsheet
+      columns. Finally, there are several places in a MAGE-TAB SDRF
+      document which may refer back to elements defined in the IDF,
+      and these must be internally consistent. No such requirement is
+      made for Tab2MAGE documents.</p>
+
+      <p CLASS="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <hr>
+    <a href="http://sourceforge.net">
+
+      <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" />
+    </a>
+    <br>
+<!-- Created: Thu Nov 24 14:18:20 GMT 2005 -->
+<!-- hhmts start -->
+Last modified: Thu Feb  7 15:38:30 GMT 2008
+<!-- hhmts end -->
+  </body>
+</html>
\ No newline at end of file
diff --git a/docs/modules.html b/docs/modules.html
new file mode 100644
index 0000000..45f3697
--- /dev/null
+++ b/docs/modules.html
@@ -0,0 +1,282 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+  <head>
+    <title>Tab2MAGE and MAGE-TAB Perl modules</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+    <link rel="stylesheet" href="style.css">
+  </head>
+
+  <body>
+    <table class="layout">
+	<tr>
+	  <td class="whitetitle" width="270"><a href="../index.html"><img src="T2M_logo.png" border="0" height="50" alt="Tab2MAGE logo"></a> <a href="../index.html#magetab"><img src="MAGETAB_logo.png" border="0" height="50" alt="MAGE-TAB logo"></a></td>
+	  <td class="pagetitle">Perl module documentation</td>
+	</tr>
+    </table>
+
+    <div CLASS="section">
+
+      <table class="layout">
+	<tr>
+	  <td class="layout">
+	    
+	    <ul>
+	      <li><a href="#config">Configuration</a></li>
+	      <li><a href="#common">Common modules</a></li>
+	      <li><a href="#magetab">MAGE-TAB parsing</a></li>
+	      <li><a href="#expt_check">Experiment Checker modules</a></li>
+	      <li><a href="#datafile">Data file parsing modules</a></li>
+	      <li><a href="#affy">Affymetrix file parsing modules</a></li>
+	      <li><a href="#visualize">Experiment design graph visualization</a></li>
+	      <li><a href="#arraymage">Array design MAGE-ML generation</a></li>
+	      <li><a href="#tracking">Submissions tracking database</a></li>
+	      <li><a href="#tab2mage">Tab2MAGE column headings</a></li>
+	    </ul>
+
+	  </td>
+	  <td class="layout" valign="top">
+	    <div CLASS="imghead">
+	      <a href="http://www.ebi.ac.uk/arrayexpress/">
+		<img src="aelogo.png" border="0" alt="ArrayExpress logo">
+	      </a>
+	    </div>
+	  </td>
+
+	</tr>
+      </table>
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="config">Configuration</a></div>
+
+      <p class="text">Configuration for all the modules is handled by
+      a single class, which generates a Config::YAML object that is
+      then imported into each module that needs it. Documentation on
+      configuration options is available here:</p>
+
+      <ul>
+	<li><a href="ArrayExpress/Curator/Config.html">ArrayExpress::Curator::Config</a></li>
+      </ul>
+
+      <p class="text">The actual configuration file to be used can be
+      set by editing the installed version of this module ("site
+      config"); alternatively the Config.yml file included with the
+      Tab2MAGE package may be edited directly ("module config"). For
+      convenience, the configuration system will also look for a file
+      named ".tab2mage.conf" in the user's home directory and use that
+      if found ("user config"). All found configuration files are
+      parsed, with priority for each specified option running in this
+      order: user config; site config; module config. </p>
+    
+      <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="common">Common modules</a></div>
+
+      <p class="text">The following modules provide a series of
+      miscellaneous functions used by many of the package
+      subsystems:</p>
+
+      <ul>
+	<li><a href="ArrayExpress/Curator/Logger.html">ArrayExpress::Curator::Logger</a></li>
+	<li><a href="ArrayExpress/Curator/Common.html">ArrayExpress::Curator::Common</a></li>
+	<li><a href="ArrayExpress/Curator/Database.html">ArrayExpress::Curator::Database</a></li>
+      </ul>
+    
+      <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="magetab">MAGE-TAB parsing</a></div>
+
+      <p class="text">The core MAGE-TAB parsing subsystem consists of
+      the following modules:</p>
+
+      <ul>
+	<li><a href="ArrayExpress/MAGETAB.html">ArrayExpress::MAGETAB</a></li>
+      </ul>
+
+      <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="expt_check">Experiment Checker modules</a></div>
+
+      <p class="text">The Experiment Checker subsystem consists of a
+      core ExperimentChecker class which is subclassed to provide
+      support for MAGE-TAB, Tab2MAGE, MIAMExpress or stand-alone
+      checks:</p>
+
+      <ul>
+	<li><a href="ArrayExpress/Curator/ExperimentChecker.html">ArrayExpress::Curator::ExperimentChecker</a></li>
+	<li><a href="ArrayExpress/MAGETAB/Checker.html">ArrayExpress::MAGETAB::Checker</a></li>
+	<li><a href="ArrayExpress/Curator/Validate.html">ArrayExpress::Curator::Validate</a></li>
+	<li><a href="ArrayExpress/Curator/MIAMExpress.html">ArrayExpress::Curator::MIAMExpress</a></li>
+	<li><a href="ArrayExpress/Curator/Standalone.html">ArrayExpress::Curator::Standalone</a></li>
+      </ul>
+
+      <p class="text">The following helper modules provide functions
+      used by these checker modules:</p>
+
+      <ul>
+	<li><a href="ArrayExpress/Curator/Report.html">ArrayExpress::Curator::Report</a></li>
+	<li><a href="ArrayExpress/Curator/Entrez_list.html">ArrayExpress::Curator::Entrez_list</a></li>
+      </ul>
+      
+      <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="datafile">Data file parsing modules</a></div>
+
+      <p class="text">The core data file parsing subsystem consists of
+      a main Datafile class, a Parser class to handle MAGE-ML
+      generation, and helper classes to handle quantitation type and
+      array design information:</p>
+
+      <ul>
+	<li><a href="ArrayExpress/Datafile.html">ArrayExpress::Datafile</a></li>
+	<li><a href="ArrayExpress/Datafile/Parser.html">ArrayExpress::Datafile::Parser</a></li>
+	<li><a href="ArrayExpress/Datafile/QT_list.html">ArrayExpress::Datafile::QT_list</a></li>
+	<li><a href="ArrayExpress/Datafile/ArrayDesign.html">ArrayExpress::Datafile::ArrayDesign</a></li>
+      </ul>
+
+      <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="affy">Affymetrix file parsing modules</a></div>
+
+      <p class="text">Affymetrix data files are handled as special
+      cases throughout the <a href="#datafile">Datafile
+      subsystem</a>. The following modules provide comprehensive
+      support for reading most commonly-used Affymetrix gene
+      expression, SNP, and resequencing data files:</p>
+
+      <ul>
+	<li><a href="ArrayExpress/Datafile/Affymetrix.html">ArrayExpress::Datafile::Affymetrix</a></li>
+	<li><a href="ArrayExpress/Datafile/Affymetrix/CDFFactory.html">ArrayExpress::Datafile::Affymetrix::CDFFactory</a></li>
+	<li><a href="ArrayExpress/Datafile/Affymetrix/CELFactory.html">ArrayExpress::Datafile::Affymetrix::CELFactory</a></li>
+	<li><a href="ArrayExpress/Datafile/Affymetrix/CHPFactory.html">ArrayExpress::Datafile::Affymetrix::CHPFactory</a></li>
+	<li><a href="ArrayExpress/Datafile/Affymetrix/EXPFactory.html">ArrayExpress::Datafile::Affymetrix::EXPFactory</a></li>
+	<li><a href="ArrayExpress/Datafile/Affymetrix/Parser.html">ArrayExpress::Datafile::Affymetrix::Parser</a></li>
+	<li><a href="ArrayExpress/Datafile/Affymetrix/CDF.html">ArrayExpress::Datafile::Affymetrix::CDF</a></li>
+	<li><a href="ArrayExpress/Datafile/Affymetrix/CDF/GDAC_CDF.html">ArrayExpress::Datafile::Affymetrix::CDF::GDAC_CDF</a></li>
+	<li><a href="ArrayExpress/Datafile/Affymetrix/CDF/XDA_CDF.html">ArrayExpress::Datafile::Affymetrix::CDF::XDA_CDF</a></li>
+	<li><a href="ArrayExpress/Datafile/Affymetrix/CEL.html">ArrayExpress::Datafile::Affymetrix::CEL</a></li>
+	<li><a href="ArrayExpress/Datafile/Affymetrix/CEL/CELv3.html">ArrayExpress::Datafile::Affymetrix::CEL::CELv3</a></li>
+	<li><a href="ArrayExpress/Datafile/Affymetrix/CEL/CELv4.html">ArrayExpress::Datafile::Affymetrix::CEL::CELv4</a></li>
+	<li><a href="ArrayExpress/Datafile/Affymetrix/CHP.html">ArrayExpress::Datafile::Affymetrix::CHP</a></li>
+	<li><a href="ArrayExpress/Datafile/Affymetrix/CHP/CHPv12.html">ArrayExpress::Datafile::Affymetrix::CHP::CHPv12</a></li>
+	<li><a href="ArrayExpress/Datafile/Affymetrix/CHP/CHPv13.html">ArrayExpress::Datafile::Affymetrix::CHP::CHPv13</a></li>
+	<li><a href="ArrayExpress/Datafile/Affymetrix/CHP/CHPv8.html">ArrayExpress::Datafile::Affymetrix::CHP::CHPv8</a></li>
+	<li><a href="ArrayExpress/Datafile/Affymetrix/CHP/GDAC_CHP.html">ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP</a></li>
+	<li><a href="ArrayExpress/Datafile/Affymetrix/CHP/XDA_CHP.html">ArrayExpress::Datafile::Affymetrix::CHP::XDA_CHP</a></li>
+	<li><a href="ArrayExpress/Datafile/Affymetrix/EXP.html">ArrayExpress::Datafile::Affymetrix::EXP</a></li>
+      </ul>
+
+      <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="visualize">Experiment design graph visualization</a></div>
+
+      <p class="text">Limited support is provided for visualizing the
+      experiment design graphs represented by MAGE-TAB and Tab2MAGE
+      documents. For more detailed visualization of MAGE-ML documents, the <a
+      href="http://sourceforge.net/project/showfiles.php?group_id=120325&package_id=163063">MAGE-ML
+      Visualize</a> script is recommended.</p>
+
+      <ul>
+	<li><a href="ArrayExpress/Curator/Visualize.html">ArrayExpress::Curator::Visualize</a></li>
+      </ul>
+    
+      <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="arraymage">Array design MAGE-ML generation</a></div>
+
+      <p class="text">Due to their increasingly large size, generating
+      full MAGE-ML documents for the array designs in use today often
+      taxes the resources of the average desktop computer. The
+      ArrayMAGE modules were created to remove the memory and CPU
+      overhead of this process, allowing them to support arbitrarily
+      large array designs:</p>
+
+      <ul>
+	<li><a href="ArrayExpress/ArrayMAGE.html">ArrayExpress::ArrayMAGE</a></li>
+      </ul>
+    
+      <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="tracking">Submissions tracking database</a></div>
+
+      <p class="text">A data submissions tracking database subsystem
+      is included in the Tab2MAGE package. This database drives the
+      main ArrayExpress MAGE-TAB and Tab2MAGE submissions web pages,
+      the automated checking of submitted experiments and export to
+      MAGE-ML, and automated import of data from NCBI GEO (see the <a
+      href="http://sourceforge.net/project/showfiles.php?group_id=120325&package_id=243607">GEOImport
+      package</a>). It also provides back-end support for data quality
+      metrics calculation against the full ArrayExpress database.</p>
+
+      <ul>
+	<li><a href="ArrayExpress/AutoSubmission/DB.html">ArrayExpress::AutoSubmission::DB</a></li>
+	<li><a href="ArrayExpress/AutoSubmission/Creator.html">ArrayExpress::AutoSubmission::Creator</a></li>
+	<li><a href="ArrayExpress/AutoSubmission/WebForm.html">ArrayExpress::AutoSubmission::WebForm</a></li>
+	<li><a href="ArrayExpress/AutoSubmission/Spreadsheet.html">ArrayExpress::AutoSubmission::Spreadsheet</a></li>
+      </ul>
+    
+      <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="tab2mage">Tab2MAGE column headings</a></div>
+
+      <p class="text">Tab2MAGE spreadsheet parsing is documented,
+      albeit sparsely, here:</p>
+
+      <ul>
+	<li><a href="ArrayExpress/Curator/MAGE/Definitions.html">ArrayExpress::Curator::MAGE::Definitions</a></li>
+      </ul>
+
+      <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+
+
+    <hr>
+    <a href="http://sourceforge.net">
+      <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" />
+    </a>
+    <br>
+<!-- Created: Tue Apr 29 11:57:20 BST 2008 -->
+<!-- hhmts start -->
+Last modified: Wed Apr 30 15:53:04 BST 2008
+<!-- hhmts end -->
+  </body>
+</html>
diff --git a/docs/parse_affy.html b/docs/parse_affy.html
new file mode 100644
index 0000000..28940e4
--- /dev/null
+++ b/docs/parse_affy.html
@@ -0,0 +1,104 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>parse_affy.pl - an example Affymetrix data file parsing
+script.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#options">OPTIONS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>parse_affy.pl - an example Affymetrix data file parsing
+script.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ parse_affy.pl -i <Affymetrix data file (CEL or CHP)>
+               -o <tab-delimited data matrix output file>
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This script is a 'toy' script to illustrate the use of the
+ArrayExpress::Datafile::Affymetrix parsing module supplied with the
+Tab2MAGE distribution. It may nonetheless be useful in
+circumstances where other Affymetrix parsers fail. The script reads
+data files (CEL and CHP) in both old (version 3, GDAC) and new
+(version 4, XDA/GCOS) formats. The script then outputs any
+combination of the following: (a) a tab-delimited data matrix, (b)
+a set of MAGE identifiers making up the DesignElementDimension of
+the matrix, and/or (c) a set of identifiers comprising the
+QuantitationTypeDimension of the data matrix. Please see <a href=
+"ArrayExpress/Datafile/Affymetrix.html">the
+ArrayExpress::Datafile::Affymetrix manpage</a> for more information
+on the Affymetrix parsing API.</p>
+<hr />
+<h1><a name="options" id="options">OPTIONS</a></h1>
+<dl>
+<dt><strong><a name="item__2di_input_filename" id=
+"item__2di_input_filename"><strong>-i</strong> <code>input
+filename</code></a></strong></dt>
+<dd>
+<p>Affymetrix CEL or CHP data file to parse.</p>
+</dd>
+<dt><strong><a name="item__2do_output_filename" id=
+"item__2do_output_filename"><strong>-o</strong> <code>output
+filename</code></a></strong></dt>
+<dd>
+<p>File to be used for output (tab-delimited data).</p>
+</dd>
+<dt><strong><a name="item__2dd_ded_filename" id=
+"item__2dd_ded_filename"><strong>-d</strong> <code>DED
+filename</code></a></strong></dt>
+<dd>
+<p>File to which the DesignElementDimension is to be written
+(optional).</p>
+</dd>
+<dt><strong><a name="item__2dq_qtd_filename" id=
+"item__2dq_qtd_filename"><strong>-q</strong> <code>QTD
+filename</code></a></strong></dt>
+<dd>
+<p>File to which the QuantitationTypeDimension is to be written
+(optional).</p>
+</dd>
+<dt><strong><a name="item__2dc_input_cdf_filename" id=
+"item__2dc_input_cdf_filename"><strong>-c</strong> <code>input CDF
+filename</code></a></strong></dt>
+<dd>
+<p>Affymetrix library CDF file to use for CHP file parsing.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/sdrf.html b/docs/sdrf.html
new file mode 100644
index 0000000..ce32768
--- /dev/null
+++ b/docs/sdrf.html
@@ -0,0 +1,941 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+  <head>
+    <title>SDRF notes</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+    <link rel="stylesheet" href="style.css" type="text/css">
+  </head>
+
+  <body>
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100"><a href="../index.html"><img src="MAGETAB_logo.png" border="0" height="50" alt="MAGE-TAB logo"></a></td>
+	    <td class="pagetitle">SDRF help notes</td>
+	  </tr>
+      </table>
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="overall">Introduction</a></div>
+
+      <p class="text">This page provides detailed help on the
+      Sample and Data Relationship Format (SDRF), and lists all the available
+      tags for use in MAGE-TAB SDRF documents. For an overview of the
+      MAGE-TAB submission process, and examples of both IDF and SDRF,
+      please see these <a href="magetab_subs.html">submission help
+      notes</a> and <a href="magetab_docs.html">MAGE-TAB
+      overview</a>. For more detail on the MAGE-TAB format, please see
+      the <a href="http://www.mged.org/mage-tab/">MAGE-TAB
+      specification</a>.</p>
+
+      <p class="text">MAGE-TAB documents may optionally include
+      information on the sources of any controlled terms used,
+      providing users with the ability to link to ontologies,
+      databases or other sources of controlled vocabularies to
+      describe their experiment. These so-called "Term Sources" are
+      defined in the IDF and may be used throughout the document. If
+      they are not used, then all controlled vocabulary terms are
+      assumed to be user-defined.</p>
+	
+      <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="sdrf_layout">Overall SDRF layout</a></div>
+      
+      <p class="text">The SDRF file consists of a table in which each
+      hybridization channel is represented by a row, and columns
+      represent the steps of the experiment. In contrast to the
+      Tab2MAGE format, the ordering of these columns is important, and
+      should read left-to-right in chronological order. The overall
+      organization of this table is shown below. To get more detail on
+      the properties of each section, click on the relevant box:</p>
+
+      <map name="sdrf_layout_Map">
+	<area shape="rect" alt="Derived Array Data File" coords="557,402,716,493" href="#derivedarraydatafile_section"/>
+	<area shape="rect" alt="Scan" coords="86,402,245,493" href="#scan_section"/>
+	<area shape="rect" alt="Array Data File" coords="243,402,402,493" href="#arraydatafile_section"/>
+        <area shape="rect" alt="Normalization" coords="400,402,559,493" href="#normalization_section"/>
+        <area shape="rect" alt="Labeled Extract" coords="324,0,434,94" href="#labeledextract_section"/>
+        <area shape="rect" alt="Extract" coords="216,0,326,94" href="#extract_section"/>
+        <area shape="rect" alt="Sample" coords="108,0,218,94" href="#sample_section"/>
+        <area shape="rect" alt="Source" coords="0,0,110,94" href="#source_section"/>
+        <area shape="rect" alt="Hybridization" coords="268,168,428,281" href="#hybridization_section"/>
+      </map>
+      <div align="center"><img src="sdrf_layout.png" width="716" height="493" border="0" alt="SDRF layout" usemap="#sdrf_layout_Map"></div>
+
+      <p class="text">An experiment can be described in terms of a
+      graph, in which the graph nodes correspond to materials or data
+      files, and the graph edges (or arcs) correspond to
+      treatments. Each block in the diagram above would be represented
+      as a node in such a graph, with the treatments (protocols)
+      acting as edges. Each node block starts with a "Name" or "File"
+      column (e.g. "Extract Name", "Array Data File") identifying the
+      type of node, followed by a set of attribute columns. Each block
+      is separated from its predecessor by "<a
+      href="#protocol_section">Protocol REF</a>" graph edge
+      columns containing references to the "Protocol Name" values
+      defined in the IDF.</p>
+
+      <p class="text">A further set of columns is used to specify the
+      values for the variables ("experimental factors") within the
+      experiment. These <a href="#factorvalue_section">Factor
+      Value[]</a> columns reference the Experimental Factor Names
+      defined in the IDF, and should be placed after the hybridization
+      section (i.e., to the right of it, in or after the scanning,
+      normalization and data section in the image above). The contents
+      of these columns will usually duplicate those in a material <a
+      href="#characteristics">Characteristics</a> or a
+      protocol <a href="#parameter_value">Parameter Value</a>
+      column. See below for an example.</p>
+
+      <p CLASS="sectionfoot"> [ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="sdrfsections">Example SDRF sections</a></div>
+      
+      <p class="text"><a name="protocol_section"
+      class="subhead">Protocols</a><br/> In most cases, each treatment
+      within an experiment will be represented simply by a <a
+      href="#protocol_ref">Protocol REF</a> column containing
+      references to the <a
+      href="idf.html#protoname">Protocol Names</a> defined in the
+      IDF. If multiple protocols need to be chained together this can
+      be achieved using multiple Protocol REF columns.</p>
+
+      <p class="text">Additionally, any <a
+      href="idf.html#protoparam">Protocol Parameters</a> associated
+      with the protocol (as defined in the IDF) should have their
+      values listed after the Protocol REF column. For example, with this in your IDF:</p>
+
+      <table class="example">
+	<tr><td><a href="idf.html#protoname" class="plain">Protocol Name</a></td><td>My Ext. Protocol</td><td>My Labeling Protocol</td></tr>
+	<tr><td><a href="idf.html#prototype" class="plain">Protocol Type</a></td><td>nucleic_acid_extraction</td><td>labeling</td></tr>
+	<tr><td><a href="idf.html#protoparam" class="plain">Protocol Parameters</a></td><td>amplification;RNA quality</td><td>amount of RNA used</td></tr>
+      </table>
+
+      <p class="text">The SDRF describing the use of these protocols
+      might look as follows:</p>
+
+      <table class="example">
+	<tr>
+	  <td><a href="#protocol_ref" class="plain">Protocol REF</a></td>
+	  <td><a href="#parameter_value" class="plain">Parameter Value [RNA quality]</a></td>
+	  <td><a href="#parameter_value" class="plain">Parameter Value [amplification]</a></td>
+	  <td><a href="#protocol_ref" class="plain">Protocol REF</a></td>
+	  <td><a href="#parameter_value" class="plain">Parameter Value [amount of RNA used]</a></td>
+	  <td><a href="#unit" class="plain">Unit [MassUnit]</a></td>
+	</tr>
+	<tr><td>My Ext. Protocol</td><td>RIN 8</td><td>RNA polymerase</td><td>My Labeling Protocol</td><td>10</td><td>ug</td></tr>
+      </table>
+
+      <p class="text">Other columns which may be used to annotate
+      these Protocol REF columns are: <a
+      href="#performer">Performer</a>, <a href="#date">Date</a>, and <a
+      href="#comment">Comment</a>.</p>
+
+      <p class="text"><a name="source_section"
+      class="subhead">Sources</a><br/> Sources are the starting
+      material for the experiment. The section starts with a Source
+      Name column, which will typically be followed by several <a
+      href="#characteristics">Characteristics</a> columns and a <a
+      href="#material_type">Material Type</a> column:</p>
+
+      <table class="example">
+	<tr>
+	  <td><a href="#source_name" class="plain">Source Name</a></td>
+	  <td><a href="#material_type" class="plain">Material Type</a></td>
+	  <td><a href="#characteristics" class="plain">Characteristics [Organism]</a></td>
+	  <td><a href="#characteristics" class="plain">Characteristics [OrganismPart]</a></td>
+	  <td><a href="#characteristics" class="plain">Characteristics [DiseaseState]</a></td>
+	  <td><a href="#characteristics" class="plain">Characteristics [BioSourceType]</a></td>
+	</tr>
+	<tr><td>Tumor 1</td><td>organism_part</td><td>Homo sapiens</td><td>mammary gland</td><td>invasive ductal carcinoma</td><td>frozen_sample</td></tr>
+      </table>
+
+      <p class="text">Additional columns which may be used to annotate
+      Sources are: <a href="#provider">Provider</a>, <a
+      href="#description">Description</a>, and <a
+      href="#comment">Comment</a>.</p>
+
+      <p class="text"><a name="sample_section"
+      class="subhead">Samples</a><br/> Samples represent steps in the
+      chain of treatments applied to the original Source. MAGE-TAB
+      allows you to create as many Sample steps as necessary:</p>
+
+      <table class="example">
+	<tr>
+	  <td><a href="#source_name" class="plain">Source Name</a></td>
+	  <td><a href="#protocol_ref" class="plain">Protocol REF</a></td>
+	  <td><a href="#sample_name" class="plain">Sample Name</a></td>
+	  <td><a href="#material_type" class="plain">Material Type</a></td>
+	  <td><a href="#protocol_ref" class="plain">Protocol REF</a></td>
+	  <td><a href="#sample_name" class="plain">Sample Name</a></td>
+	  <td><a href="#material_type" class="plain">Material Type</a></td>
+	  <td><a href="#characteristics" class="plain">Characteristics [OrganismPart]</a></td>
+	</tr>
+	<tr><td>Young Rat 99</td><td>My growth protocol</td><td>Adult Rat 99</td><td>whole_organism</td><td>My dissect protocol</td><td>Adult Rat Liver 99</td><td>organism_part</td><td>liver</td></tr>
+      </table>
+
+      <p class="text">For ArrayExpress submissions, typically only one
+      or two Sample steps are needed. Columns which may be used to
+      annotate Samples are: <a
+      href="#characteristics">Characteristics[]</a>, <a
+      href="#material_type">Material Type</a>, <a
+      href="#description">Description</a>, and <a
+      href="#comment">Comment</a>.</p>
+
+      <p class="text"><a name="extract_section"
+      class="subhead">Extracts</a><br/> Extracts refer to the
+      extracted nucleic acid used in the experiment. Again, as many
+      Extract steps may be used as are necessary. For example, if you
+      need to represent separate nucleic acid extraction and chromatin
+      immunoprecipitation steps in your SDRF, we recommend that you
+      use two Extract steps. In most cases, however, a single Extract
+      Name column would suffice:</p>
+
+      <table class="example">
+	<tr>
+	  <td><a href="#sample_name" class="plain">Sample Name</a></td>
+	  <td><a href="#material_type" class="plain">Material Type</a></td>
+	  <td><a href="#protocol_ref" class="plain">Protocol REF</a></td>
+	  <td><a href="#extract_name" class="plain">Extract Name</a></td>
+	  <td><a href="#material_type" class="plain">Material Type</a></td>
+	</tr>
+	<tr><td>Rat Liver 99</td><td>organism_part</td><td>My Ext. Protocol</td><td>Liver RNA 99</td><td>total_RNA</td></tr>
+      </table>
+
+      <p class="text">Columns which may be used to
+      annotate Extracts are: <a
+      href="#characteristics">Characteristics[]</a>, <a
+      href="#material_type">Material Type</a>, <a
+      href="#description">Description</a>, and <a
+      href="#comment">Comment</a>.</p>
+
+      <p class="text"><a name="labeledextract_section"
+      class="subhead">Labeled Extracts</a><br/> The Labeled Extracts
+      in an experiment are those materials which have been conjugated
+      to a label of some kind, prior to hybridization on an
+      array. Typically there is only one Labeled Extract step. For
+      submission to ArrayExpress, a <a href="#label">Label</a> column
+      must be included with the <a
+      href="#labeled_extract_name">Labeled Extract Name</a>
+      column to indicate which label (and therefore scanner channel)
+      corresponds to which sample:</p>
+
+      <table class="example">
+	<tr>
+	  <td><a href="#extract_name" class="plain">Extract Name</a></td>
+	  <td><a href="#material_type" class="plain">Material Type</a></td>
+	  <td><a href="#protocol_ref" class="plain">Protocol REF</a></td>
+	  <td><a href="#labeled_extract_name" class="plain">Labeled Extract Name</a></td>
+	  <td><a href="#label" class="plain">Label</a></td>
+	  <td><a href="#material_type" class="plain">Material Type</a></td>
+	</tr>
+	<tr><td>Liver RNA 99</td><td>total_RNA</td><td>My labeling protocol</td><td>Liver LE 99 Cy3</td><td>Cy3</td><td>synthetic_DNA</td></tr>
+	<tr><td>Kidney RNA 34</td><td>total_RNA</td><td>My labeling protocol</td><td>Kidney LE 34 Cy5</td><td>Cy5</td><td>synthetic_DNA</td></tr>
+      </table>
+
+      <p class="text">Note that it it wise to also include the Label
+      in the Labeled Extract Name itself, so that unique objects are
+      correctly created for each labeled extract. Columns which may be
+      used to annotate Labeled Extracts are: <a
+      href="#characteristics">Characteristics[]</a>, <a
+      href="#material_type">Material Type</a>, <a
+      href="#description">Description</a>, and <a
+      href="#comment">Comment</a>.</p>
+
+      <p class="text"><a name="hybridization_section"
+      class="subhead">Hybridizations</a><br/>The hybridization of
+      Labeled Extract to an array is a key step in the SDRF, since it
+      connects the "materials" section of the SDRF from the "data"
+      section. For submission to ArrayExpress, an <a
+      href="#array_design_ref">Array Design REF</a> column
+      must be included with the <a
+      href="#hybridization_name">Hybridization Name</a> column,
+      indicating which array design was used in the hybridization:
+      </p>
+
+      <table class="example">
+	<tr>
+	  <td><a href="#labeled_extract_name" class="plain">Labeled Extract Name</a></td>
+	  <td><a href="#label" class="plain">Label</a></td>
+	  <td><a href="#hybridization_name" class="plain">Hybridization Name</a></td>
+	  <td><a href="#array_design_ref" class="plain">Array Design REF</a></td>
+	</tr>
+	<tr><td>Liver LE 1</td><td>Cy3</td><td>Liver vs. Kidney 1</td><td>A-MEXP-88</td></tr>
+	<tr><td>Kidney LE 1</td><td>Cy5</td><td>Liver vs. Kidney 1</td><td>A-MEXP-88</td></tr>
+      </table>
+
+      <p class="text">It is also possible to use <a
+      href="#comment">Comment</a> columns to annotate both
+      Hybridization Name and Array Design REF
+      columns. Note that the values in Hybridization Name columns
+      may be used in <a
+      href="magetab_docs.html#datamatrix">Data Matrix</a> files
+      to link columns of data to individual hybridizations. </p>
+
+      <p class="text"><a name="scan_section"
+      class="subhead">Scans</a><br/>If desired, the act of scanning
+      the hybridized array may be represented as a distinct node in
+      the experimental graph, and encoded in the SDRF using <a
+      href="#scan_name">Scan Name</a> columns. These columns are
+      optional, but can be useful in cases where e.g. multiple scans
+      have been made of a single hybridized array, but where the data
+      files do not explicitly reflect this:</p>
+
+      <table class="example">
+	<tr>
+	  <td><a href="#hybridization_name" class="plain">Hybridization Name</a></td>
+	  <td><a href="#array_design_ref" class="plain">Array Design REF</a></td>
+	  <td><a href="#scan_name" class="plain">Scan Name</a></td>
+	  <td><a href="#array_data_file" class="plain">Array Data File</a></td>
+	</tr>
+	<tr><td>Liver vs. Kidney 1</td><td>A-MEXP-88</td><td>LK1 First Scan</td><td>Data1.txt</td></tr>
+	<tr><td>Liver vs. Kidney 1</td><td>A-MEXP-88</td><td>LK1 Second Scan</td><td>Data1.txt</td></tr>
+      </table>
+
+      <p class="text">Again, <a href="#comment">Comment</a> columns
+      may be used to further annotate Scan Name columns, where
+      appropriate. Note that the values in Scan Name columns may
+      be used in <a
+      href="magetab_docs.html#datamatrix">Data Matrix</a> files
+      to link columns of data to individual scanning events. </p>
+
+      <p class="text"><a name="arraydatafile_section"
+      class="subhead">Array Data Files</a><br/>The raw data files
+      generated by an investigation should be listed in an <a
+      href="#array_data_file">Array Data File</a> column
+      following the <a
+      href="#hybridization_name">Hybridization Name</a> and
+      (optional) <a href="#scan_name">Scan Name</a> columns:</p>
+
+      <table class="example">
+	<tr>
+	  <td><a href="#hybridization_name" class="plain">Hybridization Name</a></td>
+	  <td><a href="#array_design_ref" class="plain">Array Design REF</a></td>
+	  <td><a href="#array_data_file" class="plain">Array Data File</a></td>
+	  <td><a href="#comment" class="plain">Comment [EXP]</a></td>
+	</tr>
+	<tr><td>Liver vs. Kidney 1</td><td>A-AFFY-33</td><td>Data1.CEL</td><td>Data1.EXP</td></tr>
+      </table>
+
+      <p class="text"><a href="#comment">Comment</a> columns can be
+      used to add information relating to Array Data Files. For
+      example, if you are coding an Affymetrix-based experiment and
+      you wish to include the EXP files in your submission, you should
+      list them in a "Comment[EXP]" column following the "Array Data
+      File" column, as shown above.</p>
+
+      <p class="text"><a name="normalization_section"
+      class="subhead">Normalizations</a><br/> Similarly to the use of
+      <a href="#scan_name">Scan Name</a> columns <a
+      href="#scan_section">above</a>, it is possible to represent the
+      act of normalizing your data independently from the listing of
+      data files themselves. This is done using the optional <a
+      href="#normalization_name">Normalization Name</a> column:</p>
+
+      <table class="example">
+	<tr>
+	  <td><a href="#hybridization_name" class="plain">Hybridization Name</a></td>
+	  <td><a href="#array_design_ref" class="plain">Array Design REF</a></td>
+	  <td><a href="#array_data_file" class="plain">Array Data File</a></td>
+	  <td><a href="#normalization_name" class="plain">Normalization Name</a></td>
+	  <td><a href="#derived_array_data_file" class="plain">Derived Array Data File</a></td>
+	</tr>
+	<tr><td>Liver vs. Kidney 1</td><td>A-AFFY-33</td><td>Data1.CEL</td><td>Norm 1</td><td>Data1.CHP</td></tr>
+      </table>
+
+      <p class="text">Again, <a href="#comment">Comment</a> columns
+      may be used to further annotate Normalization Name columns,
+      where appropriate. Note that the values in
+      Normalization Name columns may be used in <a
+      href="magetab_docs.html#datamatrix">Data Matrix</a> files
+      to link columns of data to individual normalization events. </p>
+
+      <p class="text"><a name="derivedarraydatafile_section"
+      class="subhead">Derived Array Data Files</a><br/>The processed
+      data files which have been derived from the raw data should be
+      listed in an <a
+      href="#derived_array_data_file">Derived Array Data File</a>
+      column. <em>Note</em> that this generally only applies to
+      processed data arranged into one file per hybridization (or
+      scan, or normalization). If your files contain processed data
+      columns for more than one hybridization, you should reformat
+      these into the <a href="magetab_docs.html#datamatrix">MAGE-TAB
+      Data Matrix format</a> and include them instead in a <a
+      href="#derived_array_data_matrix_file">Derived Array Data Matrix File</a>
+      column. Multiple steps of normalization can be captured:</p>
+
+      <table class="example">
+	<tr>
+	  <td><a href="#hybridization_name" class="plain">Hybridization Name</a></td>
+	  <td><a href="#array_design_ref" class="plain">Array Design REF</a></td>
+	  <td><a href="#array_data_file" class="plain">Array Data File</a></td>
+	  <td><a href="#normalization_name" class="plain">Normalization Name</a></td>
+	  <td><a href="#derived_array_data_file" class="plain">Derived Array Data File</a></td>
+	  <td><a href="#normalization_name" class="plain">Normalization Name</a></td>
+	  <td><a href="#derived_array_data_matrix_file" class="plain">Derived Array Data Matrix File</a></td>
+	  <td><a href="#comment" class="plain">Comment [CDF]</a></td>
+	</tr>
+	<tr><td>Liver vs. Kidney 1</td><td>A-AFFY-33</td><td>Data1.CEL</td><td>MAS5 Norm 1</td><td>Data1.CHP</td><td>RMA Norm</td><td>RMANormData.txt</td><td>HG-U133A.cdf</td></tr>
+	<tr><td>Liver vs. Kidney 2</td><td>A-AFFY-33</td><td>Data2.CEL</td><td>MAS5 Norm 2</td><td>Data2.CHP</td><td>RMA Norm</td><td>RMANormData.txt</td><td>HG-U133A.cdf</td></tr>
+	<tr><td>Liver vs. Kidney 3</td><td>A-AFFY-33</td><td>Data3.CEL</td><td>MAS5 Norm 3</td><td>Data3.CHP</td><td>RMA Norm</td><td>RMANormData.txt</td><td>HG-U133A.cdf</td></tr>
+	<tr><td>Liver vs. Kidney 4</td><td>A-AFFY-33</td><td>Data4.CEL</td><td>MAS5 Norm 4</td><td>Data4.CHP</td><td>RMA Norm</td><td>RMANormData.txt</td><td>HG-U133A.cdf</td></tr>
+      </table>
+
+      <p class="text">In the above example, the columns from the
+      "RMANormData.txt" data matrix file could be linked to either the
+      Hybridization Names or the previous set of Normalization Names,
+      allowing for flexible representation of the flow of data through
+      the process. See the <a href="magetab_docs.html#datamatrix">Data
+      Matrix notes</a> for details of how these links are encoded in
+      the data matrix file header.</p>
+
+      <p class="text"><a href="#comment">Comment</a> columns may be
+      used to add information relating to processed data files. For
+      example, when coding an Affymetrix-based experiment with a Data
+      Matrix file, as in the example above, a Comment[CDF] column
+      should be used to indicate which Affymetrix library ("CDF") file
+      applies to these data.</p>
+
+      <p class="text"><a name="factorvalue_section"
+      class="subhead">Factor Values</a><br/> The Factor Values for an
+      experiment are the values of the variables under
+      investigation. For example, an experiment studying the effect of
+      different compounds on a cell culture would have "compound" as
+      an experimental variable. These variables are listed in the IDF
+      as "Experimental Factor Names" with associated Types:</p>
+
+      <table class="example">
+	<tr><td><a href="idf.html#factorname" class="plain">Experimental Factor Name</a></td><td>Cells</td><td>Drug</td></tr>
+	<tr><td><a href="idf.html#factortype" class="plain">Experimental Factor Type</a></td><td>cell_line</td><td>compound</td></tr>
+	<tr><td> </td><td> </td><td> </td></tr>
+	<tr><td><a href="idf.html#protoname" class="plain">Protocol Name</a></td><td>All Treatments</td><td> </td></tr>
+	<tr><td><a href="idf.html#protoparams" class="plain">Protocol Parameters</a></td><td>drug compound</td><td> </td></tr>
+      </table>
+
+      <p class="text">Given the above definitions in the accompanying
+      IDF, the SDRF file can then reference these factors when we come
+      to list the factor values:</p>
+
+      <table class="example">
+	<tr>
+	  <td><a href="#source_name" class="plain">Source Name</a></td>
+	  <td><a href="#characteristics" class="plain">Characteristics [CellLine]</a></td>
+	  <td><a href="#protocol_ref" class="plain">Protocol REF</a></td>
+	  <td><a href="#parameter_value" class="plain">Parameter Value [drug compound]</a></td>
+	  <td><a href="#hybridization_name" class="plain">Hybridization Name</a></td>
+	  <td><a href="#factor_value" class="plain">Factor Value [Cells]</a></td>
+	  <td><a href="#factor_value" class="plain">Factor Value [Drug]</a></td>
+	</tr>
+	<tr><td>Line 1</td><td>Jurkat</td><td>All Treatments</td><td>imatinib</td><td>Jurkat vs imatinib</td><td>Jurkat</td><td>imatinib</td></tr>
+	<tr><td>Line 1</td><td>Jurkat</td><td>All Treatments</td><td>lapatinib</td><td>Jurkat vs lapatinib</td><td>Jurkat</td><td>lapatinib</td></tr>
+	<tr><td>Line 2</td><td>RKO</td><td>All Treatments</td><td>imatinib</td><td>RKO vs imatinib</td><td>RKO</td><td>imatinib</td></tr>
+	<tr><td>Line 2</td><td>RKO</td><td>All Treatments</td><td>lapatinib</td><td>RKO vs lapatinib</td><td>RKO</td><td>lapatinib</td></tr>
+      </table>
+
+      <p class="text">Note that there is inevitably duplication
+      between Factor Values and values entered elsewhere in the
+      SDRF. It is particularly common to have the Factor Value column
+      duplicate either a <a
+      href="#characteristics">Characteristics[]</a> column or a <a
+      href="#parameter_value">Parameter Value[]</a> column.</p>
+
+      <p class="text">Factor Value columns may be placed anywhere
+      after the hybridization section of the SDRF, although they
+      should be placed to avoid disrupting any of the other
+      sections. This is most easily achieved by adding them at the end
+      (i.e., the far right) of the SDRF.</p>
+
+      <p CLASS="sectionfoot"> [ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="sdrftags">All valid SDRF column headings</a></div>
+
+      <p class="text"><a name="source_name" class="subhead">Source
+      Name</a><br/><font color="green">Used as an identifier within
+      the MAGE-TAB document.</font> This column contains user-defined names for the <a
+      href="#source_section">Source</a> materials. The following
+      columns can be used to annotate Source Name columns:</p>
+      <ul>
+	<li><a href="#characteristics">Characteristics[]</a></li>
+	<li><a href="#provider">Provider</a></li>
+	<li><a href="#material_type">Material Type</a></li>
+	<li><a href="#description">Description</a></li>
+	<li><a href="#comment">Comment[]</a></li>
+      </ul>
+      
+      <p class="text"><a name="sample_name" class="subhead">Sample
+      Name</a><br/> <font color="green">Used as an identifier within
+      the MAGE-TAB document.</font> This column contains user-defined
+      names for each <a href="#sample_section">Sample</a>
+      material. The following columns can be used to annotate Sample
+      Name columns:</p>
+
+      <ul>
+	<li><a href="#characteristics">Characteristics[]</a></li>
+	<li><a href="#material_type">Material Type</a></li>
+	<li><a href="#description">Description</a></li>
+	<li><a href="#comment">Comment[]</a></li>
+      </ul>
+      
+      <p class="text"><a name="extract_name" class="subhead">Extract
+      Name</a><br/> <font color="green">Used as an identifier within
+      the MAGE-TAB document.</font> This column contains user-defined
+      names for each <a href="#extract_section">Extract</a>
+      material. The following columns can be used to annotate Extract
+      Name columns:</p>
+
+      <ul>
+	<li><a href="#characteristics">Characteristics[]</a></li>
+	<li><a href="#material_type">Material Type</a></li>
+	<li><a href="#description">Description</a></li>
+	<li><a href="#comment">Comment[]</a></li>
+      </ul>
+      
+      <p class="text"><a name="labeled_extract_name"
+      class="subhead">Labeled Extract Name</a><br/> <font
+      color="green">Used as an identifier within the MAGE-TAB
+      document.</font> This column contains user-defined names for
+      each <a href="#labeledextract_section">Labeled Extract</a>
+      material. The following columns can be used to annotate Labeled
+      Extract Name columns:</p>
+
+      <ul>
+	<li><a href="#label">Label</a> (<font color="red">required</font>)</li>
+	<li><a href="#characteristics">Characteristics[]</a></li>
+	<li><a href="#material_type">Material Type</a></li>
+	<li><a href="#description">Description</a></li>
+	<li><a href="#comment">Comment[]</a></li>
+      </ul>
+      
+      <p class="text"><a name="hybridization_name"
+      class="subhead">Hybridization Name</a><br/> <font
+      color="green">Used as an identifier within the MAGE-TAB
+      document.</font>This column contains user-defined names for each
+      <a href="#hybridization_section">Hybridization</a>. The
+      following columns can be used to annotate Hybridization
+      Name columns:</p>
+      
+      <ul>
+	<li><a href="#array_design_ref">Array Design REF</a> (<font color="red">required</font>)</li>
+	<li><a href="#comment">Comment[]</a></li>
+      </ul>
+
+      <p class="text"><a name="assay_name" class="subhead">Assay
+      Name</a><br/> <font color="green">Used as an identifier within
+      the MAGE-TAB document.</font>This column contains user-defined
+      names for each Assay. "Assay Name" may be used instead of
+      "Hybridization Name" to identify generic biological assays, such
+      as rtPCR. Note that this column should not be used for
+      submission of regular microarray experiments to
+      ArrayExpress. The following columns can be used to annotate
+      Assay Name columns:</p>
+      
+      <ul>
+	<li><a href="#technology_type">Technology Type</a></li>
+	<li><a href="#comment">Comment[]</a></li>
+      </ul>
+
+      <p class="text">Note that as of MAGE-TAB version 1.1, all Assay
+      Name columns must be followed by a Technology Type column.</p>
+
+      <p class="text"><a name="scan_name" class="subhead">Scan
+      Name</a><br/> <font color="green">Used as an identifier within
+      the MAGE-TAB document.</font> This optional column contains
+      user-defined names for each <a href="#scan_section">Scan</a>
+      event. The following columns can be used to annotate Scan Name
+      columns:</p>
+
+      <ul>
+	<li><a href="#comment">Comment[]</a></li>	
+      </ul>
+      
+      <p class="text"><a name="normalization_name"
+      class="subhead">Normalization Name</a><br/> <font
+      color="green">Used as an identifier within the MAGE-TAB
+      document.</font> This optional column contains user-defined
+      names for each <a
+      href="#normalization_section">Normalization</a> event. The
+      following columns can be used to annotate Normalization Name
+      columns:</p>
+      
+      <ul>
+	<li><a href="#comment">Comment[]</a></li>	
+      </ul>
+      
+      <p class="text"><a name="array_data_file" class="subhead">Array
+      Data File</a><br/> This column contains a list of <a
+      href="#arraydatafile_section">raw data files</a>, one for each
+      row of the SDRF file, linking these data files to their
+      respective hybridizations. The following columns can be used to
+      annotate Array Data File columns:</p>
+      
+      <ul>
+	<li><a href="#comment">Comment[]</a></li>	
+      </ul>
+      
+      <p class="text"><a name="derived_array_data_file"
+      class="subhead">Derived Array Data File</a><br/> This column
+      contains a list of <a
+      href="#derivedarraydatafile_section">processed data files</a>,
+      one for each row of the SDRF file, linking these data files to
+      their respective hybridizations. The following columns can be
+      used to annotate Derived Array Data File columns:</p>
+      
+      <ul>
+	<li><a href="#comment">Comment[]</a></li>	
+      </ul>
+
+      <p class="text"><a name="array_data_matrix_file"
+      class="subhead">Array Data Matrix File</a><br/> This column
+      contains a list of raw data matrix files, where data from
+      multiple hybridizations is stored in a single file, and the data
+      mapped to each hybridization via the <a
+      href="magetab_docs.html#datamatrix">Data Matrix</a> format
+      itself. The following columns can be used to annotate Array Data
+      Matrix File columns:</p>
+      
+      <ul>
+	<li><a href="#comment">Comment[]</a></li>	
+      </ul>
+
+      <p class="text"><a name="derived_array_data_matrix_file"
+      class="subhead">Derived Array Data Matrix File</a><br/> This
+      column contains a list of processed data matrix files, where
+      data from multiple hybridizations is stored in a single file,
+      and the data mapped to each hybridization (or scan, or
+      normalization) via the <a
+      href="magetab_docs.html#datamatrix">Data Matrix</a> format
+      itself. The following columns can be used to annotate Derived
+      Array Data Matrix File columns:</p>
+      
+      <ul>
+	<li><a href="#comment">Comment[]</a></li>	
+      </ul>
+
+      <p class="text"><a name="image_file" class="subhead">Image
+      File</a><br/>This optional column contains a list of image
+      files, one for each row of the SDRF file, linking these image
+      files to their respective hybridizations. Note that ArrayExpress
+      does not store image data due to size constraints on the
+      database. If desired, you may use this column to include links
+      to image files stored on your local webserver. The following
+      columns can be used to annotate Derived Array Data File
+      columns:</p>
+      
+      <ul>
+	<li><a href="#comment">Comment[]</a></li>	
+      </ul>
+
+      <p class="text"><a name="array_design_ref" class="subhead">Array
+      Design REF</a><br/> This column contains references to the array
+      design used for each hybridization. For ArrayExpress submissions
+      this should be an ArrayExpress accession number,
+      e.g. "A-AFFY-33". The following columns can be used to annotate
+      Array Design REF columns:</p>
+      
+      <ul>
+	<li><a href="#term_source_ref">Term Source REF</a></li>	
+	<li><a href="#comment">Comment[]</a></li>	
+      </ul>
+
+      <p class="text">The Term Source REF column here can be used to
+      point to the source of the array design referenced; however for
+      ArrayExpress submissions this should always be ArrayExpress
+      itself, and so this column is in effect ignored.</p>
+
+      <p class="text"><a name="protocol_ref" class="subhead">Protocol
+      REF</a><br/> This column contains references to Protocol Names
+      defined in the IDF, or accession numbers of protocols already
+      deposited with ArrayExpress. The following columns can be used
+      to annotate Protocol REF columns:</p>
+      
+      <ul>
+	<li><a href="#parameter_value">Parameter Value[]</a></li>	
+	<li><a href="#performer">Performer</a></li>	
+	<li><a href="#date">Date</a></li>	
+	<li><a href="#term_source_ref">Term Source REF</a></li>	
+	<li><a href="#comment">Comment[]</a></li>	
+      </ul>
+
+      <p class="text">The Term Source REF column here can be used to
+      point to the source of the protocol referenced, if it is not
+      contained within the IDF; for ArrayExpress submissions this
+      should always be ArrayExpress itself, and a suitable
+      ArrayExpress Term Source should be defined in the IDF.</p>
+
+      <p class="text"><a name="characteristics"
+      class="subhead">Characteristics[<category term>]</a><br/>
+      <font color="blue">Controlled vocabulary term or
+      measurement.</font> Used as an attribute column following <a
+      href="#source_name">Source Name</a>, <a
+      href="#sample_name">Sample Name</a>, <a
+      href="#extract_name">Extract Name</a>, or <a
+      href="#labeled_extract_name">Labeled Extract Name</a>. This
+      column contains terms describing each material according to the
+      characteristics category indicated in the column header. For
+      example, a column headed "Characteristics[OrganismPart]" would
+      contain individual OrganismPart terms. These terms may be
+      user-defined (the default), from an external ontology source
+      (indicated using a Term Source REF column), or a measurement
+      (indicated using a Unit[] column). </p>
+
+      <ul>
+	<li><a href="#term_source_ref">Term Source REF</a></li>	
+	<li><a href="#unit">Unit[]</a></li>	
+      </ul>
+
+      <p class="text"><a name="provider"
+      class="subhead">Provider</a><br/> Used as an attribute column
+      following <a href="#source_name">Source Name</a>. A free-text
+      string identifying the organization or person from which the
+      Source was obtained.</p>
+      
+      <p class="text"><a name="material_type" class="subhead">Material
+      Type</a><br/> <font color="blue">Controlled vocabulary
+      term.</font> Used as an attribute column following <a
+      href="#source_name">Source Name</a>, <a
+      href="#sample_name">Sample Name</a>, <a
+      href="#extract_name">Extract Name</a>, or <a
+      href="#labeled_extract_name">Labeled Extract Name</a>. This
+      column contains terms describing the type of each material. For
+      ArrayExpress submissions this term should be an instance of <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php#MaterialType">MaterialType
+      from the MGED Ontology</a>. Examples: whole_organism,
+      organism_part, cell, total_RNA. The following columns can be
+      used to annotate Material Type columns:</p>
+      
+      <ul>
+	<li><a href="#term_source_ref">Term Source REF</a></li>	
+      </ul>
+
+      <p class="text">The Term Source REF column in this case would
+      point to the ontology (defined in the IDF) from which the
+      Material Type terms are taken (the MGED Ontology in the example
+      above).</p>
+
+      <p class="text"><a name="label" class="subhead">Label</a><br/>
+      <font color="blue">Controlled vocabulary term.</font> Used as an
+      attribute column following <a
+      href="#labeled_extract_name">Labeled Extract Name</a>. The label
+      compound which is conjugated to an Extract to create the Labeled
+      Extract. For ArrayExpress submissions this term should be an
+      instance of <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php#LabelCompound">LabelCompound
+      from the MGED Ontology</a>. Examples: Cy3, Cy5, biotin,
+      alexa_546. The following columns can be used to annotate Label
+      columns:</p>
+      
+      <ul>
+	<li><a href="#term_source_ref">Term Source REF</a></li>	
+      </ul>
+
+      <p class="text">The Term Source REF column in this case would
+      point to the ontology (defined in the IDF) from which the Label
+      terms are taken (the MGED Ontology in the example above).</p>
+
+      <p class="text"><a name="technology_type"
+      class="subhead">Technology Type</a><br/> <font
+      color="blue">Controlled vocabulary term.</font> Used as an
+      attribute column following <a href="#assay_name">Assay
+      Name</a>. This column contains terms describing the type of each
+      generic (non-hybridization) assay. Example: rtPCR. The following
+      columns can be used to annotate Technology Type columns:</p>
+      
+      <ul>
+	<li><a href="#term_source_ref">Term Source REF</a></li>	
+      </ul>
+
+      <p class="text">The Term Source REF column in this case would
+      point to the ontology (defined in the IDF) from which the
+      Technology Type terms are taken.</p>
+
+      <p class="text"><a name="factor_value" class="subhead">Factor
+      Value[<experiment factor name>]</a><br/> <font
+      color="blue">Controlled vocabulary term or measurement.</font>
+      This column contains terms describing the experimental factor
+      values (i.e., variables) for each row of the SDRF. The
+      Experimental Factor Name to which it pertains (from the
+      accompanying IDF) should be indicated in the column heading. For
+      example, if you have this in your IDF:</p>
+
+      <table class="example">
+	<tr><td><a href="idf.html#factorname" class="plain">Experimental Factor Name</a></td><td>TissueEF</td></tr>
+	<tr><td><a href="idf.html#factortype" class="plain">Experimental Factor Type</a></td><td>organism_part</td></tr>
+      </table>
+
+      <p class="text">You could then use this factor in your SDRF
+      (assuming you had also defined the "Mouse Anatomy" term source
+      in your IDF):</p>
+
+      <table class="example">
+	<tr>
+	  <td><a href="#factor_value" class="plain">Factor Value[TissueEF]</a></td>
+	  <td><a href="#term_source_ref" class="plain">Term Source REF</a></td>
+	</tr>
+	<tr><td>brain</td><td>Mouse Anatomy</td></tr>
+	<tr><td>kidney</td><td>Mouse Anatomy</td></tr>
+	<tr><td>liver</td><td>Mouse Anatomy</td></tr>
+	<tr><td>intestine</td><td>Mouse Anatomy</td></tr>
+	<tr><td>pancreas</td><td>Mouse Anatomy</td></tr>
+      </table>
+
+      <p class="text">The terms in the column may be user-defined (the
+      default), from an external ontology source (indicated using a
+      Term Source REF column), or a measurement (indicated using a
+      Unit[] column).</p>
+
+      <p class="text">In the example above, the column terms would be
+      treated as describing organism parts. For more precise control
+      over the treatment of these terms, the optional form "Factor
+      Value [] ()" is available, e.g. "Factor Value [growth condition
+      EF] (Nutrients)".</p>
+      
+      <p class="text"><a name="performer"
+      class="subhead">Performer</a><br/> Used as an attribute column
+      following <a href="#protocol_ref">Protocol REF</a>. The name of
+      the researcher who carried out the protocol.</p>
+      
+      <p class="text"><a name="date" class="subhead">Date</a><br/>
+      Used as an attribute column following <a
+      href="#protocol_ref">Protocol REF</a>. The date (and time, where
+      available) upon which the protocol was performed, in the
+      following format: YYYY-MM-DD</p>
+      
+      <p class="text"><a name="parameter_value"
+      class="subhead">Parameter Value[<protocol
+      parameter>]</a><br/> Used as an attribute column following <a
+      href="#protocol_ref">Protocol REF</a> columns. This column
+      contains values for the protocol parameters referenced in the
+      column header. The following columns can be used to annotate
+      Parameter Value[] columns:</p>
+      
+      <ul>
+	<li><a href="#unit">Unit[]</a></li>	
+	<li><a href="#term_source_ref">Term Source REF</a></li>	
+	<li><a href="#comment">Comment[]</a></li>	
+      </ul>
+
+      <p class="text">For example, if a Protocol Name "Array
+      Hybridization" is defined in the accompanying IDF, with Protocol
+      Parameters "hyb temp;hyb volume", the following would be
+      valid:</p>
+
+      <table class="example">
+	<tr>
+	  <td><a href="#protocol_ref" class="plain">Protocol REF</a></td>
+	  <td><a href="#parameter_value" class="plain">Parameter Value [hyb temp]</a></td>
+	  <td><a href="#unit" class="plain">Unit [TemperatureUnit]</a></td>
+	  <td><a href="#paramater_value" class="plain">Parameter Value [hyb volume]</a></td>
+	  <td><a href="#unit" class="plain">Unit [VolumeUnit]</a></td>
+	</tr>
+	<tr><td>Array Hybridization</td><td>55</td><td>degrees_C</td><td>100</td><td>ul</td></tr>
+      </table>
+
+      <p class="text"><a name="unit" class="subhead">Unit[<unit
+      category>]</a><br/> <font color="blue">Controlled vocabulary
+      term.</font> Used as an attribute column following <a
+      href="#characteristics">Characteristics[]</a>, <a
+      href="#factor_value">Factor Value[]</a> or <a
+      href="#parameter_value">Parameter Value[]</a>. This column
+      contains terms describing the unit(s) to be applied to the
+      values in the preceding column. The type of unit is included in
+      the column heading, e.g. "Unit[TimeUnit]". These unit types
+      should correspond to <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php#Unit">Unit
+      subclasses from the MGED Ontology</a>. The following columns can
+      be used to annotate Unit[] columns:</p>
+      
+      <ul>
+	<li><a href="#term_source_ref">Term Source REF</a></li>	
+      </ul>
+
+      <p class="text">The Term Source REF column in this case would
+      point to the ontology (defined in the IDF) from which the Unit
+      terms are taken.</p>
+
+      <p class="text"><a name="description"
+      class="subhead">Description</a><br/>Used as an attribute column
+      following <a href="#source_name">Source Name</a>, <a
+      href="#sample_name">Sample Name</a>, <a
+      href="#extract_name">Extract Name</a>, or <a
+      href="#labeled_extract_name">Labeled Extract Name</a>. A
+      free-text description to be attached to the corresponding
+      material. To be used sparingly, if at all - most annotations
+      should be provided using controlled vocabulary terms, using <a
+      href="#characteristics">Characteristics[]</a> columns.</p>
+      
+      <p class="text"><a name="term_source_ref" class="subhead">Term
+      Source REF</a><br/> Used as an attribute column following any
+      controlled vocabulary column (e.g., <a
+      href="#characteristics">Characteristics[]</a>, or column
+      allowing reference of external entities (e.g., <a
+      href="#protocol_ref">Protocol REF</a>. This column contains
+      references to ontology or database Term Sources defined in the
+      IDF, and from which the values in the previous column were
+      taken. The following columns can be used to annotate Term Source
+      REF columns:</p>
+      
+      <ul>
+	<li><a href="#term_accession_number">Term Accession Number</a></li>	
+      </ul>
+
+      <p class="text"><a name="term_accession_number"
+      class="subhead">Term Accession Number</a><br/> Used as an
+      attribute column following <a href="#term_source_ref">Term
+      Source REF</a> columns. This column contains the accession
+      numbers from the term source used to identify the ontology or
+      database terms in question. For example:</p>
+
+      <table class="example">
+	<tr>
+	  <td><a href="#source_name" class="plain">Source Name</a></td>
+	  <td><a href="#characteristics" class="plain">Characteristics [DiseaseState]</a></td>
+	  <td><a href="#term_source_ref" class="plain">Term Source REF</a></td>
+	  <td><a href="#term_accession_number" class="plain">Term Accession Number</a></td>
+	</tr>
+	<tr><td>Sample 1</td><td>acute lymphocytic leukemia</td><td>NCI Metathesaurus</td><td>C0023449</td></tr>
+      </table>
+
+      <p class="text">(This example relies on the "NCI Metathesaurus"
+      Term Source having been pre-defined in the IDF accompanying the
+      SDRF.)</p>
+      
+      <p class="text"><a name="comment"
+      class="subhead">Comment[<comment name>]</a><br/> This
+      column can be used to annotate the main graph node and edge
+      columns listed above. It is included as an extensibility
+      mechanism, and should not generally be used to encode meaningful
+      biological annotation. The column heading should contain a name
+      for the type of values included in the column.</p>
+      
+      <p CLASS="sectionfoot"> [ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <p class="text">Please see the <a href="http://www.mged.org/mage-tab/">
+	MAGE-TAB specification</a> for further information and examples.</p>
+
+    <hr>
+    <a href="http://sourceforge.net">
+      <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" />
+    </a>
+    <br>
+<!-- Created: Mon Jan 17 09:45:42 GMT 2005 -->
+<!-- hhmts start -->
+Last modified: Mon Feb 25 18:06:43 GMT 2008
+<!-- hhmts end -->
+  </body>
+</html>
diff --git a/docs/sdrf_layout.png b/docs/sdrf_layout.png
new file mode 100644
index 0000000..639209b
Binary files /dev/null and b/docs/sdrf_layout.png differ
diff --git a/docs/spreadsheet.html b/docs/spreadsheet.html
new file mode 100644
index 0000000..e3277c7
--- /dev/null
+++ b/docs/spreadsheet.html
@@ -0,0 +1,625 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+  <head>
+    <title>Spreadsheet construction</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+    <link rel="stylesheet" href="style.css">
+  </head>
+
+  <body style="width:90%">
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100"><a href="../index.html"><img src="T2M_logo.png" border="0" height="50" alt="Tab2MAGE logo"></a></td>
+	    <td class="pagetitle">Creating your spreadsheet</td>
+	  </tr>
+      </table>
+    </div>
+
+    <p class="text">The Tab2MAGE spreadsheet structure is split into
+    three main sections: <a href="#experiment">Experiment</a>, <a
+    href="#protocol">Protocol</a> and <a
+    href="#hybridization">Hybridization</a>. The sections are
+    separated from each other by one or more blank
+    lines. <em>Please note</em> that blank lines are used to demarcate the ends
+    of sections, and as such they should not be used within the body
+    of any section. We will discuss each of these three sections in
+    turn.</p>
+
+    <p class="text">A series of <a href="#realexamples">real-world</a>
+    and <a href="#examples">conceptual</a> examples is given at the
+    end of this page.</p>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="experiment">Experiment section</a></div>
+      
+      <p class="text">This section holds all of the top-level
+      information about an experiment. It consists of two columns; the
+      left column contains a series of predefined row tags, while the
+      right column contains the actual values pertaining to your
+      experiment. An example is shown below:</p>
+
+      <table class="example">
+	  <tr><td><a class="plain" href="detail.html#exptsection">Experiment section</a></td><td> </td></tr>
+	  <tr><td><a class="plain" href="detail.html#domain">domain</a></td><td>ebi.ac.uk</td></tr>
+	  <tr><td><a class="plain" href="detail.html#accession">accession</a></td><td>E-EXML-1</td></tr>
+	  <tr><td><a class="plain" href="detail.html#quality_control">quality_control</a></td><td>dye_swap_quality_control</td></tr>
+	  <tr><td><a class="plain" href="detail.html#experiment_design_type">experiment_design_type</a></td><td>strain_or_line_design</td></tr>
+	  <tr><td><a class="plain" href="detail.html#name">name</a></td><td><i><your experiment title></i></td></tr>
+	  <tr><td><a class="plain" href="detail.html#description">description</a></td><td><i><short description of your experiment></i></td></tr>
+	  <tr><td><a class="plain" href="detail.html#release_date">release_date</a></td><td>2004-08-30</td></tr>
+	  <tr><td><a class="plain" href="detail.html#submission_date">submission_date</a></td><td>2004-07-28</td></tr>
+	  <tr><td><a class="plain" href="detail.html#submitter">submitter</a></td><td>John Falstaff</td></tr>
+	  <tr><td><a class="plain" href="detail.html#organization">organization</a></td><td>Windsor Laboratories</td></tr>
+	  <tr><td><a class="plain" href="detail.html#publication">publication_title</a></td><td><i><your manuscript title></i></td></tr>
+	  <tr><td><a class="plain" href="detail.html#publication">authors</a></td><td>John Falstaff; Robin Goodfellow</td></tr>
+	  <tr><td><a class="plain" href="detail.html#publication">journal</a></td><td>Nature Genetics</td></tr>
+	  <tr><td><a class="plain" href="detail.html#publication">volume</a></td><td>12</td></tr>
+	  <tr><td><a class="plain" href="detail.html#publication">issue</a></td><td>4</td></tr>
+	  <tr><td><a class="plain" href="detail.html#publication">pages</a></td><td>123-456</td></tr>
+	  <tr><td><a class="plain" href="detail.html#publication">year</a></td><td>2004</td></tr>
+	  <tr><td><a class="plain" href="detail.html#publication">pubmed_id</a></td><td>12345678</td></tr>
+      </table>
+
+      <p CLASS="text">With the exception of the section header, all
+      the tags above are optional. However, if the <b>domain</b> or
+      <b>accession</b> tags are not present suitable placeholders will
+      be used instead. A <a href="detail.html#experiment">more
+      detailed explanation</a> of the meaning of each of these tags
+      may be obtained by clicking on them.</p>
+
+      <p CLASS="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="protocol">Protocol section</a></div>
+      
+      <p class="text">The Protocol section allows the user to define
+      the protocols used in the experiment. The section consists of
+      three required columns (<b>accession</b>, <b>text</b> and
+      <b>name</b>) with an optional <b>parameters</b> column. A fifth
+      column, "<b>type</b>" is available for adding custom
+      ProtocolType terms from the MGED Ontology to your protocols, but
+      its use is <em>not</em> recommended for the majority of
+      cases. This column should generally be omitted, and the tab2mage
+      script will use a set of default protocol types based on how the
+      protocols are used in the Hybridization section.</p>
+
+      <table class="example">
+	  <tr><td style="width:110pt"><a class="plain" href="detail.html#protosection">Protocol section</a></td><td> </td><td> </td><td> </td></tr>
+	  <tr>
+	    <td><a class="plain" href="detail.html#protoaccession">accession</a></td>
+	    <td><a class="plain" href="detail.html#prototext">text</a></td>
+	    <td><a class="plain" href="detail.html#protoname">name</a></td>
+	    <td><a class="plain" href="detail.html#protoparams">parameters</a></td>
+	  </tr>
+	  <tr><td>P-EXML-1</td><td>Cells were grown in YPD (1% yeast extract/2% peptone/2% glucose) to an OD600 of approximately 0.8</td><td>Yeast growth</td><td>growth temperature (degree_C); pH</td></tr>
+	  <tr><td>P-EXML-2</td><td><i><protocol text></i></td><td>Yeast cell harvesting</td><td>pellet weight (mg)</td></tr>
+	  <tr><td>P-EXML-3</td><td><i><protocol text></i></td><td>Cell lysis and RNA prep</td><td> </td></tr>
+	  <tr><td>P-EXML-4</td><td><i><protocol text></i></td><td>cDNA labeling</td><td> </td></tr>
+	  <tr><td>P-EXML-5</td><td><i><protocol text></i></td><td>Hybridization</td><td>hyb temp (degree_C); hyb volume (uL)</td></tr>
+	  <tr><td>P-EXML-6</td><td><i><protocol text></i></td><td>Scanning</td><td> </td></tr>
+	  <tr><td>P-EXML-7</td><td><i><protocol text></i></td><td>Image analysis</td><td> </td></tr>
+	  <tr><td>P-EXML-8</td><td><i><protocol text></i></td><td>Normalization</td><td> </td></tr>
+      </table>
+
+
+      <p CLASS="text">Note that you may omit protocols which have been
+      previously submitted to ArrayExpress, and simply refer to those
+      protocols directly in the <a href="#hybridization">Hybridization
+      section</a>. If no new protocols are needed then the entire Protocol
+      section may be omitted. A <a href="detail.html#protocol">more
+      detailed explanation</a> of the meaning of each of the fields in
+      this section may be obtained by clicking on the column
+      headings.</p>
+
+      <p CLASS="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="hybridization">Hybridization section</a></div>
+      
+      <p class="text">The Hybridization section describes how each
+      sample links to each hybridization, scanning and subsequent
+      normalization, and as such is the largest and most complex
+      section. There is a simple principle underlying this section,
+      however: <em>Everything which appears on a given line must be
+      related in some way.</em> </p>
+
+      <p class="text">The column headings available in this section
+      can be divided into several different types for the purpose of
+      clarity. Note that the various columns in this section may
+      appear in any order, and so you may find that re-ordering the
+      columns makes the spreadsheet more legible for your own
+      application.</p>
+
+      <ul>
+	<li><a href="#hnames">Names of materials and processes</a></li>
+	<li><a href="#htypes">Material types</a></li>
+	<li><a href="#hchars">Material characteristics</a></li>
+	<li><a href="#hproto">Links to defined protocols and parameters</a></li>
+	<li><a href="#hfacts">Experimental factors</a></li>
+	<li><a href="#hfiles">Data files and array information</a></li>
+      </ul>
+
+      <p class="text">For each of the following tables, a <a
+      href="detail.html#hybridization">more detailed explanation</a> of
+      the meaning of each of the column headings may be obtained by
+      clicking on them.</p>
+
+      <p class="text"><a name="hnames" class="subhead">Names of
+      materials and processes</a><br> These columns are provided so
+      that you can give unique names to each of the materials used in
+      your experiment (e.g., samples, extracts, labeled
+      extracts). Hybridization and normalization events are also given
+      names. The use of these names is entirely optional, as Tab2MAGE
+      will attempt to link all of these things together for you. If,
+      however, your experiment involves complex pooling or splitting
+      operations then it is recommended that you make use of the
+      names. Doing so will allow you a much greater degree of control
+      over the output MAGE-ML.</p>
+
+      <p class="text">The following table is an example of a simple
+      reference design in which individual samples are hybridized
+      against a common reference pool:</p>
+
+      <table class="example">
+	  <tr><td><a class="plain" href="detail.html#hybsection">Hybridization section</a></td><td> </td><td> </td><td> </td><td> </td><td> </td><td> </td></tr>
+	  <tr><td><a class="plain" href="detail.html#biosource">BioSource</a>
+	    </td><td><a class="plain" href="detail.html#sample">Sample</a>
+	    </td><td><a class="plain" href="detail.html#extract">Extract</a>
+	    </td><td><a class="plain" href="detail.html#label">LabeledExtract</a>
+	    </td><td><a class="plain" href="detail.html#dye">Dye</a>
+	    </td><td><a class="plain" href="detail.html#hyb">Hybridization</a>
+	    </td><td><a class="plain" href="detail.html#norm">Normalization</a></td></tr>
+	  <tr><td>S288C</td><td>S288C sample</td><td>S288C extract</td><td>S288C LE</td><td>Cy3</td><td>S288C Hyb</td><td>S288C Norm</td></tr>
+	  <tr><td>S288C</td><td>S288C sample</td><td>Reference extract</td><td>Reference LE</td><td>Cy5</td><td>S288C Hyb</td><td>S288C Norm</td></tr>
+	  <tr><td>Sigma1278b</td><td>Sigma1278b sample</td><td>Sigma1278b extract</td><td>Sigma1278b LE</td><td>Cy3</td><td>Sigma1278b Hyb</td><td>Sigma1278b Norm</td></tr>
+	  <tr><td>Sigma1278b</td><td>Sigma1278b sample</td><td>Reference extract</td><td>Reference LE</td><td>Cy5</td><td>Sigma1278b Hyb</td><td>Sigma1278b Norm</td></tr>
+	  <tr><td>W303a</td><td>W303a sample</td><td>W303a extract</td><td>W303a LE</td><td>Cy3</td><td>W303a Hyb</td><td>W303a Norm</td></tr>
+	  <tr><td>W303a</td><td>W303a sample</td><td>Reference extract</td><td>Reference LE</td><td>Cy5</td><td>W303a Hyb</td><td>W303a Norm</td></tr>
+      </table>
+
+      <p class="text">Note that Tab2MAGE does not constrain you to use the standard experimental layout:</p>
+
+      <pre>
+biosource ----------> sample ----------> extract ----------> labeled extract ----------> hybridization event
+    [growth, treatment]     [extraction]         [labeling]               [hybridization]
+</pre>
+
+      <p class="text">If, for instance, your experiment starts with a
+      series of extracts provided by an external supplier, the sample
+      and extract stages may be omitted:</p>
+
+      <pre>
+biosource ----------> labeled extract ----------> hybridization event
+          [labeling]               [hybridization]
+</pre>
+
+      <p class="text"><a name="htypes" class="subhead">Material
+      types</a><br> These columns simply indicate the type of each
+      material used. The terms should all be instances of <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php#MaterialType">MaterialType</a>
+      from the <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php">MGED
+      ontology</a>. Typical terms are shown in this example:</p>
+
+      <table class="example">
+	  <tr><td><a class="plain" href="detail.html#hybsection">Hybridization section</a></td><td> </td><td> </td><td> </td></tr>
+	  <tr>
+	    <td><a class="plain" href="detail.html#materialtype">BioSourceMaterial</a></td>
+	    <td><a class="plain" href="detail.html#materialtype">SampleMaterial</a></td>
+	    <td><a class="plain" href="detail.html#materialtype">ExtractMaterial</a></td>
+	    <td><a class="plain" href="detail.html#materialtype">LabeledExtractMaterial</a></td></tr>
+	  <tr><td>whole_organism</td><td>whole_organism</td><td>total_RNA</td><td>synthetic_DNA</td></tr>
+	  <tr><td>whole_organism</td><td>organism_part</td><td>total_RNA</td><td>synthetic_RNA</td></tr>
+	  <tr><td>organism_part</td><td>cell</td><td>polyA_RNA</td><td>synthetic_RNA</td></tr>
+      </table>
+
+      <p class="text">These columns are not absolutely required for
+      Tab2MAGE to run. However, the strictures of the MAGE model mean
+      that if these values are not given, then suitable default values
+      must be generated. It is therefore wise to use these columns
+      wherever possible.</p>
+
+      <p class="text"><a name="hchars" class="subhead">Material
+      characteristics</a><br> Each biosource used in the experiment
+      can have an arbitrary number of characteristics. The
+      "BioMaterialCharacteristics[]" heading provides a way to
+      define as many of these characteristics as are needed. The general form of this heading is: </p>
+
+      <pre>BioMaterialCharacteristics[<i><category></i>]</pre>
+
+      <p class="text">where <i><category></i> is a subclass of the <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php#BioMaterialCharacteristics">BioMaterialCharacteristics</a> class within the <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php">MGED
+      ontology</a>. Examples are given in the table below:</p>
+
+      <table class="example">
+	  <tr><td><a class="plain" href="detail.html#hybsection">Hybridization section</a></td><td> </td><td> </td></tr>
+	  <tr>
+	    <td><a class="plain" href="detail.html#bmc">BioMaterialCharacteristics[Genotype]</a></td>
+	    <td><a class="plain" href="detail.html#bmc">BioMaterialCharacteristics[Organism]</a></td>
+	    <td><a class="plain" href="detail.html#bmc">BioMaterialCharacteristics[StrainOrLine]</a></td></tr>
+	  <tr><td>CAD1::myc9:TRP1</td><td>Saccharomyces cerevisiae</td><td>S288C</td></tr>
+	  <tr><td>CAD1::myc9:TRP1</td><td>Saccharomyces cerevisiae</td><td>W303a</td></tr>
+	  <tr><td>RTG3::myc18:TRP1</td><td>Saccharomyces cerevisiae</td><td>S288C</td></tr>
+	  <tr><td>RTG3::myc18:TRP1</td><td>Saccharomyces cerevisiae</td><td>W303a</td></tr>
+      </table>
+
+      <p class="text">If the <a
+      href="detail.html#biosource">BioSource</a> name column is not
+      used, then the set of characteristics of each biosource is used
+      to determine how many biosources should be created. This means
+      that if all your biosources share identical characteristics, and
+      none are given names, then they will be treated as a single
+      biosource in the output MAGE-ML. To circumvent this behaviour,
+      please name your biosources.</p>
+
+      <p class="text"><a name="hproto" class="subhead">Links to
+      defined protocols and parameters</a><br> This set of columns
+      allows you to link the protocols defined in the <a
+      href="#protocol">previous section</a> to your hybridizations:</p>
+
+      <table class="example">
+	  <tr><td><a class="plain" href="detail.html#hybsection">Hybridization section</a></td><td> </td><td> </td><td> </td><td> </td><td> </td></tr>
+	  <tr>
+	    <td><a class="plain" href="detail.html#pgrow">Protocol[grow]</a></td>
+	    <td><a class="plain" href="detail.html#ptreat">Protocol[treatment]</a></td>
+	    <td><a class="plain" href="detail.html#pextract">Protocol[extraction]</a></td>
+	    <td><a class="plain" href="detail.html#plabel">Protocol[labeling]</a></td>
+	    <td><a class="plain" href="detail.html#phyb">Protocol[hybridization]</a></td>
+	    <td><a class="plain" href="detail.html#pscan">Protocol[scanning]</a></td></tr>
+	  <tr><td>P-EXML-1</td><td>P-EXML-2</td><td>P-EXML-3</td><td>P-EXML-4</td><td>P-EXML-5</td><td>P-EXML-6</td></tr>
+	  <tr><td>P-EXML-1</td><td>P-EXML-2</td><td>P-EXML-3</td><td>P-EXML-4</td><td>P-EXML-5</td><td>P-EXML-6</td></tr>
+	  <tr><td>P-EXML-1</td><td>P-EXML-2</td><td>P-EXML-3</td><td>P-EXML-4</td><td>P-EXML-5</td><td>P-EXML-6</td></tr>
+	  <tr><td>P-EXML-1</td><td>P-EXML-2</td><td>P-EXML-3</td><td>P-EXML-4</td><td>P-EXML-5</td><td>P-EXML-6</td></tr>
+      </table>
+
+      <p class="text">If you have assigned <a href="detail.html#protoparams">parameters</a> to your
+      protocols, you may set the parameter values here:</p>
+
+      <table class="example">
+	  <tr><td><a class="plain" href="detail.html#hybsection">Hybridization section</a></td><td> </td><td> </td><td> </td><td> </td></tr>
+	  <tr>
+	    <td><a class="plain" href="detail.html#param">Parameter[growth temperature]</a></td>
+	    <td><a class="plain" href="detail.html#param">Parameter[pH]</a></td>
+	    <td><a class="plain" href="detail.html#param">Parameter[pellet weight]</a></td>
+	    <td><a class="plain" href="detail.html#param">Parameter[hyb temp]</a></td>
+	    <td><a class="plain" href="detail.html#param">Parameter[hyb volume]</a></td></tr>
+	  <tr><td>30</td><td>6</td><td>10</td><td>56</td><td>50</td></tr>
+	  <tr><td>31</td><td>6</td><td>12</td><td>56</td><td>50</td></tr>
+	  <tr><td>30</td><td>6</td><td>10</td><td>55</td><td>50</td></tr>
+	  <tr><td>31</td><td>6</td><td>12</td><td>55</td><td>50</td></tr>
+      </table>
+
+      <p class="text">The general scheme linking protocols with
+      experimental stage is as shown below:</p>
+
+      <pre>
+biosource ----------> sample --------> extract -------> labeled extract -------> hybridization 
+      [grow, treatment]     [extraction]      [labeling]            [hybridization]    |
+                                                                                       |
+                                                                                       |
+      ----------------------------------------------------------------------------------
+      |
+      |
+      v
+hybridization ------------> image -----------> raw data file -----------> normalized data file 
+               [scanning]       [image_analysis]           [normalization]
+</pre>
+
+      <p class="text">Note that for protocols linked to the biological
+      materials, the existence of the protocol implies the existence
+      of the target material. For example, if a growth or treatment
+      protocol is used in a given row then a sample object will be
+      automatically created for that row in the output MAGE-ML.</p>
+
+      <p class="text"><a name="hfacts" class="subhead">Experimental
+      factors</a><br> Each of your hybridizations should have one or
+      more experimental factor values associated with it. These
+      factors refer to the different conditions used in the
+      preparation of each hybridization. They may reflect variation in
+      the starting biosources (<a
+      href="#hchars">BioMaterialCharacteristics</a>) or differences in
+      the way those biosources have been treated. The general form of this heading is: </p>
+
+      <pre>FactorValue[<i><category></i>]</pre>
+
+      <p class="text">where <i><category></i> is a subclass of
+      the <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php#ExperimentalFactorCategory">ExperimentalFactorCategory</a>
+      class within the <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php">MGED
+      ontology</a> (Note: you are likely to need to descend several
+      subclasses into the heirarchy to find a <a
+      href="http://mged.sourceforge.net/ontologies/MGEDontology.php#BioMaterialCharacteristics">biologically
+      meaningful</a> category). Examples are given in the table
+      below:</p>
+
+      <table class="example">
+	  <tr><td><a class="plain" href="detail.html#hybsection">Hybridization section</a></td><td> </td><td> </td></tr>
+	  <tr>
+	    <td><a class="plain" href="detail.html#fv">FactorValue[Organism]</a></td>
+	    <td><a class="plain" href="detail.html#fv">FactorValue[Sex]</a></td>
+	    <td><a class="plain" href="detail.html#fv">FactorValue[OrganismPart]</a></td></tr>
+	  <tr><td>Homo sapiens</td><td>male</td><td>liver</td></tr>
+	  <tr><td>Homo sapiens</td><td>male</td><td>kidney</td></tr>
+	  <tr><td>Homo sapiens</td><td>female</td><td>liver</td></tr>
+	  <tr><td>Homo sapiens</td><td>female</td><td>kidney</td></tr>
+	  <tr><td>Mus musculus</td><td>male</td><td>liver</td></tr>
+	  <tr><td>Mus musculus</td><td>male</td><td>kidney</td></tr>
+	  <tr><td>Mus musculus</td><td>female</td><td>liver</td></tr>
+	  <tr><td>Mus musculus</td><td>female</td><td>kidney</td></tr>
+      </table>
+
+      <p class="text">Your spreadsheet may contain as many
+      "FactorValue[]" columns as necessary to fully describe your
+      experiment.</p>
+
+      <p class="text"><a name="hfiles" class="subhead">Data files and
+      array information</a><br>The final class of headings is used to
+      specify which data files relate to which hybridization. There
+      are also columns in this section for linking the data files to
+      the array design used. Here is an example of these columns for a
+      spotted array:</p>
+
+      <table class="example">
+	  <tr><td><a class="plain" href="detail.html#hybsection">Hybridization section</a></td><td> </td><td> </td><td> </td></tr>
+	  <tr>
+	    <td><a class="plain" href="detail.html#fileraw">File[raw]</a></td>
+	    <td><a class="plain" href="detail.html#filenorm">File[normalized]</a></td>
+	    <td><a class="plain" href="detail.html#arrayacc">Array[accession]</a></td>
+	    <td><a class="plain" href="detail.html#arrayser">Array[serial]</a></td></tr>
+	  <tr><td>Data1.txt</td><td>NormData1.txt</td><td>A-EXML-1</td><td>244532</td></tr>
+	  <tr><td>Data2.txt</td><td>NormData2.txt</td><td>A-EXML-1</td><td>244533</td></tr>
+	  <tr><td>Data3.txt</td><td>NormData3.txt</td><td>A-EXML-1</td><td>244534</td></tr>
+	  <tr><td>Data4.txt</td><td>NormData4.txt</td><td>A-EXML-1</td><td>244535</td></tr>
+      </table>
+
+      <p class="text">Below is an example of what is required for an
+      Affymetrix submission. Note that for spreadsheet submissions to
+      ArrayExpress we do not require you to upload the actual CDF
+      library file, unless you are using a custom array. For such
+      submissions the "Array[serial]" column should contain Affymetrix
+      Chip Lot numbers:</p>
+
+      <table class="example">
+	  <tr><td><a class="plain" href="detail.html#hybsection">Hybridization section</a></td><td> </td><td> </td><td> </td><td> </td><td> </td></tr>
+	  <tr>
+	    <td><a class="plain" href="detail.html#fileraw">File[raw]</a></td>
+	    <td><a class="plain" href="detail.html#filenorm">File[normalized]</a></td>
+	    <td><a class="plain" href="detail.html#fileexp">File[exp]</a></td>
+	    <td><a class="plain" href="detail.html#filecdf">File[cdf]</a></td>
+	    <td><a class="plain" href="detail.html#arrayacc">Array[accession]</a></td>
+	    <td><a class="plain" href="detail.html#arrayser">Array[serial]</a></td></tr>
+	  <tr><td>Data1.CEL</td><td>Data1.CHP</td><td>Data1.EXP</td><td>YG_S98.cdf</td><td>A-AFFY-27</td><td>2125576</td></tr>
+	  <tr><td>Data2.CEL</td><td>Data2.CHP</td><td>Data2.EXP</td><td>YG_S98.cdf</td><td>A-AFFY-27</td><td>2103523</td></tr>
+	  <tr><td>Data3.CEL</td><td>Data3.CHP</td><td>Data3.EXP</td><td>YG_S98.cdf</td><td>A-AFFY-27</td><td>2468372</td></tr>
+	  <tr><td>Data4.CEL</td><td>Data4.CHP</td><td>Data4.EXP</td><td>YG_S98.cdf</td><td>A-AFFY-27</td><td>2542678</td></tr>
+      </table>
+      
+      <p class="text">Currently supported data file formats include
+      GenePix, Affymetrix, Agilent, ScanArray/QuantArray, ScanAlyze and Arrayvision. The <a
+      href="http://www.ebi.ac.uk/miamexpress/help/datafile_help.html">MetaColumn/MetaRow
+      format</a> favoured by ArrayExpress is of course also
+      supported.</p>
+
+      <p class="text">The "Array[accession]" column is required in any
+      spreadsheets referencing data files. If Tab2MAGE is used in the
+      absence of data files then the array information may be omitted,
+      although such usage of Tab2MAGE will obviously not be acceptable
+      as an ArrayExpress data submission.</p>
+
+      <p class="text">If you prefer to process your normalized data in
+      a single file mapping to multiple hybridizations, Tab2MAGE also
+      supports including this data as a <a href="datafiles.html#final_data_matrix">Final Data Matrix</a>
+      file. This file can be included in an experiment using the
+      "File[transformed]" column. <em>Please note</em> that the final data
+      matrix file must be formatted as described in the <a
+      href="datafiles.html">data file notes</a>.</p>
+
+      <table class="example">
+	  <tr><td><a class="plain" href="detail.html#hybsection">Hybridization section</a></td><td> </td><td> </td><td> </td></tr>
+	  <tr>
+	    <td><a class="plain" href="detail.html#fileraw">File[raw]</a></td>
+	    <td><a class="plain" href="detail.html#filefgem">File[transformed]</a></td>
+	    <td><a class="plain" href="detail.html#arrayacc">Array[accession]</a></td>
+	    <td><a class="plain" href="detail.html#arrayser">Hybridization</a></td></tr>
+	  <tr><td>Data1.txt</td><td>FinalDataMatrix.txt</td><td>A-EXML-1</td><td>Hyb1</td></tr>
+	  <tr><td>Data2.txt</td><td>FinalDataMatrix.txt</td><td>A-EXML-1</td><td>Hyb2</td></tr>
+	  <tr><td>Data3.txt</td><td>FinalDataMatrix.txt</td><td>A-EXML-1</td><td>Hyb3</td></tr>
+	  <tr><td>Data4.txt</td><td>FinalDataMatrix.txt</td><td>A-EXML-1</td><td>Hyb4</td></tr>
+      </table>
+
+      <p class="text"></p>
+
+      <p class="text">Please see the <a
+      href="ArrayExpress/Curator/MAGE/Definitions.html#supported_headings">MAGE
+      mappings</a> for other supported column headings, and further
+      information on how the spreadsheet information is used to create
+      the MAGE-ML document.</p>
+
+      <p CLASS="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="realexamples">Real-world Examples</a></div>
+      
+      <p class="text">Below is a selection of some of the
+      real-world curated Tab2MAGE spreadsheets submitted to
+      ArrayExpress over the past few months:</p>
+
+      <table border="1">
+	
+	  <tr>
+	    <th>Spreadsheet</th>
+	    <th>Graph</th>
+	    <th>Experiment name</th>
+	    <th>Platform</th>
+	    <th>Link to experiment</th>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/real/E-TABM-16.txt">E-TABM-16.txt</a></td>
+	    <td><a href="../examples/real/E-TABM-16.png">PNG</a></td>
+	    <td>FDA-CDER MTS RNA reagent cross platform test</td>
+	    <td>Multiple platforms</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-16">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/real/E-TABM-18.txt">E-TABM-18.txt</a></td>
+	    <td><a href="../examples/real/E-TABM-18.png">PNG</a></td>
+	    <td>Transcription profiling of 35 different Arabidopsis thaliana ecotypes</td>
+	    <td>Affymetrix</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-18">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/real/E-TABM-22.txt">E-TABM-22.txt</a></td>
+	    <td><a href="../examples/real/E-TABM-22.png">PNG</a></td>
+	    <td>Transcription profiling of human lung cancers and lung cancer cell lines miRNA expression</td>
+	    <td>One-channel custom array</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-22">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/real/E-TABM-33.txt">E-TABM-33.txt</a></td>
+	    <td><a href="../examples/real/E-TABM-33.png">PNG</a></td>
+	    <td>Transcription profiling of zebrafish development</td>
+	    <td>Affymetrix</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-33">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/real/E-TABM-35.txt">E-TABM-35.txt</a></td>
+	    <td><a href="../examples/real/E-TABM-35.png">PNG</a></td>
+	    <td>Comparative genomic hybridization of 25 Coxiella burnetii isolates relative to the Nine Mile (RSA493) reference isolate</td>
+	    <td>Affymetrix</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-35">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/real/E-TABM-54.txt">E-TABM-54.txt</a></td>
+	    <td><a href="../examples/real/E-TABM-54.png">PNG</a></td>
+	    <td>Comparative genome hybridization of 137 Bordetella pertussis strains</td>
+	    <td>Two-color custom array</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-54">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/real/E-TABM-66.txt">E-TABM-66.txt</a></td>
+	    <td><a href="../examples/real/E-TABM-66.png">PNG</a></td>
+	    <td>Transcription profiling of normal and malignant human breast epithelial cells</td>
+	    <td>Multiple platforms</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-66">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/real/E-TABM-70.txt">E-TABM-70.txt</a></td>
+	    <td><a href="../examples/real/E-TABM-70.png">PNG</a></td>
+	    <td>Transcription profiling of human cell lines treated with cytochalasin D and nocodazole with the aim of characterising tetraploid clones</td>
+	    <td>Agilent</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-70">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/real/E-TABM-102.txt">E-TABM-102.txt</a></td>
+	    <td><a href="../examples/real/E-TABM-102.png">PNG</a></td>
+	    <td>Transcription profiling of wild type and ATF3 -/-mouse bone marrow macrophages stimulated with lipopolysaccharide over time</td>
+	    <td>Affymetrix</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-102">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/real/E-TABM-134.txt">E-TABM-134.txt</a></td>
+	    <td><a href="../examples/real/E-TABM-134.png">PNG</a></td>
+	    <td>WGA-LCM and Genomewide Survey of Lung Cancer</td>
+	    <td>Affymetrix</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-134">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/real/E-TABM-136.txt">E-TABM-136.txt</a></td>
+	    <td><a href="../examples/real/E-TABM-136.png">PNG</a></td>
+	    <td>Transcription profiling of human and chimpanzee heart, brain, testis and lymphblastoid cell lines to study functionality of intergenic transcription</td>
+	    <td>Affymetrix</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-136">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/real/E-TABM-140.txt">E-TABM-140.txt</a></td>
+	    <td><a href="../examples/real/E-TABM-140.png">PNG</a></td>
+	    <td>Chromatin immunoprecipitation (ChIP-chip) of human erythroleukemia cell line K-562 with anti-histone antibodies using an ENCODE array</td>
+	    <td>Two-color custom array</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-140">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/real/E-TABM-163.txt">E-TABM-163.txt</a></td>
+	    <td><a href="../examples/real/E-TABM-163.png">PNG</a></td>
+	    <td>Transcription profiling of murine presomitic mesoderms of 17 samples at various time points to identify cyclic genes of the mouse segmentation clock</td>
+	    <td>Affymetrix</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-163">ArrayExpress</a></td>
+	  </tr>
+
+	  <tr>
+	    <td><a href="../examples/real/E-MEXP-880.txt">E-MEXP-880.txt</a></td>
+	    <td><a href="../examples/real/E-MEXP-880.png">PNG</a></td>
+	    <td>Methylation profiling of normal and cancerous breast cells from human patients and cell lines in a 125 kB region of the HOXA cluster</td>
+	    <td>Two-color custom array</td>
+	    <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-MEXP-880">ArrayExpress</a></td>
+	  </tr>
+      </table>
+
+      <p CLASS="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="examples">Conceptual Examples</a></div>
+      
+      <p class="text">A series of very simple conceptual examples is given here:</p>
+
+      <ul>
+	<li><a href="../examples/two_color/">Simple dye-swap experiment</a></li>
+	<li><a href="../examples/affymetrix/">Experiment with Affymetrix data files</a></li>
+	<li><a href="../examples/reference/">Experiment using a pooled reference sample</a></li>
+	<li><a href="../examples/normalized_data/">How to include normalized data files</a></li>
+	<li><a href="../examples/final_data_matrix/">Using a final data matrix file</a></li>
+	<li><a href="../examples/chip_chip/">ChIP chip experiment</a></li>
+	<li><a href="../examples/parameters/">Experiment with user-defined parameters</a></li>
+	<li><a href="../examples/loop_design/">Simple loop design example</a></li>
+	<li><a href="../examples/illumina/">Illumina data file example</a></li>
+      </ul>
+
+      <p CLASS="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <p class="text">Please see the <a
+    href="expt_check.html">experiment checker</a> and <a
+    href="tab2mage.html">tab2mage.pl</a> help notes for more
+    information.</p>
+
+    <hr>
+    <a href="http://sourceforge.net">
+      <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" />
+    </a>
+    <br>
+<!-- Created: Mon Jan 17 09:45:42 GMT 2005 -->
+<!-- hhmts start -->
+Last modified: Tue Feb 26 14:12:46 GMT 2008
+<!-- hhmts end -->
+  </body>
+</html>
diff --git a/docs/style.css b/docs/style.css
new file mode 100644
index 0000000..6184814
--- /dev/null
+++ b/docs/style.css
@@ -0,0 +1,171 @@
+body {
+  font-size: 14pt;
+  margin-left: 1em; 
+  margin-right: 1em;
+  width: 860px;
+  font-family: verdana, ariel, helvetica, sans-serif;
+  line-height: 140%;
+  background-color: #ceddff;
+}
+
+pre {
+  font-size: 10pt;
+  background: #dedede;
+  font-family: monospace;
+  padding: 2pt;
+  padding-left: 10pt;
+  margin-left: 2.5%; 
+  margin-right: 2.5%;  
+  border: 1px solid #bcbcbc;
+}
+
+tt { 
+  font-size: 100%;
+  font-weight: bold;
+  color: #333388;
+}
+
+td {
+  border: solid 1px #acacac;
+}
+
+th {
+  border: solid 1px #acacac;
+}
+
+ul {
+  text-align: left;
+  line-height: 140%;
+}
+
+ol {
+  text-align: left;
+  line-height: 140%;
+}
+
+em {
+  color: #000000;
+  font-style: normal;
+  font-weight: bold;
+}
+
+th { 
+  text-align: left;
+}
+
+a.plain { 
+  text-decoration: none; 
+}
+
+.text {
+  text-align: justify;
+}
+
+.imghead {
+  text-align: right;
+}
+
+.section {
+}
+
+.whitetitle {
+  font-weight: normal; 
+  font-size: 150%; 
+  text-decoration:none; 
+/*  color: #ffffff; */
+/*  background-color: #0000a0; */
+  border-top: none;
+  border-left: none;
+  border-right: none;
+  border-bottom: solid 1px #0000a0;
+  font-family: "gill sans", verdana, ariel, helvetica, sans-serif;
+}
+
+.pagetitle {
+  font-size: 150%; 
+  font-weight: bold; 
+  color: #0000a0;
+  padding: 7px;
+  border-bottom: solid 1px #0000a0;
+  border-top: none;
+  border-left: none;
+  border-right: none;
+  font-family: "gill sans", verdana, ariel, helvetica, sans-serif;
+}
+
+.layout {
+  border: none; 
+  width: 100%;
+  margin-left: auto; 
+  margin-right: auto;
+}
+
+.example {
+  font-size: 10pt;
+  background: #dedede;
+  font-family: monospace;
+  border: solid; 
+  border-width: thin; 
+  margin-left: auto; 
+  margin-right: auto;  
+}
+
+.leftexample {
+  font-size: 10pt;
+  background: #dedede;
+  font-family: monospace;
+  border: solid; 
+  border-width: thin; 
+}
+
+.information {
+  background: #eeeeee;
+  border: none;
+  border-width: thin;
+  margin-left: auto; 
+  margin-right: auto;    
+}
+
+.sectionhead {
+  border: 1px solid #0000A0;
+  background: #c0c0c0;
+  color: #0000A0; 
+  padding: 7px; 
+  text-align: left;
+  font-size: 140%;
+  font-family: "gill sans", verdana, ariel, helvetica, sans-serif;
+}
+
+.sectionfoot {
+  text-align:right;
+}
+
+.subhead {
+  text-align: left; 
+  font-weight: bold;
+  font-family: "gill sans", verdana, ariel, helvetica, sans-serif;
+  font-size: 120%;
+}
+
+.picture {
+  display:block;
+  margin-left: auto; 
+  margin-right: auto;
+  border-style: solid;
+  border-width: 2px;
+}
+
+.faqheading {
+  font-weight: bold;
+  font-size: 130%;
+}
+
+h1 {  
+  font-family: "gill sans", verdana, ariel, helvetica, sans-serif;
+  font-size: 150%;
+}
+
+h2 {  
+  font-family: "gill sans", verdana, ariel, helvetica, sans-serif;
+  font-size: 120%;
+}
diff --git a/docs/submit.css b/docs/submit.css
new file mode 100644
index 0000000..bebe4b7
--- /dev/null
+++ b/docs/submit.css
@@ -0,0 +1,91 @@
+body {
+  font-size: 12px;
+  font-family: verdana, geneva, arial, helvetica, sans-serif;
+  color: #111;
+  background-color: #ffffff;
+}
+
+input {
+  border-color: #b9cfd8;
+  border-style: solid;
+  border-width: 1px;
+  background-color: #ffffff;
+  padding: 4px 6px;
+  cursor: default;
+}
+
+.sidebar-title { 
+  border-color: #dddddd;
+  border-style: solid;
+  border-width: 1px;
+  font-size: 150%;
+  padding: 4px 6px;
+  background-color: #ffffff;
+}
+
+.page-title { 
+  font-size: 120%;
+}
+
+td { 
+  background-color: #eeeeee;
+  padding: 4px;
+}
+
+th { 
+  background-color: #dddddd;
+  padding: 4px;
+}
+
+.text {
+  width: 40em;
+  text-align: justify;  
+}
+
+.navbar {
+  width: 30em;
+  padding: 4px;
+}
+
+.fieldWithErrors {
+  padding: 0px;
+  background-color: red;
+  display: table;
+}
+
+/* Set up the sidebar */
+div.link-list {
+  width:15%;
+  position:absolute;
+  top:0;
+  bottom:0;
+  font-size:100%;
+  padding-left:1%;
+  padding-right:1%;
+  padding-top:1%;
+  background-color: #eeeeee;
+}
+
+#main {
+  margin-left:15%;
+  padding-left:3%;
+}
+
+#sidebar {
+  left:0;
+  text-align: center;
+  line-height: 150%;
+}
+
+.notice {
+  font-size: 90%;
+  color: blue;
+}
+
+:link {
+  color: #3333aa;
+}
+
+:visited {
+  color: #7700aa;
+}
\ No newline at end of file
diff --git a/docs/t2m_visualize.html b/docs/t2m_visualize.html
new file mode 100644
index 0000000..f0ab4e2
--- /dev/null
+++ b/docs/t2m_visualize.html
@@ -0,0 +1,103 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>t2m_visualize.pl - a rapid visualization script for Tab2MAGE
+spreadsheets.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#options">OPTIONS</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../index.html"><img src="T2M_logo.png" border="0" height="50" alt=
+"Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Script detail: t2m_visualize.pl</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>t2m_visualize.pl - a rapid visualization script for Tab2MAGE
+spreadsheets.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ t2m_visualize.pl -e <Tab2MAGE spreadsheet file>
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This script simply parses a Tab2MAGE spreadsheet into a format
+readable by the Graphviz ``dot'' application. If Graphviz has been
+installed the script then attempts to generate a PNG file. This PNG
+file contains a graph which represents the experiment which has
+been coded. Both the tab2mage.pl and expt_check.pl scripts include
+this functionality, but t2m_visualize.pl has two advantages: it
+does not need the data files to be present, and it works much
+faster.</p>
+<hr />
+<h1><a name="options" id="options">OPTIONS</a></h1>
+<dl>
+<dt><strong><a name="item__2de_spreadsheet_filename" id=
+"item__2de_spreadsheet_filename"><strong>-e</strong>
+<code>spreadsheet filename</code></a></strong></dt>
+<dd>
+<p>The Tab2MAGE spreadsheet to visualize.</p>
+</dd>
+<dt><strong><a name="item__2df_font_name" id=
+"item__2df_font_name"><strong>-f</strong> <code>font
+name</code></a></strong></dt>
+<dd>
+<p>Font to use in graph generation (optional).</p>
+</dd>
+<dt><strong><a name="item__2dv" id=
+"item__2dv"><strong>-v</strong></a></strong></dt>
+<dd>
+<p>Print the version of the string.</p>
+</dd>
+<dt><strong><a name="item__2dc" id=
+"item__2dc"><strong>-c</strong></a></strong></dt>
+<dd>
+<p>Overwrite (``clobber'') existing files.</p>
+</dd>
+<dt><strong><a name="item__2dh" id=
+"item__2dh"><strong>-h</strong></a></strong></dt>
+<dd>
+<p>Prints a short help text.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/tab2mage.html b/docs/tab2mage.html
new file mode 100644
index 0000000..491ae06
--- /dev/null
+++ b/docs/tab2mage.html
@@ -0,0 +1,289 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>tab2mage.pl - a script to produce valid MAGE-ML from a set
+of raw datafiles and a summary spreadsheet of defined
+format.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+        <li><a href="#name">NAME</a></li>
+        <li><a href="#synopsis">SYNOPSIS</a></li>
+        <li><a href="#description">DESCRIPTION</a></li>
+        <li><a href="#experiment_summary_file">EXPERIMENT SUMMARY FILE</a></li>
+        <ul>
+
+                <li><a href="#experiment_section">Experiment section</a></li>
+                <li><a href="#protocol_section">Protocol section</a></li>
+                <li><a href="#hybridization_section">Hybridization section</a></li>
+        </ul>
+
+        <li><a href="#options">OPTIONS</a></li>
+        <li><a href="#quantitationtype_file">QUANTITATIONTYPE FILE</a></li>
+        <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../index.html"><img src="T2M_logo.png" border="0" height="50" alt=
+"Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Script detail: tab2mage.pl</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>tab2mage.pl - a script to produce valid MAGE-ML from a set of
+raw datafiles and a summary spreadsheet of defined format.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ tab2mage.pl -e <spreadsheet_filename> -t <target_directory>
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This script is designed to take a set of unprocessed raw
+datafiles and a spreadsheet providing the experiment metadata and
+generate valid MAGE-ML. The supported data file formats are listed
+in the accompanying documentation.</p>
+<p>Important: the data files must each contain a column heading
+line immediately preceding the start of the data. Typically, raw
+unprocessed data files have this as standard. Do not remove this
+line, as the script will use it to determine which
+QuantitationTypes to keep and which to discard.</p>
+<p>There are two auxilliary files which can be supplied alongside
+the data files. The first, experiment summary spreadsheet describes
+the way in which the experiment was performed. The second simply
+defines the QuantitationTypes which are to be extracted from the
+data files. This QuantitationType file is optional, however, as the
+script is supplied with a set of defaults determined by a survey of
+incoming QuantitationTypes by ArrayExpress curators. The script
+generates a log file in the target directory which details which
+columns have been ignored.</p>
+<hr />
+<h1><a name="experiment_summary_file" id=
+"experiment_summary_file">EXPERIMENT SUMMARY FILE</a></h1>
+<p>The experiment summary file has a flexible format based around a
+set of predefined column headers. Comments may be inserted using
+the '#' character at the start of any line. The file consists of
+three sections, each of which ends with a blank line:</p>
+<h2><a name="experiment_section" id="experiment_section">Experiment
+section</a></h2>
+<p>This section contains top-level information about the
+experiment, such as the title, description and accession number.
+The section is constructed in two columns, with row names as
+described in <a href=
+"ArrayExpress/Curator/MAGE/Definitions.html#supported_headings">SUPPORTED
+HEADINGS in the ArrayExpress::Curator::MAGE::Definitions
+manpage</a>.</p>
+<h2><a name="protocol_section" id="protocol_section">Protocol
+section</a></h2>
+<p>Protocols are defined as needed in this section. The section is
+organized into rows, with column headings as described in <a href=
+"ArrayExpress/Curator/MAGE/Definitions.html#supported_headings">SUPPORTED
+HEADINGS in the ArrayExpress::Curator::MAGE::Definitions
+manpage</a>. If all of the protocols used in the experiment have
+previously been loaded into ArrayExpress and given accession
+numbers, this whole section can be omitted.</p>
+<h2><a name="hybridization_section" id=
+"hybridization_section">Hybridization section</a></h2>
+<p>This section contains the bulk of the experiment information. At
+its simplest, each row describes the route taken from BioSource to
+output data file.</p>
+<p>Column headings for BioMaterialCharacteristics and FactorValue
+should be provided in the following form:</p>
+<dl>
+<dt><strong><a name=
+"item_biomaterialcharacteristics_5b_3cmged_ontology_cate" id=
+"item_biomaterialcharacteristics_5b_3cmged_ontology_cate">BioMaterialCharacteristics[<MGED
+Ontology Category>]</a></strong></dt>
+<dt><strong><a name=
+"item_factorvalue_5b_3cmged_ontology_category_3e_5d" id=
+"item_factorvalue_5b_3cmged_ontology_category_3e_5d">FactorValue[<MGED
+Ontology Category>]</a></strong></dt>
+</dl>
+<p>Each of these headings should contain a valid Category subclass
+from the MGED ontology. The values in the columns must likewise be
+valid ontology entry values for these subclasses.</p>
+<p>Multichannel (e.g., two-colour) data can be described by
+entering each channel as a separate line. Pooling can be described
+at multiple levels by using as many lines as necessary to describe
+all the relationships between upstream and downstream samples.</p>
+<p>In a sense the Hybridization table can be compared to an SQL
+database table in the way that it provides links between MAGE
+objects. Again, the recognized column headings for this section are
+described in <a href=
+"ArrayExpress/Curator/MAGE/Definitions.html#supported_headings">SUPPORTED
+HEADINGS in the ArrayExpress::Curator::MAGE::Definitions
+manpage</a>.</p>
+<hr />
+<h1><a name="options" id="options">OPTIONS</a></h1>
+<dl>
+<dt><strong><a name="item__2de_filename" id=
+"item__2de_filename"><strong>-e</strong>
+<code>filename</code></a></strong></dt>
+<dd>
+<p>The Tab2MAGE spreadsheet to be checked.</p>
+</dd>
+<dt><strong><a name="item__2dt_directory" id=
+"item__2dt_directory"><strong>-t</strong>
+<code>directory</code></a></strong></dt>
+<dd>
+<p>The target directory to be created. This directory will contain
+the MAGE-ML file and external data files ready for validation.</p>
+</dd>
+<dt><strong><a name="item__2dn_accession" id=
+"item__2dn_accession"><strong>-n</strong>
+<code>accession</code></a></strong></dt>
+<dd>
+<p>Normally the script uses the experiment accession number from
+the spreadsheet to be parsed. In cases where no accession has been
+entered in the spreadsheet, or you wish to override that accession,
+use this option.</p>
+</dd>
+<dt><strong><a name="item__2dq_qt_filename" id=
+"item__2dq_qt_filename"><strong>-q</strong> <code>QT
+filename</code></a></strong></dt>
+<dd>
+<p>QuantitationType file. This option allows you to specify a
+custom QuantitationType definition file to override those defined
+as part of the Tab2MAGE package. See <a href=
+"ArrayExpress/Datafile/QT_list.html">the
+ArrayExpress::Datafile::QT_list manpage</a> for more
+information.</p>
+</dd>
+<dt><strong><a name="item__2dq_qt_filename" id=
+"item__2dq_qt_filename"><strong>-Q</strong> <code>QT
+filename</code></a></strong></dt>
+<dd>
+<p>QuantitationType file. This option will add the new
+QuantitationType definitions to those included with the Tab2MAGE
+package. See <a href="ArrayExpress/Datafile/QT_list.html">the
+ArrayExpress::Datafile::QT_list manpage</a> for more
+information.</p>
+</dd>
+<dt><strong><a name="item__2dk" id=
+"item__2dk"><strong>-k</strong></a></strong></dt>
+<dd>
+<p>Keep all columns in the data files, regardless of whether they
+are recognized or not. Unrecognized QTs will be created as generic
+SpecializedQuantitationTypes in the output MAGE-ML.</p>
+</dd>
+<dt><strong><a name="item__2dk" id=
+"item__2dk"><strong>-K</strong></a></strong></dt>
+<dd>
+<p>If the autosubmissions system is configured, tab2mage.pl will
+automatically reassign protocol accessions to fit a local
+convention. Use the -K option to suppress this behaviour.</p>
+</dd>
+<dt><strong><a name="item__2ds" id=
+"item__2ds"><strong>-s</strong></a></strong></dt>
+<dd>
+<p>Standalone option. This prevents the script from attempting to
+connect to ArrayExpress to retrieve array information.</p>
+</dd>
+<dt><strong><a name="item__2dr_namespace" id=
+"item__2dr_namespace"><strong>-R</strong>
+<code>namespace</code></a></strong></dt>
+<dd>
+<p>Reporter identifier prefix. By default the script uses the
+MIAMExpress convention for generating reporter identifiers. This
+option allows you to override this behaviour by supplying an
+alternate prefix for identifiers.</p>
+</dd>
+<dt><strong><a name="item__2dc_namespace" id=
+"item__2dc_namespace"><strong>-C</strong>
+<code>namespace</code></a></strong></dt>
+<dd>
+<p>CompositeSequence identifier prefix. By default the script uses
+the MIAMExpress convention for generating composite sequence
+identifiers. This option allows you to override this behaviour by
+supplying an alternate prefix for identifiers.</p>
+</dd>
+<dt><strong><a name="item__2dd_directory" id=
+"item__2dd_directory"><strong>-d</strong>
+<code>directory</code></a></strong></dt>
+<dd>
+<p>Source directory containing all the data files referenced in the
+tab2mage spreadsheet. If this is omitted, the current working
+directory will be searched for data files.</p>
+</dd>
+<dt><strong><a name="item__2dp" id=
+"item__2dp"><strong>-P</strong></a></strong></dt>
+<dd>
+<p>By default, native (usually binary) file formats are used for
+Affymetrix CEL files and all NimbleScan (NimbleGen) files. This
+encoding uses far less overhead and retains the files in their
+original formats; this is often appealing to end-users. When used
+for ArrayExpress submissions, this option allows for such files to
+be directly downloadable from the ArrayExpress web interface.
+However, in unusual circumstances when you might wish to use
+plain-text encoding for these datafile, use this option.</p>
+</dd>
+<dt><strong><a name="item__2dl" id=
+"item__2dl"><strong>-L</strong></a></strong></dt>
+<dd>
+<p>Ignore the data file size limit as configured in Config.yml
+(i.e., MAX_DATAFILE_SIZE).</p>
+</dd>
+<dt><strong><a name="item__2df_font_name" id=
+"item__2df_font_name"><strong>-f</strong> <code>font
+name</code></a></strong></dt>
+<dd>
+<p>Name of the font to be used for Graphviz-generated PNGs.</p>
+</dd>
+<dt><strong><a name="item__2dc" id=
+"item__2dc"><strong>-c</strong></a></strong></dt>
+<dd>
+<p>Overwrite preexisting files (``clobber'' option).</p>
+</dd>
+<dt><strong><a name="item__2dv" id=
+"item__2dv"><strong>-v</strong></a></strong></dt>
+<dd>
+<p>Prints the version number of the script.</p>
+</dd>
+<dt><strong><a name="item__2dh" id=
+"item__2dh"><strong>-h</strong></a></strong></dt>
+<dd>
+<p>Print a short help text summarizing these options.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="quantitationtype_file" id=
+"quantitationtype_file">QUANTITATIONTYPE FILE</a></h1>
+<p>Please see <a href="ArrayExpress/Datafile/QT_list.html">the
+ArrayExpress::Datafile::QT_list manpage</a> for a description of
+the format of this file.</p>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2004.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.
+Particular credit goes to Ele Holloway, who was responsible for
+curating the lists of QuantitationTypes included with this
+script.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/usage.html b/docs/usage.html
new file mode 100644
index 0000000..d95db49
--- /dev/null
+++ b/docs/usage.html
@@ -0,0 +1,168 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+  <head>
+    <title>Typical usage</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+    <link rel="stylesheet" href="style.css">
+  </head>
+
+  <body>
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100"><a href="../index.html"><img src="T2M_logo.png" border="0" height="50" alt="Tab2MAGE logo"></a></td>
+	    <td class="pagetitle">Typical usage</td>
+	  </tr>
+      </table>
+    </div>
+
+    <p class="text">This page gives examples of the typical ways in
+    which the scripts in this package are invoked, and addresses some
+    common pitfalls.</p>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="columns">Unknown Data File Columns</a></div>
+      
+    <p class="text">Once you have created your sample annotation spreadsheet, you
+    are ready to run the tab2mage.pl script. In an ideal world, you
+    could simply invoke the following command:</p>
+
+    <pre>tab2mage.pl -e <spreadsheet filename> -t <target directory to be created></pre>
+
+    <p class="text">This command should be executed from within the directory
+    containing the data files and the annotation spreadsheet.</p>
+
+    <p class="text">In reality, the situation is more
+    complicated. Manufacturers of scanning and image processing
+    software change the format of their output data files from time to
+    time. The most obvious example of this is when the column headings
+    (quantitation types) within a data file are altered. Additionally,
+    data files may have been modified in subsequent analyses, adding
+    customized columns. This creates a problem, because for accurate
+    encoding in MAGE-ML we need to know more information about these
+    quantitation types than just their name. Tab2MAGE stores this
+    information in a file named QT_list.txt within the installed
+    library directory. This file contains the information gathered so
+    far by ArrayExpress curators. A new version of this file is
+    released with each Tab2MAGE release as more information becomes
+    available. However, it is entirely possible that you will be using
+    data files which contain unknown quantitation types.</p>
+
+    <p class="text"><em>The default action for Tab2MAGE is to ignore unknown
+    quantitation types.</em> The package was designed like this to
+    allow very precise control of which data columns are exported to
+    MAGE-ML, and which are discarded. Tab2MAGE includes a mechanism to
+    customize its behavior so that a given site installation can set
+    up a default set of data columns which are then always exported.</p>
+
+    <p class="text">Any quantitation types which are discarded during Tab2MAGE
+    processing will be reported in the <tt>tab2mage.log</tt> file, created in
+    the target directory. To determine which columns are at risk
+    before running the tab2mage.pl script, we recommend using the <a
+    href="expt_check.html">experiment checker script</a> in Tab2MAGE mode:</p>
+
+    <pre>expt_check.pl -e <Tab2MAGE spreadsheet filename></pre>
+
+    <p class="text">This script will run some checks on the data files and generate
+    a series of log files. One of these, the <tt>expt_columnheadings.log</tt>
+    file, lists the unknown columns which would be discarded in a
+    Tab2MAGE run.</p>
+
+    <p class="text"><em>For Tab2MAGE to export unknown quantitation types</em>, you must
+    override the built-in quantitation type information using the <tt>-q</tt> or <tt>-Q</tt>
+    command-line options:</p>
+
+    <pre>expt_check.pl -q <new QT file> -e <spreadsheet filename></pre>
+    <pre>tab2mage.pl -q <new QT file> -e <spreadsheet filename> -t <target directory to be created></pre>
+      
+    <p class="text"><em>Note</em> that using the script with the
+    <tt>-q</tt> option will ignore the default QT information supplied
+    with the script; you must, therefore, include all the QTs you wish
+    to export in your new QT file. Alternatively, you may wish to use
+    the <tt>-Q</tt> option, which will use the new QTs alongside those
+    embedded in the Tab2MAGE package (but see the <a
+    href="#one_sw_per_file">note below</a>). As a last resort in case
+    of difficulties, the tab2mage.pl script also accepts a <tt>-k</tt>
+    option, which will keep <em>all</em> the unknown QTs, including
+    them in the output MAGE-ML as generic
+    SpecializedQuantitationTypes.</p>
+
+      <p class="text">The format of the QT file is described in <a
+    href="ArrayExpress/Datafile/QT_list.html#quantitation_types">these
+    QT file format notes</a>, and the <a
+    href="../examples/other_QTs.txt">other_QTs.txt</a> file included
+    with the package provides a set of examples. At its simplest,
+    however, the file can be laid out in this form:</p>
+
+    <pre>>>>Software name[Manufacturer]
+QT name 1
+QT name 2
+QT name 3
+QT name 4
+QT name 5
+QT name 6...</pre>
+
+    <p class="text">(This example will create every quantitation type as a
+    SpecializedQuantitationType object).</p>
+
+      <p class="text">For correct creation of the appropriate
+      QuantitationType subclasses, with suitable associations and
+      attributes, please see the <a
+      href="ArrayExpress/Datafile/QT_list.html#quantitation_types">QT
+      file format notes</a>. </p>
+
+      <p class="text"><em><a
+      name="one_sw_per_file">Note:</a></em> scanning software packages
+      often use column headings which are ambiguous in their
+      origin. Some column headings (e.g., "Flags") are used by many
+      different software manufacturers, and this can lead to
+      ambiguities in assigning QTs to software type. As a result, the
+      Tab2MAGE scripts have to assume that there is only one software
+      type per data file. The desired quantitation types for a given
+      data file must all be grouped together under a single <a
+      href="ArrayExpress/Datafile/QT_list.html#quantitation_types">software
+      group</a>. This prevents the creation of incorrect quantitation
+      types for ambiguous column headings which are used by more than
+      one software type.</p>
+
+      <p CLASS="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div class="section">
+
+      <div class="sectionhead"><a name="features">Feature Coordinate Checking</a></div>
+      
+    <p class="text">It is not uncommon to encounter a situation in
+    which one or more data files contain features not represented in
+    the corresponding array design. The experiment checker script will
+    automatically check for this problem when run in Tab2MAGE or
+    MIAMExpress mode. However, if you have only a set of data files
+    and a suitable <a
+    href="http://www.ebi.ac.uk/miamexpress/help/adf/">Array Definition
+    File</a> (ADF) to check them against, you can run the script in
+    stand-alone mode:</p>
+
+    <pre>expt_check.pl -a <ADF filename> -s <list of data files></pre>    
+
+    <p class="text">Please see the <a
+    href="expt_check.html">experiment checker</a> and <a
+    href="tab2mage.html">tab2mage.pl</a> help notes for more
+    information.</p>
+
+      <p CLASS="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <hr>
+    <a href="http://sourceforge.net">
+      <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" />
+    </a>
+    <br>
+<!-- Created: Mon Jan 17 09:45:42 GMT 2005 -->
+<!-- hhmts start -->
+Last modified: Sun Dec 10 12:11:16 GMT 2006
+<!-- hhmts end -->
+  </body>
+</html>
diff --git a/examples/affymetrix/Data1.CEL b/examples/affymetrix/Data1.CEL
new file mode 100644
index 0000000..368eedf
--- /dev/null
+++ b/examples/affymetrix/Data1.CEL
@@ -0,0 +1,49 @@
+[CEL]
+Version=3
+
+[HEADER]
+Cols=10
+Rows=1
+TotalX=10
+TotalY=1
+OffsetX=0
+OffsetY=0
+GridCornerUL=233 236
+GridCornerUR=4500 251
+GridCornerLR=4487 4516
+GridCornerLL=221 4501
+Axis-invertX=0
+AxisInvertY=0
+swapXY=0
+DatHeader=[0..46093]  test 1:CLS=4733 RWS=4733 XIN=3  YIN=3  VE=17        2.0 03/27/03 23:53:16       Test.1sq                  6
+Algorithm=Percentile
+AlgorithmParameters=Percentile:75;CellMargin:2;OutlierHigh:1.500;OutlierLow:1.004
+
+[INTENSITY]
+NumberCells=10
+CellHeader=X	Y	MEAN	STDV	NPIXELS
+  0	  0	158.0	28.9	 36
+  1	  0	8875.5	1390.6	 36
+  2	  0	160.0	25.3	 36
+  3	  0	8552.5	1027.3	 36
+  4	  0	78.8	15.6	 36
+  5	  0	138.8	25.5	 36
+  6	  0	8124.0	1124.9	 36
+  7	  0	146.3	22.9	 36
+  8	  0	7531.3	1038.5	 36
+  9	  0	132.3	21.7	 36
+
+[MASKS]
+NumberCells=0
+CellHeader=X	Y
+
+[OUTLIERS]
+NumberCells=3
+CellHeader=X	Y
+3	0
+6	0
+9	0
+
+[MODIFIED]
+NumberCells=0
+CellHeader=X	Y	ORIGMEAN
diff --git a/examples/affymetrix/Data1.CHP b/examples/affymetrix/Data1.CHP
new file mode 100644
index 0000000..7df0f68
Binary files /dev/null and b/examples/affymetrix/Data1.CHP differ
diff --git a/examples/affymetrix/Data1.EXP b/examples/affymetrix/Data1.EXP
new file mode 100644
index 0000000..8c93765
--- /dev/null
+++ b/examples/affymetrix/Data1.EXP
@@ -0,0 +1,49 @@
+Affymetrix GeneChip Experiment Information
+Version	1
+
+[Sample Info]
+Chip Type	HG-U133A
+Chip Lot	12345678
+Operator	Gandalf
+Sample Type	
+Description	
+Project	Test
+Comments	
+Solution Type	
+Solution Lot	
+
+[Fluidics]
+Protocol	EukGE-WS2v4
+Wash A1 Recovery Mixes	0
+Wash A1 Temperature (C)	25
+Number of Wash A1 Cycles	10
+Mixes per Wash A1 Cycle	2
+Wash B Recovery Mixes	0
+Wash B Temperature (C)	50
+Number of Wash B Cycles	4
+Mixes per Wash B Cycle	15
+Stain Temperature (C)	25
+First Stain Time (seconds)	600
+Wash A2 Recovery Mixes	0
+Wash A2 Temperature (C)	25
+Number of Wash A2 Cycles	10
+Mixes per Wash A2 Cycle	4
+Second Stain Time (seconds)	600
+Third Stain Time (seconds)	600
+Wash A3 Recovery Mixes	0
+Wash A3 Temperature (C)	30
+Number of Wash A3 Cycles	15
+Mixes per Wash A3 Cycle	4
+Holding Temperature (C)	25
+Station	2
+Module	1
+Hybridize Date	Sep 20 2003 01:10PM
+
+[Scanner]
+Pixel Size	3
+Filter	570
+Scan Temperature	30
+Scan Date	Sep 20 2003 01:25PM
+Scanner ID	87654321
+Number of Scans	2
+Scanner Type	HP
diff --git a/examples/affymetrix/Data2.CEL b/examples/affymetrix/Data2.CEL
new file mode 100644
index 0000000..a3f7d36
Binary files /dev/null and b/examples/affymetrix/Data2.CEL differ
diff --git a/examples/affymetrix/Data2.CHP b/examples/affymetrix/Data2.CHP
new file mode 100644
index 0000000..802a4cf
Binary files /dev/null and b/examples/affymetrix/Data2.CHP differ
diff --git a/examples/affymetrix/Data2.EXP b/examples/affymetrix/Data2.EXP
new file mode 100644
index 0000000..8c93765
--- /dev/null
+++ b/examples/affymetrix/Data2.EXP
@@ -0,0 +1,49 @@
+Affymetrix GeneChip Experiment Information
+Version	1
+
+[Sample Info]
+Chip Type	HG-U133A
+Chip Lot	12345678
+Operator	Gandalf
+Sample Type	
+Description	
+Project	Test
+Comments	
+Solution Type	
+Solution Lot	
+
+[Fluidics]
+Protocol	EukGE-WS2v4
+Wash A1 Recovery Mixes	0
+Wash A1 Temperature (C)	25
+Number of Wash A1 Cycles	10
+Mixes per Wash A1 Cycle	2
+Wash B Recovery Mixes	0
+Wash B Temperature (C)	50
+Number of Wash B Cycles	4
+Mixes per Wash B Cycle	15
+Stain Temperature (C)	25
+First Stain Time (seconds)	600
+Wash A2 Recovery Mixes	0
+Wash A2 Temperature (C)	25
+Number of Wash A2 Cycles	10
+Mixes per Wash A2 Cycle	4
+Second Stain Time (seconds)	600
+Third Stain Time (seconds)	600
+Wash A3 Recovery Mixes	0
+Wash A3 Temperature (C)	30
+Number of Wash A3 Cycles	15
+Mixes per Wash A3 Cycle	4
+Holding Temperature (C)	25
+Station	2
+Module	1
+Hybridize Date	Sep 20 2003 01:10PM
+
+[Scanner]
+Pixel Size	3
+Filter	570
+Scan Temperature	30
+Scan Date	Sep 20 2003 01:25PM
+Scanner ID	87654321
+Number of Scans	2
+Scanner Type	HP
diff --git a/examples/affymetrix/Data3.CEL b/examples/affymetrix/Data3.CEL
new file mode 100644
index 0000000..368eedf
--- /dev/null
+++ b/examples/affymetrix/Data3.CEL
@@ -0,0 +1,49 @@
+[CEL]
+Version=3
+
+[HEADER]
+Cols=10
+Rows=1
+TotalX=10
+TotalY=1
+OffsetX=0
+OffsetY=0
+GridCornerUL=233 236
+GridCornerUR=4500 251
+GridCornerLR=4487 4516
+GridCornerLL=221 4501
+Axis-invertX=0
+AxisInvertY=0
+swapXY=0
+DatHeader=[0..46093]  test 1:CLS=4733 RWS=4733 XIN=3  YIN=3  VE=17        2.0 03/27/03 23:53:16       Test.1sq                  6
+Algorithm=Percentile
+AlgorithmParameters=Percentile:75;CellMargin:2;OutlierHigh:1.500;OutlierLow:1.004
+
+[INTENSITY]
+NumberCells=10
+CellHeader=X	Y	MEAN	STDV	NPIXELS
+  0	  0	158.0	28.9	 36
+  1	  0	8875.5	1390.6	 36
+  2	  0	160.0	25.3	 36
+  3	  0	8552.5	1027.3	 36
+  4	  0	78.8	15.6	 36
+  5	  0	138.8	25.5	 36
+  6	  0	8124.0	1124.9	 36
+  7	  0	146.3	22.9	 36
+  8	  0	7531.3	1038.5	 36
+  9	  0	132.3	21.7	 36
+
+[MASKS]
+NumberCells=0
+CellHeader=X	Y
+
+[OUTLIERS]
+NumberCells=3
+CellHeader=X	Y
+3	0
+6	0
+9	0
+
+[MODIFIED]
+NumberCells=0
+CellHeader=X	Y	ORIGMEAN
diff --git a/examples/affymetrix/Data3.CHP b/examples/affymetrix/Data3.CHP
new file mode 100644
index 0000000..7df0f68
Binary files /dev/null and b/examples/affymetrix/Data3.CHP differ
diff --git a/examples/affymetrix/Data3.EXP b/examples/affymetrix/Data3.EXP
new file mode 100644
index 0000000..8c93765
--- /dev/null
+++ b/examples/affymetrix/Data3.EXP
@@ -0,0 +1,49 @@
+Affymetrix GeneChip Experiment Information
+Version	1
+
+[Sample Info]
+Chip Type	HG-U133A
+Chip Lot	12345678
+Operator	Gandalf
+Sample Type	
+Description	
+Project	Test
+Comments	
+Solution Type	
+Solution Lot	
+
+[Fluidics]
+Protocol	EukGE-WS2v4
+Wash A1 Recovery Mixes	0
+Wash A1 Temperature (C)	25
+Number of Wash A1 Cycles	10
+Mixes per Wash A1 Cycle	2
+Wash B Recovery Mixes	0
+Wash B Temperature (C)	50
+Number of Wash B Cycles	4
+Mixes per Wash B Cycle	15
+Stain Temperature (C)	25
+First Stain Time (seconds)	600
+Wash A2 Recovery Mixes	0
+Wash A2 Temperature (C)	25
+Number of Wash A2 Cycles	10
+Mixes per Wash A2 Cycle	4
+Second Stain Time (seconds)	600
+Third Stain Time (seconds)	600
+Wash A3 Recovery Mixes	0
+Wash A3 Temperature (C)	30
+Number of Wash A3 Cycles	15
+Mixes per Wash A3 Cycle	4
+Holding Temperature (C)	25
+Station	2
+Module	1
+Hybridize Date	Sep 20 2003 01:10PM
+
+[Scanner]
+Pixel Size	3
+Filter	570
+Scan Temperature	30
+Scan Date	Sep 20 2003 01:25PM
+Scanner ID	87654321
+Number of Scans	2
+Scanner Type	HP
diff --git a/examples/affymetrix/Data4.CEL b/examples/affymetrix/Data4.CEL
new file mode 100644
index 0000000..a3f7d36
Binary files /dev/null and b/examples/affymetrix/Data4.CEL differ
diff --git a/examples/affymetrix/Data4.CHP b/examples/affymetrix/Data4.CHP
new file mode 100644
index 0000000..802a4cf
Binary files /dev/null and b/examples/affymetrix/Data4.CHP differ
diff --git a/examples/affymetrix/Data4.EXP b/examples/affymetrix/Data4.EXP
new file mode 100644
index 0000000..8c93765
--- /dev/null
+++ b/examples/affymetrix/Data4.EXP
@@ -0,0 +1,49 @@
+Affymetrix GeneChip Experiment Information
+Version	1
+
+[Sample Info]
+Chip Type	HG-U133A
+Chip Lot	12345678
+Operator	Gandalf
+Sample Type	
+Description	
+Project	Test
+Comments	
+Solution Type	
+Solution Lot	
+
+[Fluidics]
+Protocol	EukGE-WS2v4
+Wash A1 Recovery Mixes	0
+Wash A1 Temperature (C)	25
+Number of Wash A1 Cycles	10
+Mixes per Wash A1 Cycle	2
+Wash B Recovery Mixes	0
+Wash B Temperature (C)	50
+Number of Wash B Cycles	4
+Mixes per Wash B Cycle	15
+Stain Temperature (C)	25
+First Stain Time (seconds)	600
+Wash A2 Recovery Mixes	0
+Wash A2 Temperature (C)	25
+Number of Wash A2 Cycles	10
+Mixes per Wash A2 Cycle	4
+Second Stain Time (seconds)	600
+Third Stain Time (seconds)	600
+Wash A3 Recovery Mixes	0
+Wash A3 Temperature (C)	30
+Number of Wash A3 Cycles	15
+Mixes per Wash A3 Cycle	4
+Holding Temperature (C)	25
+Station	2
+Module	1
+Hybridize Date	Sep 20 2003 01:10PM
+
+[Scanner]
+Pixel Size	3
+Filter	570
+Scan Temperature	30
+Scan Date	Sep 20 2003 01:25PM
+Scanner ID	87654321
+Number of Scans	2
+Scanner Type	HP
diff --git a/examples/affymetrix/YG_S98.cdf b/examples/affymetrix/YG_S98.cdf
new file mode 100644
index 0000000..a6d4b85
--- /dev/null
+++ b/examples/affymetrix/YG_S98.cdf
@@ -0,0 +1,192 @@
+[CDF]
+Version=GC3.0
+
+[Chip]
+Name=YG_S98
+Rows=534
+Cols=534
+NumberOfUnits=10
+MaxUnit=9375
+NumQCUnits=0
+ChipReference=
+
+[Unit1]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=1
+UnitType=3
+NumberBlocks=1
+
+[Unit1_Block1]
+Name=AFFX-MurIL2_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit2]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=2
+UnitType=3
+NumberBlocks=1
+
+[Unit2_Block1]
+Name=AFFX-MurIL10_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit3]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=3
+UnitType=3
+NumberBlocks=1
+
+[Unit3_Block1]
+Name=AFFX-MurIL4_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit4]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=4
+UnitType=3
+NumberBlocks=1
+
+[Unit4_Block1]
+Name=AFFX-MurFAS_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit5]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=5
+UnitType=3
+NumberBlocks=1
+
+[Unit5_Block1]
+Name=AFFX-BioB-5_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit6]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=6
+UnitType=3
+NumberBlocks=1
+
+[Unit6_Block1]
+Name=AFFX-BioB-M_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit7]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=7
+UnitType=3
+NumberBlocks=1
+
+[Unit7_Block1]
+Name=AFFX-BioB-3_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit8]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=8
+UnitType=3
+NumberBlocks=1
+
+[Unit8_Block1]
+Name=AFFX-BioC-5_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit9]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=9
+UnitType=3
+NumberBlocks=1
+
+[Unit9_Block1]
+Name=AFFX-BioC-3_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit10]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=10
+UnitType=3
+NumberBlocks=1
+
+[Unit10_Block1]
+Name=AFFX-BioDn-5_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
diff --git a/examples/affymetrix/affymetrix.png b/examples/affymetrix/affymetrix.png
new file mode 100644
index 0000000..2256f5c
Binary files /dev/null and b/examples/affymetrix/affymetrix.png differ
diff --git a/examples/affymetrix/affymetrix.txt b/examples/affymetrix/affymetrix.txt
new file mode 100644
index 0000000..5f7a00c
--- /dev/null
+++ b/examples/affymetrix/affymetrix.txt
@@ -0,0 +1,36 @@
+# Simple example summary spreadsheet for tab2mage.pl script.														
+# This file illustrates a simple Affymetrix experiment.														
+# Lines starting with a '#' are treated as comments and are ignored.														
+														
+Experiment section														
+domain	ebi.ac.uk													
+accession	E-EXML-2													
+quality_control	biological_replicate													
+experiment_design_type	strain_or_line_design;co-expression_design													
+name	Transcriptome analysis of invasive verses non-invasive strains of budding yeast 													
+description	An experiment was performed to...													
+release_date	2004-08-30													
+submission_date	2004-07-28													
+submitter	John Falstaff													
+# The publication details below are fictitious														
+publication_title	Comparison of the transcriptomes of invasive verses non-invasive strains of budding yeast 													
+authors	John Falstaff													
+journal	Nature Genetics													
+volume	12													
+issue	4													
+pages	123-456													
+year	2004													
+														
+# Protocol text may be formatted using standard HTML tags														
+Protocol section														
+accession	text	name											
+P-EXML-1	Cells were grown in YPD (1% yeast extract/2% peptone/2% glucose) to an OD600 of approximately 0.8	Yeast growth											
+P-EXML-3	Your protocol text here	Cell lysis and RNA prep											
+P-EXML-4	Your protocol text here	cDNA labeling											
+														
+Hybridization section														
+File[raw]	File[normalized]	File[exp]	File[cdf]	Array[accession]	Array[serial]	Protocol[grow]	Protocol[extraction]	Protocol[labeling]	BioSource	BioSourceMaterial	FactorValue[StrainOrLine]	BioMaterialCharacteristics[Organism]	BioMaterialCharacteristics[StrainOrLine]	BioMaterialCharacteristics[Sex]
+Data1.CEL	Data1.CHP	Data1.EXP	YG_S98.cdf	A-AFFY-27	243455	P-EXML-1	P-EXML-3	P-EXML-4	S288C 1	whole_organism	S288C	Saccharomyces cerevisiae	S288C	mating_type_a
+Data2.CEL	Data2.CHP	Data2.EXP	YG_S98.cdf	A-AFFY-27	244530	P-EXML-1	P-EXML-3	P-EXML-4	Sigma1278b 1	whole_organism	Sigma1278b	Saccharomyces cerevisiae	Sigma1278b	mating_type_a
+Data3.CEL	Data3.CHP	Data3.EXP	YG_S98.cdf	A-AFFY-27	348762	P-EXML-1	P-EXML-3	P-EXML-4	S288C 2	whole_organism	S288C	Saccharomyces cerevisiae	S288C	mating_type_a
+Data4.CEL	Data4.CHP	Data4.EXP	YG_S98.cdf	A-AFFY-27	234678	P-EXML-1	P-EXML-3	P-EXML-4	Sigma1278b 2	whole_organism	Sigma1278b	Saccharomyces cerevisiae	Sigma1278b	mating_type_a
diff --git a/examples/chip_chip/Data1.txt b/examples/chip_chip/Data1.txt
new file mode 100644
index 0000000..157be04
--- /dev/null
+++ b/examples/chip_chip/Data1.txt
@@ -0,0 +1,37 @@
+ATF	1.0
+24	43    
+"Type=GenePix Results 1.2"
+"DateTime=2003/01/23 11:27:59"
+"Settings=E:\settings files\array.gps"
+"GalFile="
+"Scanner=GenePix 4000A"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm	532 nm"
+"FileName=\\Server\Data1.tif"
+"PMTVolts=600	590"
+"NormalizationFactor:RatioOfMedians=1.90064"
+"NormalizationFactor:RatioOfMeans=1.90384"
+"NormalizationFactor:MedianOfRatios=1.86554"
+"NormalizationFactor:MeanOfRatios=1.23878"
+"NormalizationFactor:RegressionRatio=2.45261"
+"JpegImage=\\Server\Data1.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=1240, 6320"
+"JpegOrigin=1840, 7160"
+"Creator=GenePix Pro 3.0.0.98"
+"Temperature=1.41243"
+"LaserPower=2.19092	0.863674"
+"LaserOnTime=86000	85977"
+"Block"	"Column"	"Row"	"Name"	"ID"	"X"	"Y"	"Dia."	"F635 Median"	"F635 Mean"	"F635 SD"	"B635 Median"	"B635 Mean"	"B635 SD"	"% > B635+1SD"	"% > B635+2SD"	"F635 % Sat."	"F532 Median"	"F532 Mean"	"F532 SD"	"B532 Median"	"B532 Mean"	"B532 SD"	"% > B532+1SD"	"% > B532+2SD"	"F532 % Sat."	"Ratio of Medians"	"Ratio of Means"	"Median of Ratios"	"Mean of Ratios"	"Ratios SD"	"Rgn Ratio"	"Rgn R�"	"F Pixels"	"B Pixels"	"Sum of Medians"	"Sum of Means"	"Log Ratio"	"F635 Median - B635"	"F532 Median - B53 [...]
+1	1	1			2100	7300	90	240	240	54	40	44	21	100	100	0	502	497	98	81	86	32	100	100	0	0.475	0.481	0.494	0.491	0.129	0.413	0.731	52	376	621	616	-1.074	200	421	200	416	0
+1	2	1			2410	7300	120	694	671	209	40	41	11	98	98	0	1406	1354	413	81	81	20	100	100	0	0.494	0.496	0.500	0.599	0.409	0.433	0.777	120	778	1979	1904	-1.019	654	1325	631	1273	0
+1	3	1			2690	7320	120	501	531	267	41	41	10	99	99	0	998	1080	498	79	82	21	99	98	0	0.501	0.490	0.493	0.641	1.280	0.460	0.837	120	810	1379	1491	-0.998	460	919	490	1001	0
+1	4	1			2980	7310	120	783	770	284	40	41	10	99	96	0	1034	1014	331	81	83	22	100	99	0	0.780	0.782	0.819	1.372	3.252	0.675	0.722	120	796	1696	1663	-0.359	743	953	730	933	0
+1	5	1			3260	7320	130	457	434	219	40	41	10	100	99	0	998	938	493	81	83	21	100	98	0	0.455	0.460	0.450	0.600	0.632	0.382	0.751	120	930	1334	1251	-1.137	417	917	394	857	0
+1	6	1			3570	7310	130	166	172	67	40	41	10	94	94	0	382	388	112	82	83	20	99	98	0	0.420	0.431	0.473	0.696	2.121	0.362	0.589	120	914	426	438	-1.252	126	300	132	306	0
+1	7	1			3860	7320	100	63	65	17	40	41	10	76	55	0	182	188	53	79	80	20	100	96	0	0.223	0.229	0.226	0.313	0.289	0.160	0.283	80	552	126	134	-2.163	23	103	25	109	0
+1	8	1			4130	7330	120	415	392	122	40	41	10	96	95	0	793	779	215	79	81	20	98	97	0	0.525	0.503	0.522	0.741	1.710	0.445	0.754	120	816	1089	1052	-0.929	375	714	352	700	0
+1	9	1			4430	7320	120	153	155	61	39	41	10	97	94	0	374	382	125	80	82	20	99	99	0	0.388	0.384	0.368	0.443	0.369	0.351	0.721	120	796	408	418	-1.367	114	294	116	302	0
+1	10	1			4730	7330	120	865	826	343	40	41	10	100	98	0	1011	941	389	79	81	21	99	98	0	0.885	0.912	0.923	1.477	3.031	0.751	0.698	120	810	1757	1648	-0.176	825	932	786	862	0
diff --git a/examples/chip_chip/Data2.txt b/examples/chip_chip/Data2.txt
new file mode 100644
index 0000000..f26132d
--- /dev/null
+++ b/examples/chip_chip/Data2.txt
@@ -0,0 +1,40 @@
+ATF	1.0
+27	43    
+"Type=GenePix Results 1.4"
+"DateTime=2003/05/19 15:39:10"
+"Settings=E:\settings files\arrays.gps"
+"GalFile="
+"Scanner=GenePix 4000A [54807]"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm	532 nm"
+"FileName=\\Server\Data2.tif"
+"PMTVolts=680	610"
+"ScanPower=100	100"
+"FocusPosition=0"
+"NormalizationFactor:RatioOfMedians=0.93502"
+"NormalizationFactor:RatioOfMeans=0.917873"
+"NormalizationFactor:MedianOfRatios=0.933921"
+"NormalizationFactor:MeanOfRatios=0.954675"
+"NormalizationFactor:RegressionRatio=0.787143"
+"JpegImage=\\Server\Data2.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=560, 5360"
+"JpegOrigin=1650, 6810"
+"Creator=GenePix Pro 3.0.6.90"
+"Temperature=41.3353"
+"LaserPower=1.50975	1.13071"
+"LaserOnTime=41930	44902"
+"Supplier="
+"Block"	"Column"	"Row"	"Name"	"ID"	"X"	"Y"	"Dia."	"F635 Median"	"F635 Mean"	"F635 SD"	"B635 Median"	"B635 Mean"	"B635 SD"	"% > B635+1SD"	"% > B635+2SD"	"F635 % Sat."	"F532 Median"	"F532 Mean"	"F532 SD"	"B532 Median"	"B532 Mean"	"B532 SD"	"% > B532+1SD"	"% > B532+2SD"	"F532 % Sat."	"Ratio of Medians"	"Ratio of Means"	"Median of Ratios"	"Mean of Ratios"	"Ratios SD"	"Rgn Ratio"	"Rgn R�"	"F Pixels"	"B Pixels"	"Sum of Medians"	"Sum of Means"	"Log Ratio"	"F635 Median - B635"	"F532 Median - B53 [...]
+1	1	1	""	""	2040	7130	110	112	120	55	49	53	24	80	63	0	191	200	78	50	51	13	100	100	0	0.447	0.473	0.502	0.428	2.467	0.472	0.332	80	656	204	221	-1.162	63	141	71	150	0
+1	2	1	""	""	2330	7120	120	412	435	197	51	55	26	95	95	0	731	843	375	50	51	11	99	99	0	0.530	0.484	0.477	0.503	3.009	0.418	0.611	120	820	1042	1177	-0.916	361	681	384	793	0
+1	3	1	""	""	2610	7130	120	297	367	232	52	56	25	95	90	0	417	587	397	51	52	12	97	95	0	0.669	0.588	0.592	0.604	3.170	0.511	0.592	120	820	611	851	-0.579	245	366	315	536	0
+1	4	1	""	""	2900	7120	120	214	297	230	50	54	25	92	88	0	272	412	327	51	52	12	94	93	0	0.742	0.684	0.755	0.675	3.312	0.576	0.412	120	820	385	608	-0.430	164	221	247	361	0
+1	5	1	""	""	3200	7130	120	355	386	203	52	56	26	92	91	0	683	701	245	51	52	12	100	100	0	0.479	0.514	0.506	0.502	2.251	0.507	0.593	120	820	935	984	-1.061	303	632	334	650	0
+1	6	1	""	""	3500	7110	120	134	135	56	52	55	26	82	71	0	213	209	61	51	52	14	95	93	0	0.506	0.525	0.546	0.488	3.047	0.532	0.398	120	810	244	241	-0.982	82	162	83	158	0
+1	7	1	""	""	3770	7120	80	79	80	33	50	54	27	51	26	0	100	100	26	51	52	11	98	82	0	0.592	0.612	1.025	0.705	2.930	1.819	0.071	52	340	78	79	-0.757	29	49	30	49	0
+1	8	1	""	""	4070	7120	120	332	339	152	55	59	27	92	90	0	609	613	227	52	52	12	95	95	0	0.497	0.506	0.543	0.522	3.195	0.482	0.645	120	820	834	845	-1.008	277	557	284	561	0
+1	9	1	""	""	4370	7090	110	167	179	82	53	57	26	91	86	0	240	254	102	52	54	15	97	96	0	0.606	0.624	0.669	0.614	2.841	0.577	0.414	80	616	302	328	-0.722	114	188	126	202	0
+1	10	1	""	""	4650	7110	130	241	291	200	54	57	25	90	85	0	371	522	320	51	52	12	99	99	0	0.584	0.503	0.457	0.508	2.361	0.481	0.581	120	930	507	708	-0.775	187	320	237	471	0
diff --git a/examples/chip_chip/chip_chip.png b/examples/chip_chip/chip_chip.png
new file mode 100644
index 0000000..758f3bc
Binary files /dev/null and b/examples/chip_chip/chip_chip.png differ
diff --git a/examples/chip_chip/chip_chip.txt b/examples/chip_chip/chip_chip.txt
new file mode 100644
index 0000000..9d6713e
--- /dev/null
+++ b/examples/chip_chip/chip_chip.txt
@@ -0,0 +1,39 @@
+# Simple example summary spreadsheet for tab2mage.pl script.																					
+# This file gives an example of a ChIP chip experiment.																					
+# Lines starting with a '#' are treated as comments and are ignored.																					
+																					
+Experiment section																					
+domain	ebi.ac.uk																				
+accession	E-EXML-3																				
+experiment_design_type	strain_or_line_design;binding_site_identification_design																				
+name	RTG3 and CAD1																				
+description	An experiment was performed to...																				
+release_date	2004-08-30																				
+submission_date	2004-07-28																				
+submitter	John Falstaff																				
+# The publication details below are fictitious																					
+publication_title	Chromatin IP using myc-tagged RTG3 and CAD1 strains of Saccharomyces cerevisiae																				
+authors	John Falstaff																				
+journal	Nature Genetics																				
+volume	12																				
+issue	4																				
+pages	123-456																				
+year	2004																				
+																					
+# Protocol text may be formatted using standard HTML tags																					
+Protocol section																					
+accession	text	name																			
+P-EXML-1	Cells were grown in YPD (1% yeast extract/2% peptone/2% glucose) to an OD600 of approximately 0.8	Yeast growth																			
+P-EXML-2	Your protocol text here	Yeast cell harvesting																			
+P-EXML-3	Your protocol text here	Cell lysis and RNA prep																			
+P-EXML-4	Your protocol text here	cDNA labeling																			
+P-EXML-5	Your protocol text here	Hybridization																			
+P-EXML-6	Your protocol text here	Scanning																			
+P-EXML-7	Your protocol text here	Chromatin immunoprecipitation																			
+																					
+Hybridization section																					
+File[raw]	Array[accession]	Array[serial]	Protocol[grow]	Protocol[treatment]	Protocol[extraction]	Protocol[immunoprecipitate]	Protocol[labeling]	Protocol[hybridization]	Protocol[scanning]	BioSource	BioSourceMaterial	SampleMaterial	ExtractMaterial	ImmunoprecipitateMaterial	LabeledExtractMaterial	Dye	FactorValue[Genotype]	BioMaterialCharacteristics[Genotype]	BioMaterialCharacteristics[Organism]	BioMaterialCharacteristics[StrainOrLine]	BioMaterialCharacteristics[Sex]
+Data1.txt	A-EXML-1	244532	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-7	P-EXML-4	P-EXML-5	P-EXML-6	CAD1	whole_organism	whole_organism	genomic_DNA	genomic_DNA	genomic_DNA	Cy5	CAD1::myc9:TRP1	CAD1::myc9:TRP1	Saccharomyces cerevisiae	S288C	mating_type_a
+Data1.txt	A-EXML-1	244532	P-EXML-1	P-EXML-2	P-EXML-3		P-EXML-4	P-EXML-5	P-EXML-6	CAD1	whole_organism	whole_organism	genomic_DNA		genomic_DNA	Cy3	CAD1::myc9:TRP1	CAD1::myc9:TRP1	Saccharomyces cerevisiae	S288C	mating_type_a
+Data2.txt	A-EXML-1	244533	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-7	P-EXML-4	P-EXML-5	P-EXML-6	RTG3	whole_organism	whole_organism	genomic_DNA	genomic_DNA	genomic_DNA	Cy5	RTG3::myc18:TRP1	RTG3::myc18:TRP1	Saccharomyces cerevisiae	S288C	mating_type_a
+Data2.txt	A-EXML-1	244533	P-EXML-1	P-EXML-2	P-EXML-3		P-EXML-4	P-EXML-5	P-EXML-6	RTG3	whole_organism	whole_organism	genomic_DNA		genomic_DNA	Cy3	RTG3::myc18:TRP1	RTG3::myc18:TRP1	Saccharomyces cerevisiae	S288C	mating_type_a
diff --git a/examples/final_data_matrix/Data1.txt b/examples/final_data_matrix/Data1.txt
new file mode 100644
index 0000000..6d8b389
--- /dev/null
+++ b/examples/final_data_matrix/Data1.txt
@@ -0,0 +1,37 @@
+ATF	1																																	
+24	43																																	
+"Type=GenePix Results 1.2"																																		
+"DateTime=2003/01/23 11:27:59"																																		
+"Settings=E:\settings files\array.gps"																																		
+"GalFile="																																		
+"Scanner=GenePix 4000A"																																		
+"Comment="																																		
+"PixelSize=10"																																		
+"ImageName=635 nm	532 nm"																																	
+"FileName=\\Server\Data1.tif"																																		
+"PMTVolts=600	590"																																	
+"NormalizationFactor:RatioOfMedians=1.90064"																																		
+"NormalizationFactor:RatioOfMeans=1.90384"																																		
+"NormalizationFactor:MedianOfRatios=1.86554"																																		
+"NormalizationFactor:MeanOfRatios=1.23878"																																		
+"NormalizationFactor:RegressionRatio=2.45261"																																		
+"JpegImage=\\Server\Data1.jpg"																																		
+"RatioFormulation=W1/W2 (635 nm/532 nm)"																																		
+"Barcode="																																		
+"ImageOrigin=1240, 6320"																																		
+"JpegOrigin=1840, 7160"																																		
+"Creator=GenePix Pro 3.0.0.98"																																		
+"Temperature=1.41243"																																		
+"LaserPower=2.19092	0.863674"																																	
+"LaserOnTime=86000	85977"																																	
+"Block"	"Column"	"Row"	"Name"	"ID"	"X"	"Y"	"Dia."	"F635 Median"	"F635 Mean"	"F635 SD"	"B635 Median"	"B635 Mean"	"B635 SD"	"% > B635+1SD"	"% > B635+2SD"	"F635 % Sat."	"F532 Median"	"F532 Mean"	"F532 SD"	"B532 Median"	"B532 Mean"	"B532 SD"	"% > B532+1SD"	"% > B532+2SD"	"F532 % Sat."	"F Pixels"	"B Pixels"	"Sum of Medians"	"Sum of Means"	"F635 Median - B635"	"F532 Median - B532"	"F635 Mean - B635"	"F532 Mean - B532"	"Flags"
+1	1	1			2100	7300	90	240	240	54	40	44	21	100	100	0	502	497	98	81	86	32	100	100	0	52	376	621	616	200	421	200	416	0
+1	2	1			2410	7300	120	694	671	209	40	41	11	98	98	0	1406	1354	413	81	81	20	100	100	0	120	778	1979	1904	654	1325	631	1273	0
+1	3	1			2690	7320	120	501	531	267	41	41	10	99	99	0	998	1080	498	79	82	21	99	98	0	120	810	1379	1491	460	919	490	1001	0
+1	4	1			2980	7310	120	783	770	284	40	41	10	99	96	0	1034	1014	331	81	83	22	100	99	0	120	796	1696	1663	743	953	730	933	0
+1	5	1			3260	7320	130	457	434	219	40	41	10	100	99	0	998	938	493	81	83	21	100	98	0	120	930	1334	1251	417	917	394	857	0
+1	6	1			3570	7310	130	166	172	67	40	41	10	94	94	0	382	388	112	82	83	20	99	98	0	120	914	426	438	126	300	132	306	0
+1	7	1			3860	7320	100	63	65	17	40	41	10	76	55	0	182	188	53	79	80	20	100	96	0	80	552	126	134	23	103	25	109	0
+1	8	1			4130	7330	120	415	392	122	40	41	10	96	95	0	793	779	215	79	81	20	98	97	0	120	816	1089	1052	375	714	352	700	0
+1	9	1			4430	7320	120	153	155	61	39	41	10	97	94	0	374	382	125	80	82	20	99	99	0	120	796	408	418	114	294	116	302	0
+1	10	1			4730	7330	120	865	826	343	40	41	10	100	98	0	1011	941	389	79	81	21	99	98	0	120	810	1757	1648	825	932	786	862	0
diff --git a/examples/final_data_matrix/Data2.txt b/examples/final_data_matrix/Data2.txt
new file mode 100644
index 0000000..6975cfa
--- /dev/null
+++ b/examples/final_data_matrix/Data2.txt
@@ -0,0 +1,40 @@
+ATF	1																																	
+27	43																																	
+"Type=GenePix Results 1.4"																																		
+"DateTime=2003/05/19 15:39:10"																																		
+"Settings=E:\settings files\arrays.gps"																																		
+"GalFile="																																		
+"Scanner=GenePix 4000A [54807]"																																		
+"Comment="																																		
+"PixelSize=10"																																		
+"ImageName=635 nm	532 nm"																																	
+"FileName=\\Server\Data2.tif"																																		
+"PMTVolts=680	610"																																	
+"ScanPower=100	100"																																	
+"FocusPosition=0"																																		
+"NormalizationFactor:RatioOfMedians=0.93502"																																		
+"NormalizationFactor:RatioOfMeans=0.917873"																																		
+"NormalizationFactor:MedianOfRatios=0.933921"																																		
+"NormalizationFactor:MeanOfRatios=0.954675"																																		
+"NormalizationFactor:RegressionRatio=0.787143"																																		
+"JpegImage=\\Server\Data2.jpg"																																		
+"RatioFormulation=W1/W2 (635 nm/532 nm)"																																		
+"Barcode="																																		
+"ImageOrigin=560, 5360"																																		
+"JpegOrigin=1650, 6810"																																		
+"Creator=GenePix Pro 3.0.6.90"																																		
+"Temperature=41.3353"																																		
+"LaserPower=1.50975	1.13071"																																	
+"LaserOnTime=41930	44902"																																	
+"Supplier="																																		
+"Block"	"Column"	"Row"	"Name"	"ID"	"X"	"Y"	"Dia."	"F635 Median"	"F635 Mean"	"F635 SD"	"B635 Median"	"B635 Mean"	"B635 SD"	"% > B635+1SD"	"% > B635+2SD"	"F635 % Sat."	"F532 Median"	"F532 Mean"	"F532 SD"	"B532 Median"	"B532 Mean"	"B532 SD"	"% > B532+1SD"	"% > B532+2SD"	"F532 % Sat."	"F Pixels"	"B Pixels"	"Sum of Medians"	"Sum of Means"	"F635 Median - B635"	"F532 Median - B532"	"F635 Mean - B635"	"F532 Mean - B532"	"Flags"
+1	1	1	""	""	2040	7130	110	112	120	55	49	53	24	80	63	0	191	200	78	50	51	13	100	100	0	80	656	204	221	63	141	71	150	0
+1	2	1	""	""	2330	7120	120	412	435	197	51	55	26	95	95	0	731	843	375	50	51	11	99	99	0	120	820	1042	1177	361	681	384	793	0
+1	3	1	""	""	2610	7130	120	297	367	232	52	56	25	95	90	0	417	587	397	51	52	12	97	95	0	120	820	611	851	245	366	315	536	0
+1	4	1	""	""	2900	7120	120	214	297	230	50	54	25	92	88	0	272	412	327	51	52	12	94	93	0	120	820	385	608	164	221	247	361	0
+1	5	1	""	""	3200	7130	120	355	386	203	52	56	26	92	91	0	683	701	245	51	52	12	100	100	0	120	820	935	984	303	632	334	650	0
+1	6	1	""	""	3500	7110	120	134	135	56	52	55	26	82	71	0	213	209	61	51	52	14	95	93	0	120	810	244	241	82	162	83	158	0
+1	7	1	""	""	3770	7120	80	79	80	33	50	54	27	51	26	0	100	100	26	51	52	11	98	82	0	52	340	78	79	29	49	30	49	0
+1	8	1	""	""	4070	7120	120	332	339	152	55	59	27	92	90	0	609	613	227	52	52	12	95	95	0	120	820	834	845	277	557	284	561	0
+1	9	1	""	""	4370	7090	110	167	179	82	53	57	26	91	86	0	240	254	102	52	54	15	97	96	0	80	616	302	328	114	188	126	202	0
+1	10	1	""	""	4650	7110	130	241	291	200	54	57	25	90	85	0	371	522	320	51	52	12	99	99	0	120	930	507	708	187	320	237	471	0
diff --git a/examples/final_data_matrix/Data3.txt b/examples/final_data_matrix/Data3.txt
new file mode 100644
index 0000000..6d8b389
--- /dev/null
+++ b/examples/final_data_matrix/Data3.txt
@@ -0,0 +1,37 @@
+ATF	1																																	
+24	43																																	
+"Type=GenePix Results 1.2"																																		
+"DateTime=2003/01/23 11:27:59"																																		
+"Settings=E:\settings files\array.gps"																																		
+"GalFile="																																		
+"Scanner=GenePix 4000A"																																		
+"Comment="																																		
+"PixelSize=10"																																		
+"ImageName=635 nm	532 nm"																																	
+"FileName=\\Server\Data1.tif"																																		
+"PMTVolts=600	590"																																	
+"NormalizationFactor:RatioOfMedians=1.90064"																																		
+"NormalizationFactor:RatioOfMeans=1.90384"																																		
+"NormalizationFactor:MedianOfRatios=1.86554"																																		
+"NormalizationFactor:MeanOfRatios=1.23878"																																		
+"NormalizationFactor:RegressionRatio=2.45261"																																		
+"JpegImage=\\Server\Data1.jpg"																																		
+"RatioFormulation=W1/W2 (635 nm/532 nm)"																																		
+"Barcode="																																		
+"ImageOrigin=1240, 6320"																																		
+"JpegOrigin=1840, 7160"																																		
+"Creator=GenePix Pro 3.0.0.98"																																		
+"Temperature=1.41243"																																		
+"LaserPower=2.19092	0.863674"																																	
+"LaserOnTime=86000	85977"																																	
+"Block"	"Column"	"Row"	"Name"	"ID"	"X"	"Y"	"Dia."	"F635 Median"	"F635 Mean"	"F635 SD"	"B635 Median"	"B635 Mean"	"B635 SD"	"% > B635+1SD"	"% > B635+2SD"	"F635 % Sat."	"F532 Median"	"F532 Mean"	"F532 SD"	"B532 Median"	"B532 Mean"	"B532 SD"	"% > B532+1SD"	"% > B532+2SD"	"F532 % Sat."	"F Pixels"	"B Pixels"	"Sum of Medians"	"Sum of Means"	"F635 Median - B635"	"F532 Median - B532"	"F635 Mean - B635"	"F532 Mean - B532"	"Flags"
+1	1	1			2100	7300	90	240	240	54	40	44	21	100	100	0	502	497	98	81	86	32	100	100	0	52	376	621	616	200	421	200	416	0
+1	2	1			2410	7300	120	694	671	209	40	41	11	98	98	0	1406	1354	413	81	81	20	100	100	0	120	778	1979	1904	654	1325	631	1273	0
+1	3	1			2690	7320	120	501	531	267	41	41	10	99	99	0	998	1080	498	79	82	21	99	98	0	120	810	1379	1491	460	919	490	1001	0
+1	4	1			2980	7310	120	783	770	284	40	41	10	99	96	0	1034	1014	331	81	83	22	100	99	0	120	796	1696	1663	743	953	730	933	0
+1	5	1			3260	7320	130	457	434	219	40	41	10	100	99	0	998	938	493	81	83	21	100	98	0	120	930	1334	1251	417	917	394	857	0
+1	6	1			3570	7310	130	166	172	67	40	41	10	94	94	0	382	388	112	82	83	20	99	98	0	120	914	426	438	126	300	132	306	0
+1	7	1			3860	7320	100	63	65	17	40	41	10	76	55	0	182	188	53	79	80	20	100	96	0	80	552	126	134	23	103	25	109	0
+1	8	1			4130	7330	120	415	392	122	40	41	10	96	95	0	793	779	215	79	81	20	98	97	0	120	816	1089	1052	375	714	352	700	0
+1	9	1			4430	7320	120	153	155	61	39	41	10	97	94	0	374	382	125	80	82	20	99	99	0	120	796	408	418	114	294	116	302	0
+1	10	1			4730	7330	120	865	826	343	40	41	10	100	98	0	1011	941	389	79	81	21	99	98	0	120	810	1757	1648	825	932	786	862	0
diff --git a/examples/final_data_matrix/Data4.txt b/examples/final_data_matrix/Data4.txt
new file mode 100644
index 0000000..6975cfa
--- /dev/null
+++ b/examples/final_data_matrix/Data4.txt
@@ -0,0 +1,40 @@
+ATF	1																																	
+27	43																																	
+"Type=GenePix Results 1.4"																																		
+"DateTime=2003/05/19 15:39:10"																																		
+"Settings=E:\settings files\arrays.gps"																																		
+"GalFile="																																		
+"Scanner=GenePix 4000A [54807]"																																		
+"Comment="																																		
+"PixelSize=10"																																		
+"ImageName=635 nm	532 nm"																																	
+"FileName=\\Server\Data2.tif"																																		
+"PMTVolts=680	610"																																	
+"ScanPower=100	100"																																	
+"FocusPosition=0"																																		
+"NormalizationFactor:RatioOfMedians=0.93502"																																		
+"NormalizationFactor:RatioOfMeans=0.917873"																																		
+"NormalizationFactor:MedianOfRatios=0.933921"																																		
+"NormalizationFactor:MeanOfRatios=0.954675"																																		
+"NormalizationFactor:RegressionRatio=0.787143"																																		
+"JpegImage=\\Server\Data2.jpg"																																		
+"RatioFormulation=W1/W2 (635 nm/532 nm)"																																		
+"Barcode="																																		
+"ImageOrigin=560, 5360"																																		
+"JpegOrigin=1650, 6810"																																		
+"Creator=GenePix Pro 3.0.6.90"																																		
+"Temperature=41.3353"																																		
+"LaserPower=1.50975	1.13071"																																	
+"LaserOnTime=41930	44902"																																	
+"Supplier="																																		
+"Block"	"Column"	"Row"	"Name"	"ID"	"X"	"Y"	"Dia."	"F635 Median"	"F635 Mean"	"F635 SD"	"B635 Median"	"B635 Mean"	"B635 SD"	"% > B635+1SD"	"% > B635+2SD"	"F635 % Sat."	"F532 Median"	"F532 Mean"	"F532 SD"	"B532 Median"	"B532 Mean"	"B532 SD"	"% > B532+1SD"	"% > B532+2SD"	"F532 % Sat."	"F Pixels"	"B Pixels"	"Sum of Medians"	"Sum of Means"	"F635 Median - B635"	"F532 Median - B532"	"F635 Mean - B635"	"F532 Mean - B532"	"Flags"
+1	1	1	""	""	2040	7130	110	112	120	55	49	53	24	80	63	0	191	200	78	50	51	13	100	100	0	80	656	204	221	63	141	71	150	0
+1	2	1	""	""	2330	7120	120	412	435	197	51	55	26	95	95	0	731	843	375	50	51	11	99	99	0	120	820	1042	1177	361	681	384	793	0
+1	3	1	""	""	2610	7130	120	297	367	232	52	56	25	95	90	0	417	587	397	51	52	12	97	95	0	120	820	611	851	245	366	315	536	0
+1	4	1	""	""	2900	7120	120	214	297	230	50	54	25	92	88	0	272	412	327	51	52	12	94	93	0	120	820	385	608	164	221	247	361	0
+1	5	1	""	""	3200	7130	120	355	386	203	52	56	26	92	91	0	683	701	245	51	52	12	100	100	0	120	820	935	984	303	632	334	650	0
+1	6	1	""	""	3500	7110	120	134	135	56	52	55	26	82	71	0	213	209	61	51	52	14	95	93	0	120	810	244	241	82	162	83	158	0
+1	7	1	""	""	3770	7120	80	79	80	33	50	54	27	51	26	0	100	100	26	51	52	11	98	82	0	52	340	78	79	29	49	30	49	0
+1	8	1	""	""	4070	7120	120	332	339	152	55	59	27	92	90	0	609	613	227	52	52	12	95	95	0	120	820	834	845	277	557	284	561	0
+1	9	1	""	""	4370	7090	110	167	179	82	53	57	26	91	86	0	240	254	102	52	54	15	97	96	0	80	616	302	328	114	188	126	202	0
+1	10	1	""	""	4650	7110	130	241	291	200	54	57	25	90	85	0	371	522	320	51	52	12	99	99	0	120	930	507	708	187	320	237	471	0
diff --git a/examples/final_data_matrix/E-EXML-7.xml.dot.jpg b/examples/final_data_matrix/E-EXML-7.xml.dot.jpg
new file mode 100644
index 0000000..390886f
Binary files /dev/null and b/examples/final_data_matrix/E-EXML-7.xml.dot.jpg differ
diff --git a/examples/final_data_matrix/FGEM.txt b/examples/final_data_matrix/FGEM.txt
new file mode 100644
index 0000000..6bcb194
--- /dev/null
+++ b/examples/final_data_matrix/FGEM.txt
@@ -0,0 +1,11 @@
+Reporter Identifier	RMA(Sigma1278b_1)(Sigma1278b_2)	RMA(W303a_1)(W303a_2)
+A102340	0.147	0.473
+A102341	0.53	0.484
+A102342	0.169	0.188
+A102343	0.742	0.684
+A102344	0.479	0.514
+A102345	0.106	0.525
+A102346	0.592	0.612
+A102347	0.000	0.106
+A102348	0.606	0.624
+A102349	0.584	0.503
diff --git a/examples/final_data_matrix/NormData1.txt b/examples/final_data_matrix/NormData1.txt
new file mode 100644
index 0000000..ade2275
--- /dev/null
+++ b/examples/final_data_matrix/NormData1.txt
@@ -0,0 +1,11 @@
+MetaColumn	MetaRow	Column	Row	Reporter Identifier	"Ratio of Medians"	"Ratio of Means"	"Median of Ratios"	"Mean of Ratios"	"Ratios SD"	"Rgn Ratio"	"Rgn R�"	"Log Ratio"
+1	1	1	1	A102340	0.475	0.481	0.494	0.491	0.129	0.413	0.731	-1.074
+1	1	2	1	A102341	0.494	0.496	0.5	0.599	0.409	0.433	0.777	-1.019
+1	1	3	1	A102342	0.501	0.49	0.493	0.641	1.28	0.46	0.837	-0.998
+1	1	4	1	A102343	0.78	0.782	0.819	1.372	3.252	0.675	0.722	-0.359
+1	1	5	1	A102344	0.455	0.46	0.45	0.6	0.632	0.382	0.751	-1.137
+1	1	6	1	A102345	0.42	0.431	0.473	0.696	2.121	0.362	0.589	-1.252
+1	1	7	1	A102346	0.223	0.229	0.226	0.313	0.289	0.16	0.283	-2.163
+1	1	8	1	A102347	0.525	0.503	0.522	0.741	1.71	0.445	0.754	-0.929
+1	1	9	1	A102348	0.388	0.384	0.368	0.443	0.369	0.351	0.721	-1.367
+1	1	10	1	A102349	0.885	0.912	0.923	1.477	3.031	0.751	0.698	-0.176
diff --git a/examples/final_data_matrix/NormData2.txt b/examples/final_data_matrix/NormData2.txt
new file mode 100644
index 0000000..52c7687
--- /dev/null
+++ b/examples/final_data_matrix/NormData2.txt
@@ -0,0 +1,11 @@
+Reporter Identifier	"Ratio of Medians"	"Ratio of Means"	"Median of Ratios"	"Mean of Ratios"	"Ratios SD"	"Rgn Ratio"	"Rgn R�"	"Log Ratio"
+A102340	0.447	0.473	0.502	0.428	2.467	0.472	0.332	-1.162
+A102341	0.53	0.484	0.477	0.503	3.009	0.418	0.611	-0.916
+A102342	0.669	0.588	0.592	0.604	3.17	0.511	0.592	-0.579
+A102343	0.742	0.684	0.755	0.675	3.312	0.576	0.412	-0.43
+A102344	0.479	0.514	0.506	0.502	2.251	0.507	0.593	-1.061
+A102345	0.506	0.525	0.546	0.488	3.047	0.532	0.398	-0.982
+A102346	0.592	0.612	1.025	0.705	2.93	1.819	0.071	-0.757
+A102347	0.497	0.506	0.543	0.522	3.195	0.482	0.645	-1.008
+A102348	0.606	0.624	0.669	0.614	2.841	0.577	0.414	-0.722
+A102349	0.584	0.503	0.457	0.508	2.361	0.481	0.581	-0.775
diff --git a/examples/final_data_matrix/NormData3.txt b/examples/final_data_matrix/NormData3.txt
new file mode 100644
index 0000000..ade2275
--- /dev/null
+++ b/examples/final_data_matrix/NormData3.txt
@@ -0,0 +1,11 @@
+MetaColumn	MetaRow	Column	Row	Reporter Identifier	"Ratio of Medians"	"Ratio of Means"	"Median of Ratios"	"Mean of Ratios"	"Ratios SD"	"Rgn Ratio"	"Rgn R�"	"Log Ratio"
+1	1	1	1	A102340	0.475	0.481	0.494	0.491	0.129	0.413	0.731	-1.074
+1	1	2	1	A102341	0.494	0.496	0.5	0.599	0.409	0.433	0.777	-1.019
+1	1	3	1	A102342	0.501	0.49	0.493	0.641	1.28	0.46	0.837	-0.998
+1	1	4	1	A102343	0.78	0.782	0.819	1.372	3.252	0.675	0.722	-0.359
+1	1	5	1	A102344	0.455	0.46	0.45	0.6	0.632	0.382	0.751	-1.137
+1	1	6	1	A102345	0.42	0.431	0.473	0.696	2.121	0.362	0.589	-1.252
+1	1	7	1	A102346	0.223	0.229	0.226	0.313	0.289	0.16	0.283	-2.163
+1	1	8	1	A102347	0.525	0.503	0.522	0.741	1.71	0.445	0.754	-0.929
+1	1	9	1	A102348	0.388	0.384	0.368	0.443	0.369	0.351	0.721	-1.367
+1	1	10	1	A102349	0.885	0.912	0.923	1.477	3.031	0.751	0.698	-0.176
diff --git a/examples/final_data_matrix/NormData4.txt b/examples/final_data_matrix/NormData4.txt
new file mode 100644
index 0000000..52c7687
--- /dev/null
+++ b/examples/final_data_matrix/NormData4.txt
@@ -0,0 +1,11 @@
+Reporter Identifier	"Ratio of Medians"	"Ratio of Means"	"Median of Ratios"	"Mean of Ratios"	"Ratios SD"	"Rgn Ratio"	"Rgn R�"	"Log Ratio"
+A102340	0.447	0.473	0.502	0.428	2.467	0.472	0.332	-1.162
+A102341	0.53	0.484	0.477	0.503	3.009	0.418	0.611	-0.916
+A102342	0.669	0.588	0.592	0.604	3.17	0.511	0.592	-0.579
+A102343	0.742	0.684	0.755	0.675	3.312	0.576	0.412	-0.43
+A102344	0.479	0.514	0.506	0.502	2.251	0.507	0.593	-1.061
+A102345	0.506	0.525	0.546	0.488	3.047	0.532	0.398	-0.982
+A102346	0.592	0.612	1.025	0.705	2.93	1.819	0.071	-0.757
+A102347	0.497	0.506	0.543	0.522	3.195	0.482	0.645	-1.008
+A102348	0.606	0.624	0.669	0.614	2.841	0.577	0.414	-0.722
+A102349	0.584	0.503	0.457	0.508	2.361	0.481	0.581	-0.775
diff --git a/examples/final_data_matrix/final_data_matrix.png b/examples/final_data_matrix/final_data_matrix.png
new file mode 100644
index 0000000..943d949
Binary files /dev/null and b/examples/final_data_matrix/final_data_matrix.png differ
diff --git a/examples/final_data_matrix/final_data_matrix.txt b/examples/final_data_matrix/final_data_matrix.txt
new file mode 100644
index 0000000..5cdcdee
--- /dev/null
+++ b/examples/final_data_matrix/final_data_matrix.txt
@@ -0,0 +1,46 @@
+"# Simple example summary spreadsheet for tab2mage.pl script."																												
+"# This file illustrates how to incorporate normalized data into the spreadsheet."																												
+"# Lines starting with a '#' are treated as comments and are ignored."																												
+																												
+"Experiment section"																												
+"domain"	"ebi.ac.uk"																											
+"accession"	"E-EXML-7"																											
+"quality_control"	"dye_swap_quality_control"																											
+"experiment_design_type"	"strain_or_line_design;co-expression_design"																											
+"name"	"Transcriptome analysis of invasive verses non-invasive strains of budding yeast "																											
+"description"	"An experiment was performed to..."																											
+"release_date"	"2004-08-30"																											
+"submission_date"	"2004-07-28"																											
+"submitter"	"John Falstaff"																											
+"# The publication details below are fictitious"																												
+"publication_title"	"Comparison of the transcriptomes of invasive verses non-invasive strains of budding yeast "																											
+"authors"	"John Falstaff; Robin Goodfellow"																											
+"journal"	"Nature Genetics"																											
+"volume"	12																											
+"issue"	4																											
+"pages"	"123-456"																											
+"year"	2004																											
+"pubmed_id"	12345678																											
+																												
+"# Protocol text may be formatted using standard HTML tags"																												
+"Protocol section"																												
+"accession"	"text"	"name"																										
+"P-EXML-1"	"Cells were grown in YPD (1% yeast extract/2% peptone/2% glucose) to an OD600 of approximately 0.8"	"Yeast growth"																										
+"P-EXML-2"	"Your protocol text here"	"Yeast cell harvesting"																										
+"P-EXML-3"	"Your protocol text here"	"Cell lysis and RNA prep"																										
+"P-EXML-4"	"Your protocol text here"	"cDNA labeling"																										
+"P-EXML-5"	"Your protocol text here"	"Hybridization"																										
+"P-EXML-6"	"Your protocol text here"	"Scanning"																										
+"P-EXML-7"	"Your protocol text here"	"Log Ratio Normalization"																										
+"P-EXML-8"	"Your protocol text here"	"RMA normalization"																										
+																												
+"Hybridization section"																												
+"File[raw]"	"File[normalized]"	"NormalizationType"	"Protocol[normalization]"	"Hybridization"	"Scan"	"Normalization"	"File[transformed]"	"TransformationType"	"Protocol[transformation]"	"Software[transformation]"	"Array[accession]"	"Array[serial]"	"Protocol[grow]"	"Protocol[treatment]"	"Protocol[extraction]"	"Protocol[labeling]"	"Protocol[hybridization]"	"Protocol[scanning]"	"BioSource"	"BioSourceMaterial"	"SampleMaterial"	"ExtractMaterial"	"LabeledExtractMaterial"	"Dye"	"FactorValue[Strai [...]
+"Data1.txt"	"NormData1.txt"	"log_ratio"	"P-EXML-7"	"Sigma1278b_1"	"Sigma1278b_1"	"Sigma1278b_1"	"FGEM.txt"	"RMA"	"P-EXML-8"	"BioConductor(1.6)"	"A-EXML-1"	244532	"P-EXML-1"	"P-EXML-2"	"P-EXML-3"	"P-EXML-4"	"P-EXML-5"	"P-EXML-6"	"S288C"	"whole_organism"	"whole_organism"	"total_RNA"	"synthetic_DNA"	"Cy5"	"S288C"	"Saccharomyces cerevisiae"	"S288C"	"mating_type_a"
+"Data1.txt"	"NormData1.txt"	"log_ratio"	"P-EXML-7"	"Sigma1278b_1"	"Sigma1278b_1"	"Sigma1278b_1"	"FGEM.txt"	"RMA"	"P-EXML-8"	"BioConductor(1.6)"	"A-EXML-1"	244532	"P-EXML-1"	"P-EXML-2"	"P-EXML-3"	"P-EXML-4"	"P-EXML-5"	"P-EXML-6"	"Sigma1278b"	"whole_organism"	"whole_organism"	"total_RNA"	"synthetic_DNA"	"Cy3"	"Sigma1278b"	"Saccharomyces cerevisiae"	"Sigma1278b"	"mating_type_a"
+"Data2.txt"	"NormData2.txt"	"log_ratio"	"P-EXML-7"	"Sigma1278b_2"	"Sigma1278b_2"	"Sigma1278b_2"	"FGEM.txt"	"RMA"	"P-EXML-8"	"BioConductor(1.6)"	"A-EXML-1"	244533	"P-EXML-1"	"P-EXML-2"	"P-EXML-3"	"P-EXML-4"	"P-EXML-5"	"P-EXML-6"	"S288C"	"whole_organism"	"whole_organism"	"total_RNA"	"synthetic_DNA"	"Cy3"	"S288C"	"Saccharomyces cerevisiae"	"S288C"	"mating_type_a"
+"Data2.txt"	"NormData2.txt"	"log_ratio"	"P-EXML-7"	"Sigma1278b_2"	"Sigma1278b_2"	"Sigma1278b_2"	"FGEM.txt"	"RMA"	"P-EXML-8"	"BioConductor(1.6)"	"A-EXML-1"	244533	"P-EXML-1"	"P-EXML-2"	"P-EXML-3"	"P-EXML-4"	"P-EXML-5"	"P-EXML-6"	"Sigma1278b"	"whole_organism"	"whole_organism"	"total_RNA"	"synthetic_DNA"	"Cy5"	"Sigma1278b"	"Saccharomyces cerevisiae"	"Sigma1278b"	"mating_type_a"
+"Data3.txt"	"NormData3.txt"	"log_ratio"	"P-EXML-7"	"W303a_1"	"W303a_1"	"W303a_1"	"FGEM.txt"	"RMA"	"P-EXML-8"	"BioConductor(1.6)"	"A-EXML-1"	244532	"P-EXML-1"	"P-EXML-2"	"P-EXML-3"	"P-EXML-4"	"P-EXML-5"	"P-EXML-6"	"S288C"	"whole_organism"	"whole_organism"	"total_RNA"	"synthetic_DNA"	"Cy5"	"S288C"	"Saccharomyces cerevisiae"	"S288C"	"mating_type_a"
+"Data3.txt"	"NormData3.txt"	"log_ratio"	"P-EXML-7"	"W303a_1"	"W303a_1"	"W303a_1"	"FGEM.txt"	"RMA"	"P-EXML-8"	"BioConductor(1.6)"	"A-EXML-1"	244532	"P-EXML-1"	"P-EXML-2"	"P-EXML-3"	"P-EXML-4"	"P-EXML-5"	"P-EXML-6"	"W303a"	"whole_organism"	"whole_organism"	"total_RNA"	"synthetic_DNA"	"Cy3"	"W303a"	"Saccharomyces cerevisiae"	"W303a"	"mating_type_a"
+"Data4.txt"	"NormData4.txt"	"log_ratio"	"P-EXML-7"	"W303a_2"	"W303a_2"	"W303a_2"	"FGEM.txt"	"RMA"	"P-EXML-8"	"BioConductor(1.6)"	"A-EXML-1"	244533	"P-EXML-1"	"P-EXML-2"	"P-EXML-3"	"P-EXML-4"	"P-EXML-5"	"P-EXML-6"	"S288C"	"whole_organism"	"whole_organism"	"total_RNA"	"synthetic_DNA"	"Cy3"	"S288C"	"Saccharomyces cerevisiae"	"S288C"	"mating_type_a"
+"Data4.txt"	"NormData4.txt"	"log_ratio"	"P-EXML-7"	"W303a_2"	"W303a_2"	"W303a_2"	"FGEM.txt"	"RMA"	"P-EXML-8"	"BioConductor(1.6)"	"A-EXML-1"	244533	"P-EXML-1"	"P-EXML-2"	"P-EXML-3"	"P-EXML-4"	"P-EXML-5"	"P-EXML-6"	"W303a"	"whole_organism"	"whole_organism"	"total_RNA"	"synthetic_DNA"	"Cy5"	"W303a"	"Saccharomyces cerevisiae"	"W303a"	"mating_type_a"
diff --git a/examples/illumina/GeneGroupedData.txt b/examples/illumina/GeneGroupedData.txt
new file mode 100644
index 0000000..7aeca77
--- /dev/null
+++ b/examples/illumina/GeneGroupedData.txt
@@ -0,0 +1,18 @@
+Illumina Inc. BeadStudio version 3.2.3.27166
+Normalization = cubic spline
+Array Content = Human_WG-6.xml.xml
+Error Model = none
+DateTime = 4/7/2006 6:38 PM
+Local Settings = en-US
+
+PROBE_ID	Tissue1.MIN_Signal	Tissue1.AVG_Signal	Tissue1.MAX_Signal	Tissue1.NARRAYS	Tissue1.ARRAY_STDEV	Tissue1.BEAD_STDEV	Tissue1.Avg_NBEADS	Tissue1.Detection	Tissue2.MIN_Signal	Tissue2.AVG_Signal	Tissue2.MAX_Signal	Tissue2.NARRAYS	Tissue2.ARRAY_STDEV	Tissue2.BEAD_STDEV	Tissue2.Avg_NBEADS	Tissue2.Detection	Mixture1.MIN_Signal	Mixture1.AVG_Signal	Mixture1.MAX_Signal	Mixture1.NARRAYS	Mixture1.ARRAY_STDEV	Mixture1.BEAD_STDEV	Mixture1.Avg_NBEADS	Mixture1.Detection	Mixture2.MIN_Signal	Mixture2 [...]
+360450	163.6	192.6	215.3	5	22.896	10.220	29	0.99934	-4.8	-3.0	-1.2	5	1.655	4.069	40	0.41134	8.0	21.5	26.0	5	7.722	6.041	30	0.96902	0.5	9.9	18.7	5	8.033	6.284	30	0.79103
+1690139	12100.1	12690.8	13437.3	5	651.837	334.077	33	1.00000	17.4	47.5	80.8	5	28.639	11.742	36	0.95781	3713.4	3793.4	3842.4	5	56.041	99.723	33	1.00000	58.8	68.1	73.7	5	5.810	8.072	25	0.99604
+5420594	233.8	242.7	258.0	5	9.281	15.148	25	1.00000	321.2	330.7	351.8	5	12.739	21.817	28	0.99934	155.4	177.5	217.1	5	27.665	10.574	23	1.00000	422.2	440.1	484.5	5	26.092	24.009	19	1.00000
+3060411	407.1	438.3	466.4	5	21.578	16.360	29	1.00000	554.5	608.8	662.0	5	44.600	31.865	31	1.00000	154.9	192.4	227.6	5	30.232	8.826	34	1.00000	726.4	779.1	860.0	5	53.943	29.842	31	1.00000
+450341	2470.8	2566.6	2643.4	5	63.710	60.129	36	1.00000	1517.7	1579.3	1625.1	5	41.988	49.936	39	1.00000	1453.4	1500.6	1575.1	5	46.195	48.919	36	1.00000	2464.9	2529.3	2625.8	5	61.878	70.328	30	1.00000
+5420324	7.2	18.0	31.5	5	10.067	7.129	22	0.91628	2.5	11.4	23.2	5	8.795	5.271	29	0.75346	4.6	12.8	18.5	5	5.522	6.571	23	0.91430	253.5	267.7	288.8	5	14.313	20.951	19	1.00000
+730162	-5.3	3.1	7.8	5	5.348	4.983	31	0.63810	-11.5	-4.0	9.3	5	9.722	4.934	29	0.39156	-7.5	-1.6	1.8	5	3.514	5.049	27	0.53790	-7.9	0.4	7.9	5	5.624	5.104	29	0.54318
+4200739	53.9	59.2	64.5	5	4.617	6.342	30	0.99011	504.8	522.2	549.4	5	21.540	25.760	38	1.00000	132.9	158.6	170.5	5	15.093	8.087	33	1.00000	507.0	534.0	565.3	5	25.053	20.384	33	1.00000
+1090156	296.5	335.7	394.3	5	36.982	15.301	26	1.00000	-1.4	7.9	19.4	5	8.141	5.717	35	0.67172	62.8	69.2	77.7	5	5.786	7.231	33	0.99604	8.5	13.6	21.3	5	5.383	6.077	25	0.87607
+7050341	1.3	6.1	16.9	5	6.255	5.619	33	0.72577	4.2	13.2	20.5	5	6.862	6.073	35	0.78708	-0.9	6.3	13.9	5	5.681	5.052	34	0.78840	4.3	15.8	23.6	5	7.492	5.439	35	0.90112
diff --git a/examples/illumina/ProbeData.txt b/examples/illumina/ProbeData.txt
new file mode 100644
index 0000000..b29d4e6
--- /dev/null
+++ b/examples/illumina/ProbeData.txt
@@ -0,0 +1,18 @@
+Illumina Inc. BeadStudio version 3.2.3.27166
+Normalization = cubic spline
+Array Content = Human_WG-6.xml.xml
+Error Model = none
+DateTime = 4/7/2006 6:38 PM
+Local Settings = en-US
+
+PROBE_ID	1323910012_A.MIN_Signal	1323910012_A.AVG_Signal	1323910012_A.MAX_Signal	1323910012_A.NARRAYS	1323910012_A.ARRAY_STDEV	1323910012_A.BEAD_STDEV	1323910012_A.Avg_NBEADS	1323910012_A.Detection	1323910012_B.MIN_Signal	1323910012_B.AVG_Signal	1323910012_B.MAX_Signal	1323910012_B.NARRAYS	1323910012_B.ARRAY_STDEV	1323910012_B.BEAD_STDEV	1323910012_B.Avg_NBEADS	1323910012_B.Detection	1323910012_C.MIN_Signal	1323910012_C.AVG_Signal	1323910012_C.MAX_Signal	1323910012_C.NARRAYS	1323910012_C [...]
+360450	157.5	157.5	157.5	1	NaN	5.821	26	0.99868	207.8	207.8	207.8	1	NaN	12.980	27	0.99934	170.4	170.4	170.4	1	NaN	9.440	35	0.99934	206.7	206.7	206.7	1	NaN	11.886	21	0.99934	186.8	186.8	186.8	1	NaN	7.323	38	1.00000	-2.5	-2.5	-2.5	1	NaN	4.488	43	0.43243	-0.9	-0.9	-0.9	1	NaN	4.168	32	0.46210	-4.0	-4.0	-4.0	1	NaN	4.108	34	0.37640	-4.4	-4.4	-4.4	1	NaN	3.755	52	0.39288	-0.6	-0.6	-0.6	1	NaN	3.780	37	0.46473	8.6	8.6	8.6	1	NaN	2.680	25	0.79565	26.3	26.3	26.3	1	NaN	4.459	36	0.98220	26.2	26.2	26.2	 [...]
+1690139	13230.6	13230.6	13230.6	1	NaN	325.577	29	1.00000	12232.2	12232.2	12232.2	1	NaN	357.805	33	1.00000	11960.1	11960.1	11960.1	1	NaN	225.731	34	1.00000	11912.3	11912.3	11912.3	1	NaN	322.376	35	1.00000	13139.1	13139.1	13139.1	1	NaN	389.941	33	1.00000	18.2	18.2	18.2	1	NaN	5.875	36	0.83125	23.4	23.4	23.4	1	NaN	5.492	36	0.85959	70.5	70.5	70.5	1	NaN	17.215	33	0.97627	42.9	42.9	42.9	1	NaN	7.071	38	0.95056	78.3	78.3	78.3	1	NaN	15.564	37	0.98220	3751.4	3751.4	3751.4	1	NaN	102.943	28	1.00000	3 [...]
+5420594	236.1	236.1	236.1	1	NaN	21.651	16	1.00000	232.5	232.5	232.5	1	NaN	9.196	26	1.00000	226.3	226.3	226.3	1	NaN	10.479	33	1.00000	250.7	250.7	250.7	1	NaN	18.603	19	1.00000	230.2	230.2	230.2	1	NaN	8.282	30	1.00000	342.6	342.6	342.6	1	NaN	29.768	17	0.99934	324.4	324.4	324.4	1	NaN	16.646	24	0.99934	312.9	312.9	312.9	1	NaN	19.538	32	0.99934	315.9	315.9	315.9	1	NaN	17.821	39	0.99736	312.0	312.0	312.0	1	NaN	19.734	28	0.99934	149.4	149.4	149.4	1	NaN	11.086	21	1.00000	209.8	209.8	209.8	1	NaN	 [...]
+3060411	419.2	419.2	419.2	1	NaN	14.573	25	1.00000	432.8	432.8	432.8	1	NaN	15.491	33	1.00000	396.3	396.3	396.3	1	NaN	13.728	29	1.00000	451.0	451.0	451.0	1	NaN	20.503	26	1.00000	428.8	428.8	428.8	1	NaN	14.104	31	1.00000	613.8	613.8	613.8	1	NaN	24.467	36	1.00000	553.9	553.9	553.9	1	NaN	33.553	29	1.00000	600.4	600.4	600.4	1	NaN	25.920	31	1.00000	540.8	540.8	540.8	1	NaN	30.170	27	1.00000	639.7	639.7	639.7	1	NaN	38.179	32	1.00000	205.9	205.9	205.9	1	NaN	12.051	28	1.00000	163.2	163.2	163.2	1	Na [...]
+450341	2542.7	2542.7	2542.7	1	NaN	57.743	27	1.00000	2419.4	2419.4	2419.4	1	NaN	43.567	46	1.00000	2501.8	2501.8	2501.8	1	NaN	62.710	28	1.00000	2509.3	2509.3	2509.3	1	NaN	62.196	48	1.00000	2584.8	2584.8	2584.8	1	NaN	65.386	32	1.00000	1549.3	1549.3	1549.3	1	NaN	51.455	41	1.00000	1596.1	1596.1	1596.1	1	NaN	49.101	37	1.00000	1525.7	1525.7	1525.7	1	NaN	58.618	36	1.00000	1482.3	1482.3	1482.3	1	NaN	44.746	33	1.00000	1576.9	1576.9	1576.9	1	NaN	37.999	47	1.00000	1414.2	1414.2	1414.2	1	NaN	54.880	2 [...]
+5420324	7.7	7.7	7.7	1	NaN	6.353	23	0.74621	31.6	31.6	31.6	1	NaN	7.836	20	0.96111	18.1	18.1	18.1	1	NaN	8.746	16	0.89057	24.6	24.6	24.6	1	NaN	6.910	28	0.93672	10.3	10.3	10.3	1	NaN	5.306	21	0.78840	17.5	17.5	17.5	1	NaN	5.599	39	0.81938	23.9	23.9	23.9	1	NaN	5.513	28	0.86486	11.6	11.6	11.6	1	NaN	4.647	31	0.72577	3.0	3.0	3.0	1	NaN	4.182	25	0.57086	4.0	4.0	4.0	1	NaN	6.170	21	0.57680	5.0	5.0	5.0	1	NaN	5.007	28	0.71457	16.6	16.6	16.6	1	NaN	7.569	20	0.94067	10.7	10.7	10.7	1	NaN	5.237	29	0.84113	16 [...]
+730162	-4.9	-4.9	-4.9	1	NaN	4.708	27	0.41266	8.4	8.4	8.4	1	NaN	5.663	41	0.74489	3.6	3.6	3.6	1	NaN	4.223	29	0.62162	8.2	8.2	8.2	1	NaN	6.101	28	0.74160	2.5	2.5	2.5	1	NaN	3.856	30	0.61833	-11.4	-11.4	-11.4	1	NaN	5.531	24	0.21885	-11.3	-11.3	-11.3	1	NaN	4.882	24	0.21028	9.9	9.9	9.9	1	NaN	4.898	35	0.69875	-9.5	-9.5	-9.5	1	NaN	4.025	33	0.25313	4.0	4.0	4.0	1	NaN	5.206	30	0.57680	0.5	0.5	0.5	1	NaN	5.029	25	0.58075	-0.8	-0.8	-0.8	1	NaN	5.831	30	0.54581	2.3	2.3	2.3	1	NaN	4.046	29	0.60976	-7.1	-7.1 [...]
+4200739	54.0	54.0	54.0	1	NaN	5.792	31	0.98879	62.9	62.9	62.9	1	NaN	5.879	33	0.99209	58.5	58.5	58.5	1	NaN	6.012	28	0.98879	61.3	61.3	61.3	1	NaN	7.225	29	0.98879	52.8	52.8	52.8	1	NaN	6.105	27	0.98813	494.0	494.0	494.0	1	NaN	25.935	44	1.00000	536.6	536.6	536.6	1	NaN	23.539	41	1.00000	490.2	490.2	490.2	1	NaN	30.217	30	1.00000	490.8	490.8	490.8	1	NaN	23.458	42	1.00000	529.4	529.4	529.4	1	NaN	21.175	35	1.00000	161.5	161.5	161.5	1	NaN	9.237	26	1.00000	158.0	158.0	158.0	1	NaN	6.707	39	1.00000	15 [...]
+1090156	384.2	384.2	384.2	1	NaN	11.330	18	1.00000	306.2	306.2	306.2	1	NaN	9.133	13	1.00000	334.4	334.4	334.4	1	NaN	15.539	33	1.00000	289.1	289.1	289.1	1	NaN	16.736	39	1.00000	320.0	320.0	320.0	1	NaN	19.402	25	1.00000	20.2	20.2	20.2	1	NaN	8.095	31	0.85234	4.0	4.0	4.0	1	NaN	5.022	34	0.56625	5.8	5.8	5.8	1	NaN	5.910	33	0.62030	13.3	13.3	13.3	1	NaN	4.399	41	0.76467	-0.8	-0.8	-0.8	1	NaN	4.292	38	0.45880	74.8	74.8	74.8	1	NaN	5.652	27	0.99802	63.4	63.4	63.4	1	NaN	12.553	28	0.99604	69.2	69.2	69.2 [...]
+7050341	1.9	1.9	1.9	1	NaN	4.078	31	0.60580	3.6	3.6	3.6	1	NaN	7.133	23	0.63612	6.6	6.6	6.6	1	NaN	5.750	40	0.70864	3.7	3.7	3.7	1	NaN	4.862	33	0.63942	17.6	17.6	17.6	1	NaN	5.800	39	0.89453	21.3	21.3	21.3	1	NaN	6.834	30	0.85695	16.3	16.3	16.3	1	NaN	6.212	36	0.79169	8.6	8.6	8.6	1	NaN	5.130	42	0.67963	18.6	18.6	18.6	1	NaN	6.243	40	0.83652	4.6	4.6	4.6	1	NaN	5.814	27	0.59130	14.7	14.7	14.7	1	NaN	5.893	28	0.89519	-0.3	-0.3	-0.3	1	NaN	4.062	35	0.55900	10.2	10.2	10.2	1	NaN	5.905	37	0.83191	3.9	3.9	 [...]
diff --git a/examples/illumina/illumina.png b/examples/illumina/illumina.png
new file mode 100644
index 0000000..e49dbc3
Binary files /dev/null and b/examples/illumina/illumina.png differ
diff --git a/examples/illumina/illumina.txt b/examples/illumina/illumina.txt
new file mode 100644
index 0000000..d69b865
--- /dev/null
+++ b/examples/illumina/illumina.txt
@@ -0,0 +1,46 @@
+# Simple example spreadsheet for Tab2MAGE.								
+# This file illustrates how to reference multi-hyb Illumina data files from within the spreadsheet.								
+# Note that more protocols would typically be required for MIAME compliance.								
+# Lines starting with a '#' are treated as comments and are ignored.								
+								
+Experiment section								
+domain	ebi.ac.uk							
+accession	E-EXML-9							
+quality_control	technical_replicate							
+experiment_design_type	array_platform_variation_design							
+name	MAQC Illumina data set (excerpt)							
+description	The MicroArray Quality Control (MAQC) project was initiated to address these concerns, as well as other performance and analysis issues. We demonstrate the consistency of results within a platform across test sites as well as the high level of cross-platform concordance in terms of genes identified as differentially expressed. The MAQC study provides a rich resource that will help build consensus on the use of microarrays in research, clinical and regulatory settings. Manuscr [...]
+release_date	2006-09-08							
+submission_date	2006-08-02							
+submitter	Leming Shi							
+publication_title	The MicroArray Quality Control (MAQC) project demonstrates concordant results between gene expression technology platforms							
+authors	Leming Shi, Laura H. Reid, Richard Shippy, Janet A. Warrington, William Slikker Jr.							
+journal	Nature Biotechnology							
+								
+# Protocol text may be formatted using standard HTML tags								
+Protocol section								
+accession	text	name						
+P-EXML-1	Data was processed using Illumina BeadStudio version 1.5.0.34 software using background subtraction and cubic spline normalization	BeadStudio normalization						
+								
+Hybridization section								
+BioSource	LabeledExtract	Dye	File[raw]	Array[accession]	Hybridization	Normalization	Protocol[transformation]	File[transformed]	FactorValue[Sample]
+A	1A	biotin	ProbeData.txt	A-MEXP-524	1323910012_A	Tissue1	P-EXML-1	GeneGroupedData.txt	1
+A	1A	biotin	ProbeData.txt	A-MEXP-524	1323910012_B	Tissue1	P-EXML-1	GeneGroupedData.txt	1
+A	1A	biotin	ProbeData.txt	A-MEXP-524	1323910012_C	Tissue1	P-EXML-1	GeneGroupedData.txt	1
+A	1A	biotin	ProbeData.txt	A-MEXP-524	1323910012_D	Tissue1	P-EXML-1	GeneGroupedData.txt	1
+A	1A	biotin	ProbeData.txt	A-MEXP-524	1323910012_E	Tissue1	P-EXML-1	GeneGroupedData.txt	1
+B	1B	biotin	ProbeData.txt	A-MEXP-524	1323901020_A	Tissue2	P-EXML-1	GeneGroupedData.txt	2
+B	1B	biotin	ProbeData.txt	A-MEXP-524	1323901020_B	Tissue2	P-EXML-1	GeneGroupedData.txt	2
+B	1B	biotin	ProbeData.txt	A-MEXP-524	1323901020_C	Tissue2	P-EXML-1	GeneGroupedData.txt	2
+B	1B	biotin	ProbeData.txt	A-MEXP-524	1323901020_E	Tissue2	P-EXML-1	GeneGroupedData.txt	2
+B	1B	biotin	ProbeData.txt	A-MEXP-524	1323901020_F	Tissue2	P-EXML-1	GeneGroupedData.txt	2
+C	1C	biotin	ProbeData.txt	A-MEXP-524	1323901024_A	Mixture1	P-EXML-1	GeneGroupedData.txt	3
+C	1C	biotin	ProbeData.txt	A-MEXP-524	1323901024_B	Mixture1	P-EXML-1	GeneGroupedData.txt	3
+C	1C	biotin	ProbeData.txt	A-MEXP-524	1323901024_D	Mixture1	P-EXML-1	GeneGroupedData.txt	3
+C	1C	biotin	ProbeData.txt	A-MEXP-524	1323901024_E	Mixture1	P-EXML-1	GeneGroupedData.txt	3
+C	1C	biotin	ProbeData.txt	A-MEXP-524	1323901024_F	Mixture1	P-EXML-1	GeneGroupedData.txt	3
+D	1D	biotin	ProbeData.txt	A-MEXP-524	1323928007_A	Mixture2	P-EXML-1	GeneGroupedData.txt	4
+D	1D	biotin	ProbeData.txt	A-MEXP-524	1323928007_C	Mixture2	P-EXML-1	GeneGroupedData.txt	4
+D	1D	biotin	ProbeData.txt	A-MEXP-524	1323928007_D	Mixture2	P-EXML-1	GeneGroupedData.txt	4
+D	1D	biotin	ProbeData.txt	A-MEXP-524	1323928007_E	Mixture2	P-EXML-1	GeneGroupedData.txt	4
+D	1D	biotin	ProbeData.txt	A-MEXP-524	1323928007_F	Mixture2	P-EXML-1	GeneGroupedData.txt	4
diff --git a/examples/loop_design/loop_design.png b/examples/loop_design/loop_design.png
new file mode 100644
index 0000000..c837dbf
Binary files /dev/null and b/examples/loop_design/loop_design.png differ
diff --git a/examples/loop_design/loop_design.txt b/examples/loop_design/loop_design.txt
new file mode 100644
index 0000000..5e636f2
--- /dev/null
+++ b/examples/loop_design/loop_design.txt
@@ -0,0 +1,55 @@
+# Simple example summary spreadsheet for tab2mage.pl script.																								
+# This file illustrates how to describe a loop design experiment in the spreadsheet.																								
+# Lines starting with a '#' are treated as comments and are ignored.																								
+																								
+Experiment section																								
+domain	ebi.ac.uk																							
+accession	E-EXML-8																							
+quality_control	dye_swap_quality_control																							
+experiment_design_type	organism_part_comparison_design, loop_design																							
+name	Analysis of tissue-specific expression profile using a loop design																							
+description	An experiment was performed to...																							
+release_date	2004-08-30																							
+submission_date	2004-07-28																							
+submitter	John Falstaff																							
+# The publication details below are fictitious																								
+publication_title	Analysis of tissue-specific expression profile using a loop design																							
+authors	John Falstaff; Robin Goodfellow																							
+journal	Genome Biol																							
+volume	12																							
+issue	4																							
+pages	123-456																							
+year	2004																							
+																								
+# Protocol text may be formatted using standard HTML tags																								
+Protocol section																								
+accession	text	name																						
+P-EXML-1	Your protocol text here	Mouse strain maintenance																						
+P-EXML-2	Your protocol text here	Dissection																						
+P-EXML-3	Your protocol text here	RNA Extraction																						
+P-EXML-4	Your protocol text here	cDNA labeling																						
+P-EXML-5	Your protocol text here	Hybridization																						
+P-EXML-6	Your protocol text here	Scanning																						
+																								
+Hybridization section																								
+BioSource	Sample	Extract	LabeledExtract	Hybridization	File[raw]	Array[accession]	Array[serial]	Protocol[grow]	Protocol[treatment]	Protocol[extraction]	Protocol[labeling]	Protocol[hybridization]	Protocol[scanning]	BioSourceMaterial	SampleMaterial	ExtractMaterial	LabeledExtractMaterial	Dye	FactorValue[OrganismPart]	BioMaterialCharacteristics[Organism]	BioMaterialCharacteristics[StrainOrLine]	BioMaterialCharacteristics[Sex]	BioMaterialCharacteristics[BioSourceType]	BioMaterialCharacteristic [...]
+brain	brain	brain	brain Cy3	spleen Cy5 vs brain Cy3	spleen_Cy5_brain_Cy3.txt	A-EXML-1	10	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	organism_part	organism_part	total_RNA	synthetic_DNA	Cy3	brain	Mus musculus	C57BL/6	male	frozen_sample	brain
+brain	brain	brain	brain Cy3	brain Cy3 vs heart Cy5	brain_Cy3_heart_Cy5.txt	A-EXML-1	1	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	organism_part	organism_part	total_RNA	synthetic_DNA	Cy3	brain	Mus musculus	C57BL/6	male	frozen_sample	brain
+brain	brain	brain	brain Cy5	spleen Cy3 vs brain Cy5	spleen_Cy3_brain_Cy5.txt	A-EXML-1	9	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	organism_part	organism_part	total_RNA	synthetic_DNA	Cy5	brain	Mus musculus	C57BL/6	male	frozen_sample	brain
+brain	brain	brain	brain Cy5	brain Cy5 vs heart Cy3	brain_Cy5_heart_Cy3.txt	A-EXML-1	2	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	organism_part	organism_part	total_RNA	synthetic_DNA	Cy5	brain	Mus musculus	C57BL/6	male	frozen_sample	brain
+heart	heart	heart	heart Cy3	brain Cy5 vs heart Cy3	brain_Cy5_heart_Cy3.txt	A-EXML-1	2	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	organism_part	organism_part	total_RNA	synthetic_DNA	Cy3	heart	Mus musculus	C57BL/6	male	frozen_sample	heart
+heart	heart	heart	heart Cy3	heart Cy3 vs kidney Cy5	heart_Cy3_kidney_Cy5.txt	A-EXML-1	3	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	organism_part	organism_part	total_RNA	synthetic_DNA	Cy3	heart	Mus musculus	C57BL/6	male	frozen_sample	heart
+heart	heart	heart	heart Cy5	brain Cy3 vs heart Cy5	brain_Cy3_heart_Cy5.txt	A-EXML-1	1	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	organism_part	organism_part	total_RNA	synthetic_DNA	Cy5	heart	Mus musculus	C57BL/6	male	frozen_sample	heart
+heart	heart	heart	heart Cy5	heart Cy5 vs kidney Cy3	heart_Cy5_kidney_Cy3.txt	A-EXML-1	4	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	organism_part	organism_part	total_RNA	synthetic_DNA	Cy5	heart	Mus musculus	C57BL/6	male	frozen_sample	heart
+kidney	kidney	kidney	kidney Cy3	heart Cy5 vs kidney Cy3	heart_Cy5_kidney_Cy3.txt	A-EXML-1	4	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	organism_part	organism_part	total_RNA	synthetic_DNA	Cy3	kidney	Mus musculus	C57BL/6	male	frozen_sample	kidney
+kidney	kidney	kidney	kidney Cy3	kidney Cy3 vs liver Cy5	kidney_Cy3_liver_Cy5.txt	A-EXML-1	5	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	organism_part	organism_part	total_RNA	synthetic_DNA	Cy3	kidney	Mus musculus	C57BL/6	male	frozen_sample	kidney
+kidney	kidney	kidney	kidney Cy5	heart Cy3 vs kidney Cy5	heart_Cy3_kidney_Cy5.txt	A-EXML-1	3	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	organism_part	organism_part	total_RNA	synthetic_DNA	Cy5	kidney	Mus musculus	C57BL/6	male	frozen_sample	kidney
+kidney	kidney	kidney	kidney Cy5	kidney Cy5 vs liver Cy3	kidney_Cy5_liver_Cy3.txt	A-EXML-1	6	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	organism_part	organism_part	total_RNA	synthetic_DNA	Cy5	kidney	Mus musculus	C57BL/6	male	frozen_sample	kidney
+liver	liver	liver	liver Cy3	kidney Cy5 vs liver Cy3	kidney_Cy5_liver_Cy3.txt	A-EXML-1	6	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	organism_part	organism_part	total_RNA	synthetic_DNA	Cy3	liver	Mus musculus	C57BL/6	male	frozen_sample	liver
+liver	liver	liver	liver Cy3	liver Cy3 vs spleen Cy5	liver_Cy3_spleen_Cy5.txt	A-EXML-1	7	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	organism_part	organism_part	total_RNA	synthetic_DNA	Cy3	liver	Mus musculus	C57BL/6	male	frozen_sample	liver
+liver	liver	liver	liver Cy5	kidney Cy3 vs liver Cy5	kidney_Cy3_liver_Cy5.txt	A-EXML-1	5	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	organism_part	organism_part	total_RNA	synthetic_DNA	Cy5	liver	Mus musculus	C57BL/6	male	frozen_sample	liver
+liver	liver	liver	liver Cy5	liver Cy5 vs spleen Cy3	liver_Cy5_spleen_Cy3.txt	A-EXML-1	8	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	organism_part	organism_part	total_RNA	synthetic_DNA	Cy5	liver	Mus musculus	C57BL/6	male	frozen_sample	liver
+spleen	spleen	spleen	spleen Cy3	liver Cy5 vs spleen Cy3	liver_Cy5_spleen_Cy3.txt	A-EXML-1	8	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	organism_part	organism_part	total_RNA	synthetic_DNA	Cy3	spleen	Mus musculus	C57BL/6	male	frozen_sample	spleen
+spleen	spleen	spleen	spleen Cy3	spleen Cy3 vs brain Cy5	spleen_Cy3_brain_Cy5.txt	A-EXML-1	9	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	organism_part	organism_part	total_RNA	synthetic_DNA	Cy3	spleen	Mus musculus	C57BL/6	male	frozen_sample	spleen
+spleen	spleen	spleen	spleen Cy5	spleen Cy5 vs brain Cy3	spleen_Cy5_brain_Cy3.txt	A-EXML-1	10	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	organism_part	organism_part	total_RNA	synthetic_DNA	Cy5	spleen	Mus musculus	C57BL/6	male	frozen_sample	spleen
+spleen	spleen	spleen	spleen Cy5	liver Cy3 vs spleen Cy5	liver_Cy3_spleen_Cy5.txt	A-EXML-1	7	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	organism_part	organism_part	total_RNA	synthetic_DNA	Cy5	spleen	Mus musculus	C57BL/6	male	frozen_sample	spleen
diff --git a/examples/magetab/affymetrix/ATH1-121501.cdf b/examples/magetab/affymetrix/ATH1-121501.cdf
new file mode 100644
index 0000000..a6d4b85
--- /dev/null
+++ b/examples/magetab/affymetrix/ATH1-121501.cdf
@@ -0,0 +1,192 @@
+[CDF]
+Version=GC3.0
+
+[Chip]
+Name=YG_S98
+Rows=534
+Cols=534
+NumberOfUnits=10
+MaxUnit=9375
+NumQCUnits=0
+ChipReference=
+
+[Unit1]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=1
+UnitType=3
+NumberBlocks=1
+
+[Unit1_Block1]
+Name=AFFX-MurIL2_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit2]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=2
+UnitType=3
+NumberBlocks=1
+
+[Unit2_Block1]
+Name=AFFX-MurIL10_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit3]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=3
+UnitType=3
+NumberBlocks=1
+
+[Unit3_Block1]
+Name=AFFX-MurIL4_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit4]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=4
+UnitType=3
+NumberBlocks=1
+
+[Unit4_Block1]
+Name=AFFX-MurFAS_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit5]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=5
+UnitType=3
+NumberBlocks=1
+
+[Unit5_Block1]
+Name=AFFX-BioB-5_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit6]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=6
+UnitType=3
+NumberBlocks=1
+
+[Unit6_Block1]
+Name=AFFX-BioB-M_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit7]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=7
+UnitType=3
+NumberBlocks=1
+
+[Unit7_Block1]
+Name=AFFX-BioB-3_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit8]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=8
+UnitType=3
+NumberBlocks=1
+
+[Unit8_Block1]
+Name=AFFX-BioC-5_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit9]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=9
+UnitType=3
+NumberBlocks=1
+
+[Unit9_Block1]
+Name=AFFX-BioC-3_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit10]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=10
+UnitType=3
+NumberBlocks=1
+
+[Unit10_Block1]
+Name=AFFX-BioDn-5_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
diff --git a/examples/magetab/affymetrix/Data1.CEL b/examples/magetab/affymetrix/Data1.CEL
new file mode 100644
index 0000000..368eedf
--- /dev/null
+++ b/examples/magetab/affymetrix/Data1.CEL
@@ -0,0 +1,49 @@
+[CEL]
+Version=3
+
+[HEADER]
+Cols=10
+Rows=1
+TotalX=10
+TotalY=1
+OffsetX=0
+OffsetY=0
+GridCornerUL=233 236
+GridCornerUR=4500 251
+GridCornerLR=4487 4516
+GridCornerLL=221 4501
+Axis-invertX=0
+AxisInvertY=0
+swapXY=0
+DatHeader=[0..46093]  test 1:CLS=4733 RWS=4733 XIN=3  YIN=3  VE=17        2.0 03/27/03 23:53:16       Test.1sq                  6
+Algorithm=Percentile
+AlgorithmParameters=Percentile:75;CellMargin:2;OutlierHigh:1.500;OutlierLow:1.004
+
+[INTENSITY]
+NumberCells=10
+CellHeader=X	Y	MEAN	STDV	NPIXELS
+  0	  0	158.0	28.9	 36
+  1	  0	8875.5	1390.6	 36
+  2	  0	160.0	25.3	 36
+  3	  0	8552.5	1027.3	 36
+  4	  0	78.8	15.6	 36
+  5	  0	138.8	25.5	 36
+  6	  0	8124.0	1124.9	 36
+  7	  0	146.3	22.9	 36
+  8	  0	7531.3	1038.5	 36
+  9	  0	132.3	21.7	 36
+
+[MASKS]
+NumberCells=0
+CellHeader=X	Y
+
+[OUTLIERS]
+NumberCells=3
+CellHeader=X	Y
+3	0
+6	0
+9	0
+
+[MODIFIED]
+NumberCells=0
+CellHeader=X	Y	ORIGMEAN
diff --git a/examples/magetab/affymetrix/Data1.CHP b/examples/magetab/affymetrix/Data1.CHP
new file mode 100644
index 0000000..7df0f68
Binary files /dev/null and b/examples/magetab/affymetrix/Data1.CHP differ
diff --git a/examples/magetab/affymetrix/Data1.EXP b/examples/magetab/affymetrix/Data1.EXP
new file mode 100644
index 0000000..8c93765
--- /dev/null
+++ b/examples/magetab/affymetrix/Data1.EXP
@@ -0,0 +1,49 @@
+Affymetrix GeneChip Experiment Information
+Version	1
+
+[Sample Info]
+Chip Type	HG-U133A
+Chip Lot	12345678
+Operator	Gandalf
+Sample Type	
+Description	
+Project	Test
+Comments	
+Solution Type	
+Solution Lot	
+
+[Fluidics]
+Protocol	EukGE-WS2v4
+Wash A1 Recovery Mixes	0
+Wash A1 Temperature (C)	25
+Number of Wash A1 Cycles	10
+Mixes per Wash A1 Cycle	2
+Wash B Recovery Mixes	0
+Wash B Temperature (C)	50
+Number of Wash B Cycles	4
+Mixes per Wash B Cycle	15
+Stain Temperature (C)	25
+First Stain Time (seconds)	600
+Wash A2 Recovery Mixes	0
+Wash A2 Temperature (C)	25
+Number of Wash A2 Cycles	10
+Mixes per Wash A2 Cycle	4
+Second Stain Time (seconds)	600
+Third Stain Time (seconds)	600
+Wash A3 Recovery Mixes	0
+Wash A3 Temperature (C)	30
+Number of Wash A3 Cycles	15
+Mixes per Wash A3 Cycle	4
+Holding Temperature (C)	25
+Station	2
+Module	1
+Hybridize Date	Sep 20 2003 01:10PM
+
+[Scanner]
+Pixel Size	3
+Filter	570
+Scan Temperature	30
+Scan Date	Sep 20 2003 01:25PM
+Scanner ID	87654321
+Number of Scans	2
+Scanner Type	HP
diff --git a/examples/magetab/affymetrix/Data2.CEL b/examples/magetab/affymetrix/Data2.CEL
new file mode 100644
index 0000000..a3f7d36
Binary files /dev/null and b/examples/magetab/affymetrix/Data2.CEL differ
diff --git a/examples/magetab/affymetrix/Data2.CHP b/examples/magetab/affymetrix/Data2.CHP
new file mode 100644
index 0000000..802a4cf
Binary files /dev/null and b/examples/magetab/affymetrix/Data2.CHP differ
diff --git a/examples/magetab/affymetrix/Data2.EXP b/examples/magetab/affymetrix/Data2.EXP
new file mode 100644
index 0000000..8c93765
--- /dev/null
+++ b/examples/magetab/affymetrix/Data2.EXP
@@ -0,0 +1,49 @@
+Affymetrix GeneChip Experiment Information
+Version	1
+
+[Sample Info]
+Chip Type	HG-U133A
+Chip Lot	12345678
+Operator	Gandalf
+Sample Type	
+Description	
+Project	Test
+Comments	
+Solution Type	
+Solution Lot	
+
+[Fluidics]
+Protocol	EukGE-WS2v4
+Wash A1 Recovery Mixes	0
+Wash A1 Temperature (C)	25
+Number of Wash A1 Cycles	10
+Mixes per Wash A1 Cycle	2
+Wash B Recovery Mixes	0
+Wash B Temperature (C)	50
+Number of Wash B Cycles	4
+Mixes per Wash B Cycle	15
+Stain Temperature (C)	25
+First Stain Time (seconds)	600
+Wash A2 Recovery Mixes	0
+Wash A2 Temperature (C)	25
+Number of Wash A2 Cycles	10
+Mixes per Wash A2 Cycle	4
+Second Stain Time (seconds)	600
+Third Stain Time (seconds)	600
+Wash A3 Recovery Mixes	0
+Wash A3 Temperature (C)	30
+Number of Wash A3 Cycles	15
+Mixes per Wash A3 Cycle	4
+Holding Temperature (C)	25
+Station	2
+Module	1
+Hybridize Date	Sep 20 2003 01:10PM
+
+[Scanner]
+Pixel Size	3
+Filter	570
+Scan Temperature	30
+Scan Date	Sep 20 2003 01:25PM
+Scanner ID	87654321
+Number of Scans	2
+Scanner Type	HP
diff --git a/examples/magetab/affymetrix/Data3.CEL b/examples/magetab/affymetrix/Data3.CEL
new file mode 100644
index 0000000..368eedf
--- /dev/null
+++ b/examples/magetab/affymetrix/Data3.CEL
@@ -0,0 +1,49 @@
+[CEL]
+Version=3
+
+[HEADER]
+Cols=10
+Rows=1
+TotalX=10
+TotalY=1
+OffsetX=0
+OffsetY=0
+GridCornerUL=233 236
+GridCornerUR=4500 251
+GridCornerLR=4487 4516
+GridCornerLL=221 4501
+Axis-invertX=0
+AxisInvertY=0
+swapXY=0
+DatHeader=[0..46093]  test 1:CLS=4733 RWS=4733 XIN=3  YIN=3  VE=17        2.0 03/27/03 23:53:16       Test.1sq                  6
+Algorithm=Percentile
+AlgorithmParameters=Percentile:75;CellMargin:2;OutlierHigh:1.500;OutlierLow:1.004
+
+[INTENSITY]
+NumberCells=10
+CellHeader=X	Y	MEAN	STDV	NPIXELS
+  0	  0	158.0	28.9	 36
+  1	  0	8875.5	1390.6	 36
+  2	  0	160.0	25.3	 36
+  3	  0	8552.5	1027.3	 36
+  4	  0	78.8	15.6	 36
+  5	  0	138.8	25.5	 36
+  6	  0	8124.0	1124.9	 36
+  7	  0	146.3	22.9	 36
+  8	  0	7531.3	1038.5	 36
+  9	  0	132.3	21.7	 36
+
+[MASKS]
+NumberCells=0
+CellHeader=X	Y
+
+[OUTLIERS]
+NumberCells=3
+CellHeader=X	Y
+3	0
+6	0
+9	0
+
+[MODIFIED]
+NumberCells=0
+CellHeader=X	Y	ORIGMEAN
diff --git a/examples/magetab/affymetrix/Data3.CHP b/examples/magetab/affymetrix/Data3.CHP
new file mode 100644
index 0000000..7df0f68
Binary files /dev/null and b/examples/magetab/affymetrix/Data3.CHP differ
diff --git a/examples/magetab/affymetrix/Data3.EXP b/examples/magetab/affymetrix/Data3.EXP
new file mode 100644
index 0000000..8c93765
--- /dev/null
+++ b/examples/magetab/affymetrix/Data3.EXP
@@ -0,0 +1,49 @@
+Affymetrix GeneChip Experiment Information
+Version	1
+
+[Sample Info]
+Chip Type	HG-U133A
+Chip Lot	12345678
+Operator	Gandalf
+Sample Type	
+Description	
+Project	Test
+Comments	
+Solution Type	
+Solution Lot	
+
+[Fluidics]
+Protocol	EukGE-WS2v4
+Wash A1 Recovery Mixes	0
+Wash A1 Temperature (C)	25
+Number of Wash A1 Cycles	10
+Mixes per Wash A1 Cycle	2
+Wash B Recovery Mixes	0
+Wash B Temperature (C)	50
+Number of Wash B Cycles	4
+Mixes per Wash B Cycle	15
+Stain Temperature (C)	25
+First Stain Time (seconds)	600
+Wash A2 Recovery Mixes	0
+Wash A2 Temperature (C)	25
+Number of Wash A2 Cycles	10
+Mixes per Wash A2 Cycle	4
+Second Stain Time (seconds)	600
+Third Stain Time (seconds)	600
+Wash A3 Recovery Mixes	0
+Wash A3 Temperature (C)	30
+Number of Wash A3 Cycles	15
+Mixes per Wash A3 Cycle	4
+Holding Temperature (C)	25
+Station	2
+Module	1
+Hybridize Date	Sep 20 2003 01:10PM
+
+[Scanner]
+Pixel Size	3
+Filter	570
+Scan Temperature	30
+Scan Date	Sep 20 2003 01:25PM
+Scanner ID	87654321
+Number of Scans	2
+Scanner Type	HP
diff --git a/examples/magetab/affymetrix/Data4.CEL b/examples/magetab/affymetrix/Data4.CEL
new file mode 100644
index 0000000..a3f7d36
Binary files /dev/null and b/examples/magetab/affymetrix/Data4.CEL differ
diff --git a/examples/magetab/affymetrix/Data4.CHP b/examples/magetab/affymetrix/Data4.CHP
new file mode 100644
index 0000000..802a4cf
Binary files /dev/null and b/examples/magetab/affymetrix/Data4.CHP differ
diff --git a/examples/magetab/affymetrix/Data4.EXP b/examples/magetab/affymetrix/Data4.EXP
new file mode 100644
index 0000000..8c93765
--- /dev/null
+++ b/examples/magetab/affymetrix/Data4.EXP
@@ -0,0 +1,49 @@
+Affymetrix GeneChip Experiment Information
+Version	1
+
+[Sample Info]
+Chip Type	HG-U133A
+Chip Lot	12345678
+Operator	Gandalf
+Sample Type	
+Description	
+Project	Test
+Comments	
+Solution Type	
+Solution Lot	
+
+[Fluidics]
+Protocol	EukGE-WS2v4
+Wash A1 Recovery Mixes	0
+Wash A1 Temperature (C)	25
+Number of Wash A1 Cycles	10
+Mixes per Wash A1 Cycle	2
+Wash B Recovery Mixes	0
+Wash B Temperature (C)	50
+Number of Wash B Cycles	4
+Mixes per Wash B Cycle	15
+Stain Temperature (C)	25
+First Stain Time (seconds)	600
+Wash A2 Recovery Mixes	0
+Wash A2 Temperature (C)	25
+Number of Wash A2 Cycles	10
+Mixes per Wash A2 Cycle	4
+Second Stain Time (seconds)	600
+Third Stain Time (seconds)	600
+Wash A3 Recovery Mixes	0
+Wash A3 Temperature (C)	30
+Number of Wash A3 Cycles	15
+Mixes per Wash A3 Cycle	4
+Holding Temperature (C)	25
+Station	2
+Module	1
+Hybridize Date	Sep 20 2003 01:10PM
+
+[Scanner]
+Pixel Size	3
+Filter	570
+Scan Temperature	30
+Scan Date	Sep 20 2003 01:25PM
+Scanner ID	87654321
+Number of Scans	2
+Scanner Type	HP
diff --git a/examples/magetab/affymetrix/YG_S98.cdf b/examples/magetab/affymetrix/YG_S98.cdf
new file mode 100644
index 0000000..a6d4b85
--- /dev/null
+++ b/examples/magetab/affymetrix/YG_S98.cdf
@@ -0,0 +1,192 @@
+[CDF]
+Version=GC3.0
+
+[Chip]
+Name=YG_S98
+Rows=534
+Cols=534
+NumberOfUnits=10
+MaxUnit=9375
+NumQCUnits=0
+ChipReference=
+
+[Unit1]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=1
+UnitType=3
+NumberBlocks=1
+
+[Unit1_Block1]
+Name=AFFX-MurIL2_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit2]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=2
+UnitType=3
+NumberBlocks=1
+
+[Unit2_Block1]
+Name=AFFX-MurIL10_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit3]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=3
+UnitType=3
+NumberBlocks=1
+
+[Unit3_Block1]
+Name=AFFX-MurIL4_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit4]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=4
+UnitType=3
+NumberBlocks=1
+
+[Unit4_Block1]
+Name=AFFX-MurFAS_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit5]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=5
+UnitType=3
+NumberBlocks=1
+
+[Unit5_Block1]
+Name=AFFX-BioB-5_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit6]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=6
+UnitType=3
+NumberBlocks=1
+
+[Unit6_Block1]
+Name=AFFX-BioB-M_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit7]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=7
+UnitType=3
+NumberBlocks=1
+
+[Unit7_Block1]
+Name=AFFX-BioB-3_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit8]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=8
+UnitType=3
+NumberBlocks=1
+
+[Unit8_Block1]
+Name=AFFX-BioC-5_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit9]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=9
+UnitType=3
+NumberBlocks=1
+
+[Unit9_Block1]
+Name=AFFX-BioC-3_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit10]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=10
+UnitType=3
+NumberBlocks=1
+
+[Unit10_Block1]
+Name=AFFX-BioDn-5_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
diff --git a/examples/magetab/affymetrix/affy_dm.txt b/examples/magetab/affymetrix/affy_dm.txt
new file mode 100644
index 0000000..d793838
--- /dev/null
+++ b/examples/magetab/affymetrix/affy_dm.txt
@@ -0,0 +1,14 @@
+Normalization REF	TK6 replicate 1	TK6 replicate 1	TK6 replicate 2	TK6 replicate 2	TK6MDR1 replicate 1	TK6MDR1 replicate 1	TK6MDR1 replicate 2	TK6MDR1 replicate 2
+Composite Element REF	CHPSignal	Call	CHPSignal	Call	CHPSignal	Call	CHPSignal	Call
+AFFX-BioB-5_at	172.05	A	141.47	P	103.52	A	634.15	P
+AFFX-BioB-M_at	17.43	A	40.69	P	50.78	P	242.73	P
+AFFX-BioB-3_at	1392.58	A	80.38	P	142.13	A	729.81	P
+AFFX-BioC-5_at	7.4	A	8.02	A	18.42	A	119.96	P
+AFFX-BioC-3_at	90.08	A	8.02	A	7.53	P	102.39	P
+AFFX-BioDn-5_at	13.24	A	4.45	A	6.56	A	93.37	P
+AFFX-BioDn-3_at	7.23	A	5.24	A	12.49	P	137.39	P
+AFFX-CreX-5_at	6.68	A	3.35	A	4.36	A	12.21	A
+AFFX-CreX-3_at	262.31	A	10.36	A	20.49	A	16.89	A
+AFFX-DapX-5_at	76.58	A	50.31	A	31.78	A	20.42	A
+AFFX-DapX-M_at	37.87	A	56.34	A	57.47	A	27.23	A
+AFFX-DapX-3_at	49.99	A	35.63	A	15.78	A	12.28	A
diff --git a/examples/magetab/affymetrix/affy_sdrf.txt b/examples/magetab/affymetrix/affy_sdrf.txt
new file mode 100644
index 0000000..df37e06
--- /dev/null
+++ b/examples/magetab/affymetrix/affy_sdrf.txt
@@ -0,0 +1,5 @@
+"Source Name"	"Material Type"	"Term Source REF"	"Characteristics [Genotype]"	"Characteristics[Organism]"	"Term Source REF"	"Characteristics[BioSourceType]"	"Term Source REF"	"Characteristics[CellType]"	"Term Source REF"	"Characteristics[CellLine]"	"Protocol REF"	"Parameter Value [media]"	"Sample Name"	"Protocol REF"	"Parameter Value [Extracted Product]"	"Parameter Value [Amplification]"	"Extract Name"	"Protocol REF"	"Term Source REF"	"Labeled Extract Name"	"Label"	"Protocol REF:Affymetri [...]
+"TK6 replicate 1"	"cell"	"MO"	"wild_type"	"Homo sapiens"	"ncbitax"	"fresh_sample"	"MO"	"B_lymphoblast"	"CTO"	"TK6"	"GROWTHPRTCL10653"	"RPMI 1640 medium supplemented with 10% horse serum"	"TK6 replicate 1"	"EXTPRTCL10654"	"total RNA"	"none"	"TK6 replicate 1"	"P-AFFY-2"	"ArrayExpress"	"TK6 replicate 1"	"biotin"		"H_TK6 replicate 1"	"A-AFFY-44"	"ArrayExpress"		"TK6 replicate 1"	"Data1.CEL"	"Data1.EXP"		"TK6 replicate 1"	"Data1.CHP"	"wild_type"	"combined data"	"affy_dm.txt"	"HG-U133A_Plus_2.CDF"
+"TK6 replicate 2"	"cell"	"MO"	"wild_type"	"Homo sapiens"	"ncbitax"	"fresh_sample"	"MO"	"B_lymphoblast"	"CTO"	"TK6"	"GROWTHPRTCL10653"	"RPMI 1640 medium supplemented with 10% horse serum"	"TK6 replicate 2"	"EXTPRTCL10654"	"total RNA"	"none"	"TK6 replicate 2"	"P-AFFY-2"	"ArrayExpress"	"TK6 replicate 2"	"biotin"		"H_TK6 replicate 2"	"A-AFFY-44"	"ArrayExpress"		"TK6 replicate 2"	"Data2.CEL"	"Data2.EXP"		"TK6 replicate 2"	"Data2.CHP"	"wild_type"	"combined data"	"affy_dm.txt"	"HG-U133A_Plus_2.CDF"
+"TK6MDR1 replicate 1"	"cell"	"MO"	"SF91m3 oncoretrovirus"	"Homo sapiens"	"ncbitax"	"fresh_sample"	"MO"	"B_lymphoblast"	"CTO"	"TK6"	"GROWTHPRTCL10653"	"RPMI 1640 medium supplemented with 10% horse serum"	"TK6MDR1 replicate 1"	"EXTPRTCL10654"	"total RNA"	"none"	"TK6MDR1 replicate 1"	"P-AFFY-2"	"ArrayExpress"	"TK6MDR1 replicate 1"	"biotin"	"Hybridization-EukGE-WS2v5"	"H_TK6MDR1 replicate 1"	"A-AFFY-44"	"ArrayExpress"	"Scan"	"TK6MDR1 replicate 1"	"Data3.CEL"	"Data3.EXP"	"TRANPRTCL10656"	"TK6 [...]
+"TK6MDR1 replicate 2"	"cell"	"MO"	"SF91m3 oncoretrovirus"	"Homo sapiens"	"ncbitax"	"fresh_sample"	"MO"	"B_lymphoblast"	"CTO"	"TK6"	"GROWTHPRTCL10653"	"RPMI 1640 medium supplemented with 10% horse serum"	"TK6MDR1 replicate 2"	"EXTPRTCL10654"	"total RNA"	"none"	"TK6MDR1 replicate 2"	"P-AFFY-2"	"ArrayExpress"	"TK6MDR1 replicate 2"	"biotin"	"Hybridization-EukGE-WS2v5"	"H_TK6MDR1 replicate 2"	"A-AFFY-44"	"ArrayExpress"	"Scan"	"TK6MDR1 replicate 2"	"Data4.CEL"	"Data4.EXP"	"TRANPRTCL10656"	"TK6 [...]
diff --git a/examples/magetab/affymetrix/affymetrix.idf b/examples/magetab/affymetrix/affymetrix.idf
new file mode 100644
index 0000000..586f50c
--- /dev/null
+++ b/examples/magetab/affymetrix/affymetrix.idf
@@ -0,0 +1,49 @@
+"Investigation Title"	"University of Heidelberg H sapiens TK6"							
+"Experimental Design"	"genetic_modification_design"							
+"Experimental Factor Name"	"EF1"	"EF2"						
+"Experimental Factor Type"	"genetic_modification"	"compound"						
+"Experimental Factor Term Source REF"	"MO"							
+								
+"Person Last Name"	"Maier"	"Fleckenstein"	"Li"	"Laufs"	"Zeller"	"Freuhauf"	"Herskind"	"Wenz"
+"Person First Name"	"Patrick"	"Katharina"	"Li"	"Stephanie"	"Jens"	"Stephan"	"Carsten"	"Frederik"
+"Person Mid Initials"					"W"			
+"Person Email"	"patrick.maier at radonk.ma.uni-heidelberg.de"							
+"Person Phone"	491334787822							
+"Person Fax"	123
+"Person Address"	"Theodor-Kutzer-Ufer 1-3"							
+"Person Affiliation"	"Department of Radiation Oncology, University of Heidelberg"	"Department of Radiation Oncology, University of Heidelberg"	"University of Heidelberg, ZMF"	"DKFZ, Heidelberg"	"DKFZ, Heidelberg"	"University of Heidelberg, Department of Int Med V"	"Department of Radiation Oncology, University of Heidelberg"	"Department of Radiation Oncology, University of Heidelberg"
+"Person Roles"	"submitter;investigator"	"investigator"	"investigator"	"investigator"	"investigator"	"investigator"	"investigator"	"investigator"
+"Person Roles Term Source REF"	"MO"							
+								
+"Quality Control Type"	"biological_replicate"							
+"Quality Control Term Source REF"	"MO"							
+"Replicate Type"	"biological_replicate"							
+"Replicate Term Source REF"	"MO"							
+"Normalization Type"	"whatever"							
+"Normalization Term Source REF"	"MO"							
+"Date of Experiment"	"2005-01-04"							
+"Public Release Date"	"2005-04-01"							
+								
+"PubMed ID"	16953664							
+"Publication DOI"	"doi: some number here"
+"Publication Author List"	"Me, him and her"
+"Publication Title"	"What I did on my holidays"
+"Publication Status"	"Universally panned"
+"Publication Status Term Source REF"	"MO"
+
+"Experiment Description"	"Gene expression of TK6 cells transduced with an oncoretrovirus expressing MDR1 (TK6MDR1) was compared to untransduced TK6 cells and to TK6 cell transduced with an oncoretrovirus expressing the Neomycin resistance gene (TK6neo). Two biological replicates of each were generated and the expression profiles were determined using Affymetrix Human Genome U133 Plus2.0 GeneChip microarrays. Comparisons between the sample groups allow the identification of genes with exp [...]
+								
+"Protocol Name"	"GROWTHPRTCL10653"	"EXTPRTCL10654"	"TRANPRTCL10656"					
+"Protocol Type"	"grow"	"nucleic_acid_extraction"	"bioassay_data_transformation"					
+"Protocol Description"	"TK6 cells were grown in suspension cultures in RPMI 1640 medium supplemented with 10% horse serum (Invitrogen, Karlsruhe, Germany). The cells were routinely maintained at 37 C and 5% CO2."	"Approximately 10^6 cells were lysed in RLT buffer (Qiagen).Total RNA was extracted from the cell lysate using an RNeasy kit (Qiagen)."	"Mixed Model Normalization with SAS Micro Array Solutions (version 1.3)."					
+"Protocol Parameters"	"media"	"Extracted Product; Amplification"						
+"Protocol Term Source REF"	"MO"							
+"Protocol Hardware"		"MyHardware"
+"Protocol Software"		"MySoftware"
+"Protocol Contact"		"MyContact"
+
+"SDRF File"	"affy_sdrf.txt"							
+								
+"Term Source Name"	"CTO"	"MO"	"ncbitax"	"ArrayExpress"				
+"Term Source File"	"http://obo.sourceforge.net/cgi-bin/detail.cgi?cell"	"http://mged.sourceforge.net/ontologies/MGEDontology.php"	"http://www.ncbi.nlm.nih.gov/Taxonomy/taxonomyhome.html/"	"http://www.ebi.ac.uk/arrayexpress/"				
+"Term Source Version"		"1.3.0.1"						
diff --git a/examples/magetab/illumina/GeneGroupedData.txt b/examples/magetab/illumina/GeneGroupedData.txt
new file mode 100644
index 0000000..8540fdb
--- /dev/null
+++ b/examples/magetab/illumina/GeneGroupedData.txt
@@ -0,0 +1,18 @@
+Illumina Inc. BeadStudio version 3.2.3.27166
+Normalization = cubic spline
+Array Content = Human_WG-6.xml.xml
+Error Model = none
+DateTime = 4/7/2006 6:38 PM
+Local Settings = en-US
+
+PROBE_ID	Tissue1.MIN_Signal	Tissue1.AVG_Signal	Tissue1.MAX_Signal	Tissue1.NARRAYS	Tissue1.ARRAY_STDEV	Tissue1.BEAD_STDEV	Tissue1.Avg_NBEADS	Tissue1.Detection	Tissue2.MIN_Signal	Tissue2.AVG_Signal	Tissue2.MAX_Signal	Tissue2.NARRAYS	Tissue2.ARRAY_STDEV	Tissue2.BEAD_STDEV	Tissue2.Avg_NBEADS	Tissue2.Detection	Mixture1.MIN_Signal	Mixture1.AVG_Signal	Mixture1.MAX_Signal	Mixture1.NARRAYS	Mixture1.ARRAY_STDEV	Mixture1.BEAD_STDEV	Mixture1.Avg_NBEADS	Mixture1.Detection	Mixture2.MIN_Signal	Mixture2 [...]
+360450	163.6	192.6	215.3	5	22.896	10.220	29	0.99934	-4.8	-3.0	-1.2	5	1.655	4.069	40	0.41134	8.0	21.5	26.0	5	7.722	6.041	30	0.96902	0.5	9.9	18.7	5	8.033	6.284	30	0.79103
+1690139	12100.1	12690.8	13437.3	5	651.837	334.077	33	1.00000	17.4	47.5	80.8	5	28.639	11.742	36	0.95781	3713.4	3793.4	3842.4	5	56.041	99.723	33	1.00000	58.8	68.1	73.7	5	5.810	8.072	25	0.99604
+5420594	233.8	242.7	258.0	5	9.281	15.148	25	1.00000	321.2	330.7	351.8	5	12.739	21.817	28	0.99934	155.4	177.5	217.1	5	27.665	10.574	23	1.00000	422.2	440.1	484.5	5	26.092	24.009	19	1.00000
+3060411	407.1	438.3	466.4	5	21.578	16.360	29	1.00000	554.5	608.8	662.0	5	44.600	31.865	31	1.00000	154.9	192.4	227.6	5	30.232	8.826	34	1.00000	726.4	779.1	860.0	5	53.943	29.842	31	1.00000
+450341	2470.8	2566.6	2643.4	5	63.710	60.129	36	1.00000	1517.7	1579.3	1625.1	5	41.988	49.936	39	1.00000	1453.4	1500.6	1575.1	5	46.195	48.919	36	1.00000	2464.9	2529.3	2625.8	5	61.878	70.328	30	1.00000
+5420324	7.2	18.0	31.5	5	10.067	7.129	22	0.91628	2.5	11.4	23.2	5	8.795	5.271	29	0.75346	4.6	12.8	18.5	5	5.522	6.571	23	0.91430	253.5	267.7	288.8	5	14.313	20.951	19	1.00000
+730162	-5.3	3.1	7.8	5	5.348	4.983	31	0.63810	-11.5	-4.0	9.3	5	9.722	4.934	29	0.39156	-7.5	-1.6	1.8	5	3.514	5.049	27	0.53790	-7.9	0.4	7.9	5	5.624	5.104	29	0.54318
+4200739	53.9	59.2	64.5	5	4.617	6.342	30	0.99011	504.8	522.2	549.4	5	21.540	25.760	38	1.00000	132.9	158.6	170.5	5	15.093	8.087	33	1.00000	507.0	534.0	565.3	5	25.053	20.384	33	1.00000
+1090156	296.5	335.7	394.3	5	36.982	15.301	26	1.00000	-1.4	7.9	19.4	5	8.141	5.717	35	0.67172	62.8	69.2	77.7	5	5.786	7.231	33	0.99604	8.5	13.6	21.3	5	5.383	6.077	25	0.87607
+7050341	1.3	6.1	16.9	5	6.255	5.619	33	0.72577	4.2	13.2	20.5	5	6.862	6.073	35	0.78708	-0.9	6.3	13.9	5	5.681	5.052	34	0.78840	4.3	15.8	23.6	5	7.492	5.439	35	0.90112
diff --git a/examples/magetab/illumina/ProbeData.txt b/examples/magetab/illumina/ProbeData.txt
new file mode 100644
index 0000000..08ef278
--- /dev/null
+++ b/examples/magetab/illumina/ProbeData.txt
@@ -0,0 +1,18 @@
+Illumina Inc. BeadStudio version 3.2.3.27166
+Normalization = cubic spline
+Array Content = Human_WG-6.xml.xml
+Error Model = none
+DateTime = 4/7/2006 6:38 PM
+Local Settings = en-US
+
+PROBE_ID	1323910012_A.MIN_Signal	1323910012_A.AVG_Signal	1323910012_A.MAX_Signal	1323910012_A.NARRAYS	1323910012_A.ARRAY_STDEV	1323910012_A.BEAD_STDEV	1323910012_A.Avg_NBEADS	1323910012_A.Detection	1323910012_B.MIN_Signal	1323910012_B.AVG_Signal	1323910012_B.MAX_Signal	1323910012_B.NARRAYS	1323910012_B.ARRAY_STDEV	1323910012_B.BEAD_STDEV	1323910012_B.Avg_NBEADS	1323910012_B.Detection	1323910012_C.MIN_Signal	1323910012_C.AVG_Signal	1323910012_C.MAX_Signal	1323910012_C.NARRAYS	1323910012_C [...]
+360450	157.5	157.5	157.5	1	NaN	5.821	26	0.99868	207.8	207.8	207.8	1	NaN	12.980	27	0.99934	170.4	170.4	170.4	1	NaN	9.440	35	0.99934	206.7	206.7	206.7	1	NaN	11.886	21	0.99934	186.8	186.8	186.8	1	NaN	7.323	38	1.00000	-2.5	-2.5	-2.5	1	NaN	4.488	43	0.43243	-0.9	-0.9	-0.9	1	NaN	4.168	32	0.46210	-4.0	-4.0	-4.0	1	NaN	4.108	34	0.37640	-4.4	-4.4	-4.4	1	NaN	3.755	52	0.39288	-0.6	-0.6	-0.6	1	NaN	3.780	37	0.46473	8.6	8.6	8.6	1	NaN	2.680	25	0.79565	26.3	26.3	26.3	1	NaN	4.459	36	0.98220	26.2	26.2	26.2	 [...]
+1690139	13230.6	13230.6	13230.6	1	NaN	325.577	29	1.00000	12232.2	12232.2	12232.2	1	NaN	357.805	33	1.00000	11960.1	11960.1	11960.1	1	NaN	225.731	34	1.00000	11912.3	11912.3	11912.3	1	NaN	322.376	35	1.00000	13139.1	13139.1	13139.1	1	NaN	389.941	33	1.00000	18.2	18.2	18.2	1	NaN	5.875	36	0.83125	23.4	23.4	23.4	1	NaN	5.492	36	0.85959	70.5	70.5	70.5	1	NaN	17.215	33	0.97627	42.9	42.9	42.9	1	NaN	7.071	38	0.95056	78.3	78.3	78.3	1	NaN	15.564	37	0.98220	3751.4	3751.4	3751.4	1	NaN	102.943	28	1.00000	3 [...]
+5420594	236.1	236.1	236.1	1	NaN	21.651	16	1.00000	232.5	232.5	232.5	1	NaN	9.196	26	1.00000	226.3	226.3	226.3	1	NaN	10.479	33	1.00000	250.7	250.7	250.7	1	NaN	18.603	19	1.00000	230.2	230.2	230.2	1	NaN	8.282	30	1.00000	342.6	342.6	342.6	1	NaN	29.768	17	0.99934	324.4	324.4	324.4	1	NaN	16.646	24	0.99934	312.9	312.9	312.9	1	NaN	19.538	32	0.99934	315.9	315.9	315.9	1	NaN	17.821	39	0.99736	312.0	312.0	312.0	1	NaN	19.734	28	0.99934	149.4	149.4	149.4	1	NaN	11.086	21	1.00000	209.8	209.8	209.8	1	NaN	 [...]
+3060411	419.2	419.2	419.2	1	NaN	14.573	25	1.00000	432.8	432.8	432.8	1	NaN	15.491	33	1.00000	396.3	396.3	396.3	1	NaN	13.728	29	1.00000	451.0	451.0	451.0	1	NaN	20.503	26	1.00000	428.8	428.8	428.8	1	NaN	14.104	31	1.00000	613.8	613.8	613.8	1	NaN	24.467	36	1.00000	553.9	553.9	553.9	1	NaN	33.553	29	1.00000	600.4	600.4	600.4	1	NaN	25.920	31	1.00000	540.8	540.8	540.8	1	NaN	30.170	27	1.00000	639.7	639.7	639.7	1	NaN	38.179	32	1.00000	205.9	205.9	205.9	1	NaN	12.051	28	1.00000	163.2	163.2	163.2	1	Na [...]
+450341	2542.7	2542.7	2542.7	1	NaN	57.743	27	1.00000	2419.4	2419.4	2419.4	1	NaN	43.567	46	1.00000	2501.8	2501.8	2501.8	1	NaN	62.710	28	1.00000	2509.3	2509.3	2509.3	1	NaN	62.196	48	1.00000	2584.8	2584.8	2584.8	1	NaN	65.386	32	1.00000	1549.3	1549.3	1549.3	1	NaN	51.455	41	1.00000	1596.1	1596.1	1596.1	1	NaN	49.101	37	1.00000	1525.7	1525.7	1525.7	1	NaN	58.618	36	1.00000	1482.3	1482.3	1482.3	1	NaN	44.746	33	1.00000	1576.9	1576.9	1576.9	1	NaN	37.999	47	1.00000	1414.2	1414.2	1414.2	1	NaN	54.880	2 [...]
+5420324	7.7	7.7	7.7	1	NaN	6.353	23	0.74621	31.6	31.6	31.6	1	NaN	7.836	20	0.96111	18.1	18.1	18.1	1	NaN	8.746	16	0.89057	24.6	24.6	24.6	1	NaN	6.910	28	0.93672	10.3	10.3	10.3	1	NaN	5.306	21	0.78840	17.5	17.5	17.5	1	NaN	5.599	39	0.81938	23.9	23.9	23.9	1	NaN	5.513	28	0.86486	11.6	11.6	11.6	1	NaN	4.647	31	0.72577	3.0	3.0	3.0	1	NaN	4.182	25	0.57086	4.0	4.0	4.0	1	NaN	6.170	21	0.57680	5.0	5.0	5.0	1	NaN	5.007	28	0.71457	16.6	16.6	16.6	1	NaN	7.569	20	0.94067	10.7	10.7	10.7	1	NaN	5.237	29	0.84113	16 [...]
+730162	-4.9	-4.9	-4.9	1	NaN	4.708	27	0.41266	8.4	8.4	8.4	1	NaN	5.663	41	0.74489	3.6	3.6	3.6	1	NaN	4.223	29	0.62162	8.2	8.2	8.2	1	NaN	6.101	28	0.74160	2.5	2.5	2.5	1	NaN	3.856	30	0.61833	-11.4	-11.4	-11.4	1	NaN	5.531	24	0.21885	-11.3	-11.3	-11.3	1	NaN	4.882	24	0.21028	9.9	9.9	9.9	1	NaN	4.898	35	0.69875	-9.5	-9.5	-9.5	1	NaN	4.025	33	0.25313	4.0	4.0	4.0	1	NaN	5.206	30	0.57680	0.5	0.5	0.5	1	NaN	5.029	25	0.58075	-0.8	-0.8	-0.8	1	NaN	5.831	30	0.54581	2.3	2.3	2.3	1	NaN	4.046	29	0.60976	-7.1	-7.1 [...]
+4200739	54.0	54.0	54.0	1	NaN	5.792	31	0.98879	62.9	62.9	62.9	1	NaN	5.879	33	0.99209	58.5	58.5	58.5	1	NaN	6.012	28	0.98879	61.3	61.3	61.3	1	NaN	7.225	29	0.98879	52.8	52.8	52.8	1	NaN	6.105	27	0.98813	494.0	494.0	494.0	1	NaN	25.935	44	1.00000	536.6	536.6	536.6	1	NaN	23.539	41	1.00000	490.2	490.2	490.2	1	NaN	30.217	30	1.00000	490.8	490.8	490.8	1	NaN	23.458	42	1.00000	529.4	529.4	529.4	1	NaN	21.175	35	1.00000	161.5	161.5	161.5	1	NaN	9.237	26	1.00000	158.0	158.0	158.0	1	NaN	6.707	39	1.00000	15 [...]
+1090156	384.2	384.2	384.2	1	NaN	11.330	18	1.00000	306.2	306.2	306.2	1	NaN	9.133	13	1.00000	334.4	334.4	334.4	1	NaN	15.539	33	1.00000	289.1	289.1	289.1	1	NaN	16.736	39	1.00000	320.0	320.0	320.0	1	NaN	19.402	25	1.00000	20.2	20.2	20.2	1	NaN	8.095	31	0.85234	4.0	4.0	4.0	1	NaN	5.022	34	0.56625	5.8	5.8	5.8	1	NaN	5.910	33	0.62030	13.3	13.3	13.3	1	NaN	4.399	41	0.76467	-0.8	-0.8	-0.8	1	NaN	4.292	38	0.45880	74.8	74.8	74.8	1	NaN	5.652	27	0.99802	63.4	63.4	63.4	1	NaN	12.553	28	0.99604	69.2	69.2	69.2 [...]
+7050341	1.9	1.9	1.9	1	NaN	4.078	31	0.60580	3.6	3.6	3.6	1	NaN	7.133	23	0.63612	6.6	6.6	6.6	1	NaN	5.750	40	0.70864	3.7	3.7	3.7	1	NaN	4.862	33	0.63942	17.6	17.6	17.6	1	NaN	5.800	39	0.89453	21.3	21.3	21.3	1	NaN	6.834	30	0.85695	16.3	16.3	16.3	1	NaN	6.212	36	0.79169	8.6	8.6	8.6	1	NaN	5.130	42	0.67963	18.6	18.6	18.6	1	NaN	6.243	40	0.83652	4.6	4.6	4.6	1	NaN	5.814	27	0.59130	14.7	14.7	14.7	1	NaN	5.893	28	0.89519	-0.3	-0.3	-0.3	1	NaN	4.062	35	0.55900	10.2	10.2	10.2	1	NaN	5.905	37	0.83191	3.9	3.9	 [...]
diff --git a/examples/magetab/illumina/illumina.idf b/examples/magetab/illumina/illumina.idf
new file mode 100755
index 0000000..5f619b2
--- /dev/null
+++ b/examples/magetab/illumina/illumina.idf
@@ -0,0 +1,28 @@
+Investigation Title	MAQC Illumina data set (excerpt)
+Experimental Design	array_platform_variation_design
+Experimental Factor Name	SAMPLE
+Experimental Factor Type	Sample
+Experimental Factor Type Term Source REF	MGED Ontology
+	
+Person Last Name	Shi
+Person First Name	Leming
+Person Roles	submitter
+Person Roles Term Source REF	MGED Ontology
+	
+Quality Control Types	technical_replicate
+Quality Control Types Term Source REF	MGED Ontology
+Public Release Date	08/09/2006
+Comment[ArrayExpressSubmissionDate]	02/08/2006
+Publication Author List	Leming Shi, Laura H. Reid, Richard Shippy, Janet A. Warrington, William Slikker Jr.
+Publication Title	The MicroArray Quality Control (MAQC) project demonstrates concordant results between gene expression technology platforms
+Experiment Description	The MicroArray Quality Control (MAQC) project was initiated to address these concerns, as well as other performance and analysis issues. We demonstrate the consistency of results within a platform across test sites as well as the high level of cross-platform concordance in terms of genes identified as differentially expressed. The MAQC study provides a rich resource that will help build consensus on the use of microarrays in research, clinical and regulatory settin [...]
+	
+Protocol Name	P-EXML-1
+Protocol Type	transformation
+Protocol Description	Data was processed using Illumina BeadStudio version 1.5.0.34 software using background subtraction and cubic spline normalization
+Protocol Term Source REF	MGED Ontology
+	
+SDRF File	illumina_sdrf.txt
+	
+Term Source Name	MGED Ontology
+Term Source File	http://mged.sourceforge.net/ontologies/MGEDontology.php
diff --git a/examples/magetab/illumina/illumina_sdrf.txt b/examples/magetab/illumina/illumina_sdrf.txt
new file mode 100755
index 0000000..477a3c6
--- /dev/null
+++ b/examples/magetab/illumina/illumina_sdrf.txt
@@ -0,0 +1,21 @@
+Source Name	Material Type	Term Source REF	Labeled Extract Name	Material Type	Term Source REF	Label	Term Source REF	Hybridization Name	Array Design REF	Scan Name	Normalization Name	Protocol REF	Array Data File	Derived Array Data File	Factor Value[SAMPLE](Sample)
+A	whole_organism	MGED Ontology	1A.biotin	synthetic_DNA	MGED Ontology	biotin	MGED Ontology	1323910012_A	A-MEXP-524	1323910012_A	Tissue1	P-EXML-1	ProbeData.txt	GeneGroupedData.txt	1
+A	whole_organism	MGED Ontology	1A.biotin	synthetic_DNA	MGED Ontology	biotin	MGED Ontology	1323910012_B	A-MEXP-524	1323910012_B	Tissue1	P-EXML-1	ProbeData.txt	GeneGroupedData.txt	1
+A	whole_organism	MGED Ontology	1A.biotin	synthetic_DNA	MGED Ontology	biotin	MGED Ontology	1323910012_C	A-MEXP-524	1323910012_C	Tissue1	P-EXML-1	ProbeData.txt	GeneGroupedData.txt	1
+A	whole_organism	MGED Ontology	1A.biotin	synthetic_DNA	MGED Ontology	biotin	MGED Ontology	1323910012_D	A-MEXP-524	1323910012_D	Tissue1	P-EXML-1	ProbeData.txt	GeneGroupedData.txt	1
+A	whole_organism	MGED Ontology	1A.biotin	synthetic_DNA	MGED Ontology	biotin	MGED Ontology	1323910012_E	A-MEXP-524	1323910012_E	Tissue1	P-EXML-1	ProbeData.txt	GeneGroupedData.txt	1
+B	whole_organism	MGED Ontology	1B.biotin	synthetic_DNA	MGED Ontology	biotin	MGED Ontology	1323901020_A	A-MEXP-524	1323901020_A	Tissue2	P-EXML-1	ProbeData.txt	GeneGroupedData.txt	2
+B	whole_organism	MGED Ontology	1B.biotin	synthetic_DNA	MGED Ontology	biotin	MGED Ontology	1323901020_B	A-MEXP-524	1323901020_B	Tissue2	P-EXML-1	ProbeData.txt	GeneGroupedData.txt	2
+B	whole_organism	MGED Ontology	1B.biotin	synthetic_DNA	MGED Ontology	biotin	MGED Ontology	1323901020_C	A-MEXP-524	1323901020_C	Tissue2	P-EXML-1	ProbeData.txt	GeneGroupedData.txt	2
+B	whole_organism	MGED Ontology	1B.biotin	synthetic_DNA	MGED Ontology	biotin	MGED Ontology	1323901020_E	A-MEXP-524	1323901020_E	Tissue2	P-EXML-1	ProbeData.txt	GeneGroupedData.txt	2
+B	whole_organism	MGED Ontology	1B.biotin	synthetic_DNA	MGED Ontology	biotin	MGED Ontology	1323901020_F	A-MEXP-524	1323901020_F	Tissue2	P-EXML-1	ProbeData.txt	GeneGroupedData.txt	2
+C	whole_organism	MGED Ontology	1C.biotin	synthetic_DNA	MGED Ontology	biotin	MGED Ontology	1323901024_A	A-MEXP-524	1323901024_A	Mixture1	P-EXML-1	ProbeData.txt	GeneGroupedData.txt	3
+C	whole_organism	MGED Ontology	1C.biotin	synthetic_DNA	MGED Ontology	biotin	MGED Ontology	1323901024_B	A-MEXP-524	1323901024_B	Mixture1	P-EXML-1	ProbeData.txt	GeneGroupedData.txt	3
+C	whole_organism	MGED Ontology	1C.biotin	synthetic_DNA	MGED Ontology	biotin	MGED Ontology	1323901024_D	A-MEXP-524	1323901024_D	Mixture1	P-EXML-1	ProbeData.txt	GeneGroupedData.txt	3
+C	whole_organism	MGED Ontology	1C.biotin	synthetic_DNA	MGED Ontology	biotin	MGED Ontology	1323901024_E	A-MEXP-524	1323901024_E	Mixture1	P-EXML-1	ProbeData.txt	GeneGroupedData.txt	3
+C	whole_organism	MGED Ontology	1C.biotin	synthetic_DNA	MGED Ontology	biotin	MGED Ontology	1323901024_F	A-MEXP-524	1323901024_F	Mixture1	P-EXML-1	ProbeData.txt	GeneGroupedData.txt	3
+D	whole_organism	MGED Ontology	1D.biotin	synthetic_DNA	MGED Ontology	biotin	MGED Ontology	1323928007_A	A-MEXP-524	1323928007_A	Mixture2	P-EXML-1	ProbeData.txt	GeneGroupedData.txt	4
+D	whole_organism	MGED Ontology	1D.biotin	synthetic_DNA	MGED Ontology	biotin	MGED Ontology	1323928007_C	A-MEXP-524	1323928007_C	Mixture2	P-EXML-1	ProbeData.txt	GeneGroupedData.txt	4
+D	whole_organism	MGED Ontology	1D.biotin	synthetic_DNA	MGED Ontology	biotin	MGED Ontology	1323928007_D	A-MEXP-524	1323928007_D	Mixture2	P-EXML-1	ProbeData.txt	GeneGroupedData.txt	4
+D	whole_organism	MGED Ontology	1D.biotin	synthetic_DNA	MGED Ontology	biotin	MGED Ontology	1323928007_E	A-MEXP-524	1323928007_E	Mixture2	P-EXML-1	ProbeData.txt	GeneGroupedData.txt	4
+D	whole_organism	MGED Ontology	1D.biotin	synthetic_DNA	MGED Ontology	biotin	MGED Ontology	1323928007_F	A-MEXP-524	1323928007_F	Mixture2	P-EXML-1	ProbeData.txt	GeneGroupedData.txt	4
diff --git a/examples/magetab/real/E-MEXP-880.idf b/examples/magetab/real/E-MEXP-880.idf
new file mode 100644
index 0000000..08c68f8
--- /dev/null
+++ b/examples/magetab/real/E-MEXP-880.idf
@@ -0,0 +1,27 @@
+"Investigation Title"	BF-H.sapiens-HOXAmethylation
+"Experimental Design"	disease_state_design
+"Experimental Factor Name"	DISEASESTATE	CELLLINE	MATERIALTYPE
+"Experimental Factor Type"	disease_state	cell_line	material_type
+
+"Person Last Name"	Novak
+"Person First Name"	Petr
+"Person Mid Initial"	
+"Person Email"	futscherlab at azcc.arizona.edu
+"Person Address"	"1515 N Campbell Ave, Tucson, AZ, 85722, USA"
+"Person Affiliation"	"University of Arizona"
+"Person Roles"	submitter
+
+"Public Release Date"	2006-09-30
+
+Comment[ArrayExpressSubmissionDate]	2006-10-04
+
+"Publication Title"	"Epigenetic Inactivation of the HOXA Gene Cluster in Breast Cancer"
+"Experiment Description"	"To evaluate DNA methylation profile associated with breast cancer in 125 kB region of HOXA cluster we analyzed DNA from normal cancerous breast specimens and cell lines. Analysis was performed on the tiling array covering entire HOXA region with 500bp resolution. Immunoprecipitation with anti-methylcytosine antibody was for target preparation."
+
+"Protocol Name"	BF_CGI_hyb	HMEC	CCL-231-BT549	BREAST_TISSUE	MeCIP_Cy3	BF_CGI_Scan	BF-MeCIP	MeCIP_Cy5	BF-HOXA_transf
+"Protocol Type"	hybridization	specified_biomaterial_action	specified_biomaterial_action	specified_biomaterial_action	labeling	image_acquisition	nucleic_acid_extraction	labeling	bioassay_data_transformation
+"Protocol Description"	"Cy3 and Cy5 labeled extracts were mixed and purified using Qiagen PCR purification kit according manufacturer's instruction.Hybridization Protocol:Slide Preparation Protocol:1. Remove all slides that you need from N2 storage and label accordingly. On the back side of the slide, scratch an outline of the corners of the array using a diamond-tipped scribe. The front is defined as the side the array is printed on and should have a label on it.Note: Only label with a  [...]
+"Protocol Parameters"									
+"Protocol Software"									
+
+"SDRF File"	E-MEXP-880_sdrf.txt
diff --git a/examples/magetab/real/E-MEXP-880_sdrf.txt b/examples/magetab/real/E-MEXP-880_sdrf.txt
new file mode 100644
index 0000000..4c8bd12
--- /dev/null
+++ b/examples/magetab/real/E-MEXP-880_sdrf.txt
@@ -0,0 +1,41 @@
+"Source Name"	"Material Type"	"Characteristics [BioSourceProvider]"	"Characteristics [BioSourceType]"	"Characteristics [CellLine]"	"Characteristics [ClinicalHistory]"	"Characteristics [DevelopmentalStage]"	"Characteristics [DiseaseState]"	"Characteristics [Individual]"	"Characteristics [OrganismPart]"	"Characteristics [Organism]"	"Characteristics [Sex]"	"Characteristics [TargetedCellType]"	"Protocol REF"	"Sample Name"	"Material Type"	"Protocol REF"	"Sample Name"	"Material Type"	"Extract  [...]
+1139T	whole_organism	"University Medical Center, Tucson,AZ"	frozen_sample	1139	"Lymph node metastasis"	adult	"Lymph node metastasis"	1139	"mammary gland"	"Homo sapiens"	female	Epithelial	BREAST_TISSUE	1139T	organism_part	BF-MeCIP	E_1139T	genomic_DNA	"L_1139T IP"	molecular_mixture	MeCIP_Cy3	"L_1139T Cy3"	synthetic_DNA	Cy3	BF_CGI_hyb	H_1139T_1139T_1	A-MEXP-568	BF_CGI_Scan	1139T	1139T.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	1139	"Lymph node metastasis"	molecular_mixture
+1139T	whole_organism	"University Medical Center, Tucson,AZ"	frozen_sample	1139	"Lymph node metastasis"	adult	"Lymph node metastasis"	1139	"mammary gland"	"Homo sapiens"	female	Epithelial	BREAST_TISSUE	1139T	organism_part	BF-MeCIP	E_1139T	genomic_DNA	"L_1139T sonicated input DNA"	genomic_DNA	MeCIP_Cy5	"L_1139T_1 Cy5"	synthetic_DNA	Cy5	BF_CGI_hyb	H_1139T_1139T_1	A-MEXP-568	BF_CGI_Scan	1139T	1139T.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	1139	"Lymph node metastasis" [...]
+120T	whole_organism	"University Medical Center, Tucson,AZ"	frozen_sample	120	"Infiltrating ductal carcinoma"	adult	"Infiltrating ductal carcinoma"	120	"mammary gland"	"Homo sapiens"	female	Epithelial	BREAST_TISSUE	120T	organism_part	BF-MeCIP	E_120T	genomic_DNA	"L_120T IP"	molecular_mixture	MeCIP_Cy3	"L_120T Cy3"	synthetic_DNA	Cy3	BF_CGI_hyb	H_120T_120T_1	A-MEXP-568	BF_CGI_Scan	120T	120T.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	120	"Infiltrating ductal carcinoma"	 [...]
+120T	whole_organism	"University Medical Center, Tucson,AZ"	frozen_sample	120	"Infiltrating ductal carcinoma"	adult	"Infiltrating ductal carcinoma"	120	"mammary gland"	"Homo sapiens"	female	Epithelial	BREAST_TISSUE	120T	organism_part	BF-MeCIP	E_120T	genomic_DNA	"L_120 sonicated input DNA"	genomic_DNA	MeCIP_Cy5	"L_120T_1 Cy5"	synthetic_DNA	Cy5	BF_CGI_hyb	H_120T_120T_1	A-MEXP-568	BF_CGI_Scan	120T	120T.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	120	"Infiltrating ductal [...]
+2845T	whole_organism	"University Medical Center, Tucson,AZ"	frozen_sample	2845	"Infiltrating ductal carcinoma"	adult	"Infiltrating ductal carcinoma"	2845	"mammary gland"	"Homo sapiens"	female	Epithelial	BREAST_TISSUE	2845T	organism_part	BF-MeCIP	E_2845T	genomic_DNA	"L_2845T IP"	molecular_mixture	MeCIP_Cy3	"L_2845T Cy3"	synthetic_DNA	Cy3	BF_CGI_hyb	H_2845T_2845T_1	A-MEXP-568	BF_CGI_Scan	2845T	2845T.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	2845	"Infiltrating ductal [...]
+2845T	whole_organism	"University Medical Center, Tucson,AZ"	frozen_sample	2845	"Infiltrating ductal carcinoma"	adult	"Infiltrating ductal carcinoma"	2845	"mammary gland"	"Homo sapiens"	female	Epithelial	BREAST_TISSUE	2845T	organism_part	BF-MeCIP	E_2845T	genomic_DNA	"L_2845T sonicated input DNA"	genomic_DNA	MeCIP_Cy5	"L_2845T_1 Cy5"	synthetic_DNA	Cy5	BF_CGI_hyb	H_2845T_2845T_1	A-MEXP-568	BF_CGI_Scan	2845T	2845T.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	2845	"Infilt [...]
+5256T	whole_organism	"University Medical Center, Tucson,AZ"	frozen_sample	5256	"Infiltrating ductal carcinoma"	adult	"Infiltrating ductal carcinoma"	5256	"mammary gland"	"Homo sapiens"	female	Epithelial	BREAST_TISSUE	5256T	organism_part	BF-MeCIP	E_5256	genomic_DNA	"L_5256T IP"	molecular_mixture	MeCIP_Cy3	"L_5256T Cy3"	synthetic_DNA	Cy3	BF_CGI_hyb	H_5256T_5256T_1	A-MEXP-568	BF_CGI_Scan	5256T	5256T.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	5256	"Infiltrating ductal  [...]
+5256T	whole_organism	"University Medical Center, Tucson,AZ"	frozen_sample	5256	"Infiltrating ductal carcinoma"	adult	"Infiltrating ductal carcinoma"	5256	"mammary gland"	"Homo sapiens"	female	Epithelial	BREAST_TISSUE	5256T	organism_part	BF-MeCIP	E_5256	genomic_DNA	"L_5256T sonicated input DNA"	genomic_DNA	MeCIP_Cy5	"L_5256T_1 Cy5"	synthetic_DNA	Cy5	BF_CGI_hyb	H_5256T_5256T_1	A-MEXP-568	BF_CGI_Scan	5256T	5256T.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	5256	"Infiltr [...]
+5343N	whole_organism	"University Medical Center, Tucson,AZ"	frozen_sample	5343		adult	Normal	5343	"mammary gland"	"Homo sapiens"	female	Epithelial	BREAST_TISSUE	5343N	organism_part	BF-MeCIP	E_5343N	genomic_DNA	"L_5343N IP"	molecular_mixture	MeCIP_Cy3	"L_5343N Cy3"	synthetic_DNA	Cy3	BF_CGI_hyb	H_5343N_5343N_1	A-MEXP-568	BF_CGI_Scan	5343N	5343N.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	5343	normal	molecular_mixture
+5343N	whole_organism	"University Medical Center, Tucson,AZ"	frozen_sample	5343		adult	Normal	5343	"mammary gland"	"Homo sapiens"	female	Epithelial	BREAST_TISSUE	5343N	organism_part	BF-MeCIP	E_5343N	genomic_DNA	"L_5343N sonicated input DNA"	genomic_DNA	MeCIP_Cy5	"L_5343N_1 Cy5"	synthetic_DNA	Cy5	BF_CGI_hyb	H_5343N_5343N_1	A-MEXP-568	BF_CGI_Scan	5343N	5343N.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	5343	normal	genomic_DNA
+5799T	whole_organism	"University Medical Center, Tucson,AZ"	frozen_sample	5799	"Infiltrating ductal carcinoma"	adult	"Infiltrating ductal carcinoma"	5799	"mammary gland"	"Homo sapiens"	female	Epithelial	BREAST_TISSUE	5799T	organism_part	BF-MeCIP	E_5799T	genomic_DNA	"L_5799T IP"	molecular_mixture	MeCIP_Cy3	"L_5799T Cy3"	synthetic_DNA	Cy3	BF_CGI_hyb	H_5799T_5799T_1	A-MEXP-568	BF_CGI_Scan	5799T	5799T.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	5799	"Infiltrating ductal [...]
+5799T	whole_organism	"University Medical Center, Tucson,AZ"	frozen_sample	5799	"Infiltrating ductal carcinoma"	adult	"Infiltrating ductal carcinoma"	5799	"mammary gland"	"Homo sapiens"	female	Epithelial	BREAST_TISSUE	5799T	organism_part	BF-MeCIP	E_5799T	genomic_DNA	"L_5799T sonicated input DNA"	genomic_DNA	MeCIP_Cy5	"L_5799T_1 Cy5"	synthetic_DNA	Cy5	BF_CGI_hyb	H_5799T_5799T_1	A-MEXP-568	BF_CGI_Scan	5799T	5799T.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	5799	"Infilt [...]
+6245T	whole_organism	"University Medical Center, Tucson,AZ"	frozen_sample	6245	"Infiltrating ductal carcinoma"	adult	"Infiltrating ductal carcinoma"	6245	"mammary gland"	"Homo sapiens"	female	Epithelial	BREAST_TISSUE	6245T	organism_part	BF-MeCIP	E_6245T	genomic_DNA	"L_6245T IP"	molecular_mixture	MeCIP_Cy3	"L_6245T Cy3"	synthetic_DNA	Cy3	BF_CGI_hyb	H_6245T_6245T_1	A-MEXP-568	BF_CGI_Scan	6245T	6245T.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	6245	"Infiltrating ductal [...]
+6245T	whole_organism	"University Medical Center, Tucson,AZ"	frozen_sample	6245	"Infiltrating ductal carcinoma"	adult	"Infiltrating ductal carcinoma"	6245	"mammary gland"	"Homo sapiens"	female	Epithelial	BREAST_TISSUE	6245T	organism_part	BF-MeCIP	E_6245T	genomic_DNA	"L_6245T sonicated input DNA"	genomic_DNA	MeCIP_Cy5	"L_6245T_1 Cy5"	synthetic_DNA	Cy5	BF_CGI_hyb	H_6245T_6245T_1	A-MEXP-568	BF_CGI_Scan	6245T	6245T.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	6245	"Infilt [...]
+6333N	whole_organism	"University Medical Center, Tucson,AZ"	frozen_sample	6333		adult	Normal	6333	"mammary gland"	"Homo sapiens"	female	Epithelial	BREAST_TISSUE	6333N	organism_part	BF-MeCIP	E_6333N	genomic_DNA	"L_6333N IP"	molecular_mixture	MeCIP_Cy3	"L_6333N Cy3"	synthetic_DNA	Cy3	BF_CGI_hyb	H_6333N_6333N_1	A-MEXP-568	BF_CGI_Scan	6333N	6333N.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	6333	normal	molecular_mixture
+6333N	whole_organism	"University Medical Center, Tucson,AZ"	frozen_sample	6333		adult	Normal	6333	"mammary gland"	"Homo sapiens"	female	Epithelial	BREAST_TISSUE	6333N	organism_part	BF-MeCIP	E_6333N	genomic_DNA	"L_6333N sonicated input DNA"	genomic_DNA	MeCIP_Cy5	"L_6333N_1 Cy5"	synthetic_DNA	Cy5	BF_CGI_hyb	H_6333N_6333N_1	A-MEXP-568	BF_CGI_Scan	6333N	6333N.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	6333	normal	genomic_DNA
+7732N	whole_organism	"University Medical Center, Tucson,AZ"	frozen_sample	7732		adult	Normal	7732	"mammary gland"	"Homo sapiens"	female	Epithelial	BREAST_TISSUE	7732N	organism_part	BF-MeCIP	E_7732N	genomic_DNA	"L_7732N IP"	molecular_mixture	MeCIP_Cy3	"L_7732N Cy3"	synthetic_DNA	Cy3	BF_CGI_hyb	H_7732N_7732N_1	A-MEXP-568	BF_CGI_Scan	7732N	7732N.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	7732	normal	molecular_mixture
+7732N	whole_organism	"University Medical Center, Tucson,AZ"	frozen_sample	7732		adult	Normal	7732	"mammary gland"	"Homo sapiens"	female	Epithelial	BREAST_TISSUE	7732N	organism_part	BF-MeCIP	E_7732N	genomic_DNA	"L_7732N sonicated input DNA"	genomic_DNA	MeCIP_Cy5	"L_7732N_1 Cy5"	synthetic_DNA	Cy5	BF_CGI_hyb	H_7732N_7732N_1	A-MEXP-568	BF_CGI_Scan	7732N	7732N.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	7732	normal	genomic_DNA
+7732T	whole_organism	"University Medical Center, Tucson,AZ"	frozen_sample	7732	"Infiltrating ductal carcinoma"	adult	"Infiltrating ductal carcinoma"	7732	"mammary gland"	"Homo sapiens"	female	Epithelial	BREAST_TISSUE	7732T	organism_part	BF-MeCIP	E_7732T	genomic_DNA	"L_7732T IP"	molecular_mixture	MeCIP_Cy3	"L_7732T Cy3"	synthetic_DNA	Cy3	BF_CGI_hyb	H_7732T_7732T_1	A-MEXP-568	BF_CGI_Scan	7732T	7732T.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	7732	"Infiltrating ductal [...]
+7732T	whole_organism	"University Medical Center, Tucson,AZ"	frozen_sample	7732	"Infiltrating ductal carcinoma"	adult	"Infiltrating ductal carcinoma"	7732	"mammary gland"	"Homo sapiens"	female	Epithelial	BREAST_TISSUE	7732T	organism_part	BF-MeCIP	E_7732T	genomic_DNA	"L_7732T sonicated input DNA"	genomic_DNA	MeCIP_Cy5	"L_7732T_1 Cy5"	synthetic_DNA	Cy5	BF_CGI_hyb	H_7732T_7732T_1	A-MEXP-568	BF_CGI_Scan	7732T	7732T.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	7732	"Infilt [...]
+7768T	whole_organism	"University Medical Center, Tucson,AZ"	frozen_sample	7768	"Infiltrating ductal carcinoma"	adult	"Infiltrating ductal carcinoma"	7768	"mammary gland"	"Homo sapiens"	female	Epithelial	BREAST_TISSUE	7768T	organism_part	BF-MeCIP	E_7768T	genomic_DNA	"L_7768T IP"	molecular_mixture	MeCIP_Cy3	"L_7768T Cy3"	synthetic_DNA	Cy3	BF_CGI_hyb	H_7768T_7768T_1	A-MEXP-568	BF_CGI_Scan	7768T	7768T.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	7768	"Infiltrating ductal [...]
+7768T	whole_organism	"University Medical Center, Tucson,AZ"	frozen_sample	7768	"Infiltrating ductal carcinoma"	adult	"Infiltrating ductal carcinoma"	7768	"mammary gland"	"Homo sapiens"	female	Epithelial	BREAST_TISSUE	7768T	organism_part	BF-MeCIP	E_7768T	genomic_DNA	"L_7768T sonicated input DNA"	genomic_DNA	MeCIP_Cy5	"L_7768T_1 Cy5"	synthetic_DNA	Cy5	BF_CGI_hyb	H_7768T_7768T_1	A-MEXP-568	BF_CGI_Scan	7768T	7768T.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	7768	"Infilt [...]
+7788T	whole_organism	"University Medical Center, Tucson,AZ"	frozen_sample	7788	"Infiltrating lobular carcinoma"	adult	"Infiltrating lobular carcinoma"	7788	"mammary gland"	"Homo sapiens"	female	Epithelial	BREAST_TISSUE	7788T	organism_part	BF-MeCIP	E_7788T	genomic_DNA	"L_7788T IP"	molecular_mixture	MeCIP_Cy3	"L_7788T Cy3"	synthetic_DNA	Cy3	BF_CGI_hyb	H_7788T_7788T_1	A-MEXP-568	BF_CGI_Scan	7788T	7788T.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	7788	"Infiltrating lobu [...]
+7788T	whole_organism	"University Medical Center, Tucson,AZ"	frozen_sample	7788	"Infiltrating lobular carcinoma"	adult	"Infiltrating lobular carcinoma"	7788	"mammary gland"	"Homo sapiens"	female	Epithelial	BREAST_TISSUE	7788T	organism_part	BF-MeCIP	E_7788T	genomic_DNA	"L_7788T sonicated input DNA"	genomic_DNA	MeCIP_Cy5	"L_7788T_1 Cy5"	synthetic_DNA	Cy5	BF_CGI_hyb	H_7788T_7788T_1	A-MEXP-568	BF_CGI_Scan	7788T	7788T.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	7788	"Infi [...]
+9663T	whole_organism	"University Medical Center, Tucson,AZ"	frozen_sample	9663	"Lymph node metastasis"	adult	"Lymph node metastasis"	9663	"mammary gland"	"Homo sapiens"	female	Epithelial	BREAST_TISSUE	9663T	organism_part	BF-MeCIP	E_9663T	genomic_DNA	"L_9663T IP"	molecular_mixture	MeCIP_Cy3	"L_9663T Cy3"	synthetic_DNA	Cy3	BF_CGI_hyb	H_9663T_9663T_1	A-MEXP-568	BF_CGI_Scan	9663T	9663T.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	9663	"Lymph node metastasis"	molecular_mixture
+9663T	whole_organism	"University Medical Center, Tucson,AZ"	frozen_sample	9663	"Lymph node metastasis"	adult	"Lymph node metastasis"	9663	"mammary gland"	"Homo sapiens"	female	Epithelial	BREAST_TISSUE	9663T	organism_part	BF-MeCIP	E_9663T	genomic_DNA	"L_9663T sonicated input DNA"	genomic_DNA	MeCIP_Cy5	"L_9663T_1 Cy5"	synthetic_DNA	Cy5	BF_CGI_hyb	H_9663T_9663T_1	A-MEXP-568	BF_CGI_Scan	9663T	9663T.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	9663	"Lymph node metastasis" [...]
+"BT 549-1"	whole_organism	"American Type Culture Collection (Rockville, MD)"		"BT 549"	"Breast Cancer Cell line"	adult	"Breast Cancer Cell line"	"BT 549"	"mammary gland"	"Homo sapiens"	female	Epithelial	CCL-231-BT549	"BT 549-1"	organism_part	BF-MeCIP	"E_BT 549-1"	genomic_DNA	"L_BT 549-1 IP"	molecular_mixture	MeCIP_Cy3	"L_BT 549-1 Cy3"	synthetic_DNA	Cy3	BF_CGI_hyb	"H_BT 549-1_BT 549-1_1"	A-MEXP-568	BF_CGI_Scan	BT549-1	BT549-1.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.t [...]
+"BT 549-1"	whole_organism	"American Type Culture Collection (Rockville, MD)"		"BT 549"	"Breast Cancer Cell line"	adult	"Breast Cancer Cell line"	"BT 549"	"mammary gland"	"Homo sapiens"	female	Epithelial	CCL-231-BT549	"BT 549-1"	organism_part	BF-MeCIP	"E_BT 549-1"	genomic_DNA	"L_BT 549-1 sonicated input DNA"	genomic_DNA	MeCIP_Cy5	"L_BT 549-1_1 Cy5"	synthetic_DNA	Cy5	BF_CGI_hyb	"H_BT 549-1_BT 549-1_1"	A-MEXP-568	BF_CGI_Scan	BT549-1	BT549-1.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combine [...]
+"BT 549-2"	whole_organism	"American Type Culture Collection (Rockville, MD)"		"BT 549"	"Breast Cancer Cell line"	adult	"Breast Cancer Cell line"	"BT 549"	"mammary gland"	"Homo sapiens"	female	Epithelial	CCL-231-BT549	"BT 549-2"	organism_part	BF-MeCIP	"E_BT 549-2"	genomic_DNA	"L_BT 549-2 IP"	molecular_mixture	MeCIP_Cy3	"L_BT 549-2 Cy3"	synthetic_DNA	Cy3	BF_CGI_hyb	"H_BT 549-2_BT 549-2_1"	A-MEXP-568	BF_CGI_Scan	BT549-2	BT549-2.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.t [...]
+"BT 549-2"	whole_organism	"American Type Culture Collection (Rockville, MD)"		"BT 549"	"Breast Cancer Cell line"	adult	"Breast Cancer Cell line"	"BT 549"	"mammary gland"	"Homo sapiens"	female	Epithelial	CCL-231-BT549	"BT 549-2"	organism_part	BF-MeCIP	"E_BT 549-2"	genomic_DNA	"L_BT 549-2 sonicated input DNA"	genomic_DNA	MeCIP_Cy5	"L_BT 549-2_1 Cy5"	synthetic_DNA	Cy5	BF_CGI_hyb	"H_BT 549-2_BT 549-2_1"	A-MEXP-568	BF_CGI_Scan	BT549-2	BT549-2.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combine [...]
+HMEC1	whole_organism	"Clonetics, San Diego, CA"		HMEC	"Human Mammary Epithelial Cell"	adult	Normal	HMEC	"mammary gland"	"Homo sapiens"	female	Epithelial	HMEC	HMEC1	organism_part	BF-MeCIP	E_HMEC1	genomic_DNA	"L_HMEC1 IP"	molecular_mixture	MeCIP_Cy3	"L_HMEC1 Cy3"	synthetic_DNA	Cy3	BF_CGI_hyb	H_HMEC1_HMEC1_1	A-MEXP-568	BF_CGI_Scan	HMEC1	HMEC1.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	HMEC	normal	molecular_mixture
+HMEC1	whole_organism	"Clonetics, San Diego, CA"		HMEC	"Human Mammary Epithelial Cell"	adult	Normal	HMEC	"mammary gland"	"Homo sapiens"	female	Epithelial	HMEC	HMEC1	organism_part	BF-MeCIP	E_HMEC1	genomic_DNA	"L_HMEC1 sonicated input DNA"	genomic_DNA	MeCIP_Cy5	"L_HMEC1_1 Cy5"	synthetic_DNA	Cy5	BF_CGI_hyb	H_HMEC1_HMEC1_1	A-MEXP-568	BF_CGI_Scan	HMEC1	HMEC1.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	HMEC	normal	genomic_DNA
+HMEC2	whole_organism	"Clonetics, San Diego, CA"		HMEC	"Human Mammary Epithelial Cell"	adult	Normal	HMEC	"mammary gland"	"Homo sapiens"	female	Epithelial	HMEC	HMEC2	organism_part	BF-MeCIP	E_HMEC2	genomic_DNA	"L_HMEC2 IP"	molecular_mixture	MeCIP_Cy3	"L_HMEC2 Cy3"	synthetic_DNA	Cy3	BF_CGI_hyb	H_HMEC2_HMEC2_1	A-MEXP-568	BF_CGI_Scan	HMEC2	HMEC2.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	HMEC	normal	molecular_mixture
+HMEC2	whole_organism	"Clonetics, San Diego, CA"		HMEC	"Human Mammary Epithelial Cell"	adult	Normal	HMEC	"mammary gland"	"Homo sapiens"	female	Epithelial	HMEC	HMEC2	organism_part	BF-MeCIP	E_HMEC2	genomic_DNA	"L_HMEC2 sonicated input DNA"	genomic_DNA	MeCIP_Cy5	"L_HMEC2_1 Cy5"	synthetic_DNA	Cy5	BF_CGI_hyb	H_HMEC2_HMEC2_1	A-MEXP-568	BF_CGI_Scan	HMEC2	HMEC2.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	HMEC	normal	genomic_DNA
+HMEC3	whole_organism	"Clonetics, San Diego, CA"		HMEC	"Human Mammary Epithelial Cell"	adult	Normal	HMEC	"mammary gland"	"Homo sapiens"	female	Epithelial	HMEC	HMEC3	organism_part	BF-MeCIP	E_HMEC3	genomic_DNA	"L_HMEC3 IP"	molecular_mixture	MeCIP_Cy3	"L_HMEC3 Cy3"	synthetic_DNA	Cy3	BF_CGI_hyb	H_HMEC3_HMEC3_1	A-MEXP-568	BF_CGI_Scan	HMEC3	HMEC3.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	HMEC	normal	molecular_mixture
+HMEC3	whole_organism	"Clonetics, San Diego, CA"		HMEC	"Human Mammary Epithelial Cell"	adult	Normal	HMEC	"mammary gland"	"Homo sapiens"	female	Epithelial	HMEC	HMEC3	organism_part	BF-MeCIP	E_HMEC3	genomic_DNA	"L_HMEC3 sonicated input DNA"	genomic_DNA	MeCIP_Cy5	"L_HMEC3_1 Cy5"	synthetic_DNA	Cy5	BF_CGI_hyb	H_HMEC3_HMEC3_1	A-MEXP-568	BF_CGI_Scan	HMEC3	HMEC3.gpr	BF-HOXA_transf	combined_data_HOXA.txt		combined_data_HOXA.txt	HMEC	normal	genomic_DNA
+MB-MDA-231-1	whole_organism	"American Type Culture Collection (Rockville, MD)"		MB-MDA-231	"Breast Cancer Cell line"	adult	"Breast Cancer Cell line"	MB-MDA-231	"mammary gland"	"Homo sapiens"	female	Epithelial	CCL-231-BT549	MB-MDA-231-1	organism_part	BF-MeCIP	E_MB-MDA-231-1	genomic_DNA	"L_MB-MDA-231-1 IP"	molecular_mixture	MeCIP_Cy3	"L_MB-MDA-231-1 Cy3"	synthetic_DNA	Cy3	BF_CGI_hyb	H_MB-MDA-231-1_MB-MDA-231-1_1	A-MEXP-568	BF_CGI_Scan	MDA-MB-231-1	MDA-MB-231-1.gpr	BF-HOXA_transf	combined_d [...]
+MB-MDA-231-1	whole_organism	"American Type Culture Collection (Rockville, MD)"		MB-MDA-231	"Breast Cancer Cell line"	adult	"Breast Cancer Cell line"	MB-MDA-231	"mammary gland"	"Homo sapiens"	female	Epithelial	CCL-231-BT549	MB-MDA-231-1	organism_part	BF-MeCIP	E_MB-MDA-231-1	genomic_DNA	"L_MB-MDA-231-1 sonicated input DNA"	genomic_DNA	MeCIP_Cy5	"L_MB-MDA-231-1_1 Cy5"	synthetic_DNA	Cy5	BF_CGI_hyb	H_MB-MDA-231-1_MB-MDA-231-1_1	A-MEXP-568	BF_CGI_Scan	MDA-MB-231-1	MDA-MB-231-1.gpr	BF-HOXA_tran [...]
+MB-MDA-231-2	whole_organism	"American Type Culture Collection (Rockville, MD)"		MB-MDA-231	"Breast Cancer Cell line"	adult	"Breast Cancer Cell line"	MB-MDA-231	"mammary gland"	"Homo sapiens"	female	Epithelial	CCL-231-BT549	MB-MDA-231-2	organism_part	BF-MeCIP	E_MB-MDA-231-2	genomic_DNA	"L_MB-MDA-231-2 IP"	molecular_mixture	MeCIP_Cy3	"L_MB-MDA-231-2 Cy3"	synthetic_DNA	Cy3	BF_CGI_hyb	H_MB-MDA-231-2_MB-MDA-231-2_1	A-MEXP-568	BF_CGI_Scan	MDA-MB-231-2	MDA-MB-231-2.gpr	BF-HOXA_transf	combined_d [...]
+MB-MDA-231-2	whole_organism	"American Type Culture Collection (Rockville, MD)"		MB-MDA-231	"Breast Cancer Cell line"	adult	"Breast Cancer Cell line"	MB-MDA-231	"mammary gland"	"Homo sapiens"	female	Epithelial	CCL-231-BT549	MB-MDA-231-2	organism_part	BF-MeCIP	E_MB-MDA-231-2	genomic_DNA	"L_MB-MDA-231-2 sonicated input DNA"	genomic_DNA	MeCIP_Cy5	"L_MB-MDA-231-2_1 Cy5"	synthetic_DNA	Cy5	BF_CGI_hyb	H_MB-MDA-231-2_MB-MDA-231-2_1	A-MEXP-568	BF_CGI_Scan	MDA-MB-231-2	MDA-MB-231-2.gpr	BF-HOXA_tran [...]
diff --git a/examples/magetab/real/E-TABM-102.idf b/examples/magetab/real/E-TABM-102.idf
new file mode 100644
index 0000000..7163233
--- /dev/null
+++ b/examples/magetab/real/E-TABM-102.idf
@@ -0,0 +1,33 @@
+"Investigation Title"	"Transcription profiling of wild-type and ATF3 -/-mouse bone marrow macrophages stimulated with lipopolysaccharide after 1, 2, 4, 8 and 24 hours"			
+"Experimental Design"	compound_treatment_design	genetic_modification_design	time_series_design	
+"Experimental Design Term Source REF"	"MGED Ontology"	"MGED Ontology"	"MGED Ontology"	
+"Experimental Factor Name"	COMPOUND	DOSE	TIME	GENOTYPE
+"Experimental Factor Type"	compound	dose	time	genotype
+				
+"Person Last Name"	Marzolf			
+"Person First Name"	Bruz			
+"Person Mid Initial"				
+"Person Affiliation"	"Institute for Systems Biology"			
+"Person Roles"	submitter			
+"Person Roles Term Source REF"	"MGED Ontology"			
+				
+"Quality Control Types"	biological_replicate			
+"Quality Control Term Source REF"	"MGED Ontology"			
+"Public Release Date"	"2006-05-30"			
+				
+Comment[ArrayExpressSubmissionDate]	"2006-05-30"			
+				
+"Publication Author List"	"Mark Gilchrist; Vesteinn Thorsson; Bin Li; Alistair G. Rust; Martin Korb; Kathleen Kennedy; Tsonwin Hai; Hamid Bolouri and Alan Aderem"			
+"Publication Title"	"Systems biology approaches identify ATF3 as a negative regulator of Toll-like receptor 4"			
+"Experiment Description"	"Time course data from lipopolysaccharide-stimulated wild-type and ATF3 -/- mouse bone marrow macrophages were collected to investigate the transcriptional network in Toll-like recepter 4-activated macrophages."			
+				
+"Protocol Name"	P-TABM-gilchrist1	P-TABM-gilchrist2		
+"Protocol Type"	grow	specified_biomaterial_action		
+"Protocol Description"	"Flush femurs from C57BL/6 mice (Jackson Laboratories) with complete RPMI (RPMI 1640 supplemented with 10% FCS, 2mM L-glutamine, 100 IU/mL penicillin and 100ug/mL streptomycin, all from Cellgro, Mediatech, except the FCS which is from from Hyclone). Plate bone marrow cells on non-tissue culture treated plastic in complete RPMI supplemented with recombinant human M-CSF (rhM-CSF) at 50 ng/mL (Chiron). On day 3, wash the cells two times with complete RPMI and then gro [...]
+"Protocol Term Source REF"	"MGED Ontology"	"MGED Ontology"		
+				
+"SDRF File"	E-TABM-102_sdrf.txt			
+				
+"Term Source Name"	"MGED Ontology"	"ArrayExpress"	"Cell Type Ontology"	
+"Term Source File"	http://mged.sourceforge.net/ontologies/MGEDontology.php	http://www.ebi.ac.uk/arrayexpress/	http://obo.cvs.sourceforge.net/*checkout*/obo/obo/ontology/anatomy/cell_type/cell.obo	
+"Term Source Version"	"1.3.1.1"			
diff --git a/examples/magetab/real/E-TABM-102_sdrf.txt b/examples/magetab/real/E-TABM-102_sdrf.txt
new file mode 100644
index 0000000..1eb41a7
--- /dev/null
+++ b/examples/magetab/real/E-TABM-102_sdrf.txt
@@ -0,0 +1,31 @@
+"Source Name"	"Material Type"	"Term Source REF"	"Characteristics [CellType]"	"Term Source REF"	"Term Accession Number"	"Characteristics [GeneticModification]"	"Term Source REF"	"Characteristics [Genotype]"	"Term Source REF"	"Characteristics [Organism]"	"Characteristics [Sex]"	"Term Source REF"	"Characteristics [StrainOrLine]"	"Protocol REF"	"Protocol REF"	"Sample Name"	"Material Type"	"Term Source REF"	"Protocol REF"	"Term Source REF"	"Extract Name"	"Material Type"	"Term Source REF"	"Pro [...]
+"LPS 0 hr Mouse 1"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235			wild_type	"MGED Ontology"	"Mus musculus"	mixed_sex	"MGED Ontology"	C57BL/6	P-TABM-gilchrist1	P-TABM-gilchrist2	"LPS 0 hr Mouse 1"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"LPS 0 hr Mouse 1"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"LPS 0 hr Mouse 1 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20040603_03_LPS1-0	A-AFFY-45	ArrayExpress	20040603_03_LPS1-0	20040603_03_LPS1-0 [...]
+"LPS 0 hr Mouse 2"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235			wild_type	"MGED Ontology"	"Mus musculus"	mixed_sex	"MGED Ontology"	C57BL/6	P-TABM-gilchrist1	P-TABM-gilchrist2	"LPS 0 hr Mouse 2"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"LPS 0 hr Mouse 2"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"LPS 0 hr Mouse 2 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20040621_01_LPS2-0	A-AFFY-45	ArrayExpress	20040621_01_LPS2-0	20040621_01_LPS2-0 [...]
+"LPS 0 hr Mouse 3"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235			wild_type	"MGED Ontology"	"Mus musculus"	mixed_sex	"MGED Ontology"	C57BL/6	P-TABM-gilchrist1	P-TABM-gilchrist2	"LPS 0 hr Mouse 3"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"LPS 0 hr Mouse 3"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"LPS 0 hr Mouse 3 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20040622_01_LPS3-0	A-AFFY-45	ArrayExpress	20040622_01_LPS3-0	20040622_01_LPS3-0 [...]
+"LPS 1 hr Mouse 1"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235			wild_type	"MGED Ontology"	"Mus musculus"	mixed_sex	"MGED Ontology"	C57BL/6	P-TABM-gilchrist1	P-TABM-gilchrist2	"LPS 1 hr Mouse 1"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"LPS 1 hr Mouse 1"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"LPS 1 hr Mouse 1 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20040609_03_LPS1-60	A-AFFY-45	ArrayExpress	20040609_03_LPS1-60	20040609_03_LPS1 [...]
+"LPS 1 hr Mouse 2"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235			wild_type	"MGED Ontology"	"Mus musculus"	mixed_sex	"MGED Ontology"	C57BL/6	P-TABM-gilchrist1	P-TABM-gilchrist2	"LPS 1 hr Mouse 2"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"LPS 1 hr Mouse 2"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"LPS 1 hr Mouse 2 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20040621_04_LPS2-60	A-AFFY-45	ArrayExpress	20040621_04_LPS2-60	20040621_04_LPS2 [...]
+"LPS 1 hr Mouse 3"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235			wild_type	"MGED Ontology"	"Mus musculus"	mixed_sex	"MGED Ontology"	C57BL/6	P-TABM-gilchrist1	P-TABM-gilchrist2	"LPS 1 hr Mouse 3"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"LPS 1 hr Mouse 3"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"LPS 1 hr Mouse 3 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20040622_04_LPS3-60	A-AFFY-45	ArrayExpress	20040622_04_LPS3-60	20040622_04_LPS3 [...]
+"LPS 2 hr Mouse 1"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235			wild_type	"MGED Ontology"	"Mus musculus"	mixed_sex	"MGED Ontology"	C57BL/6	P-TABM-gilchrist1	P-TABM-gilchrist2	"LPS 2 hr Mouse 1"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"LPS 2 hr Mouse 1"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"LPS 2 hr Mouse 1 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20040603_04_LPS1-120	A-AFFY-45	ArrayExpress	20040603_04_LPS1-120	20040603_04_LP [...]
+"LPS 2 hr Mouse 2"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235			wild_type	"MGED Ontology"	"Mus musculus"	mixed_sex	"MGED Ontology"	C57BL/6	P-TABM-gilchrist1	P-TABM-gilchrist2	"LPS 2 hr Mouse 2"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"LPS 2 hr Mouse 2"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"LPS 2 hr Mouse 2 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20040621_06_LPS2-120	A-AFFY-45	ArrayExpress	20040621_06_LPS2-120	20040621_06_LP [...]
+"LPS 2 hr Mouse 3"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235			wild_type	"MGED Ontology"	"Mus musculus"	mixed_sex	"MGED Ontology"	C57BL/6	P-TABM-gilchrist1	P-TABM-gilchrist2	"LPS 2 hr Mouse 3"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"LPS 2 hr Mouse 3"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"LPS 2 hr Mouse 3 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20040622_06_LPS3-120	A-AFFY-45	ArrayExpress	20040622_06_LPS3-120	20040622_06_LP [...]
+"LPS 4 hr Mouse 1"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235			wild_type	"MGED Ontology"	"Mus musculus"	mixed_sex	"MGED Ontology"	C57BL/6	P-TABM-gilchrist1	P-TABM-gilchrist2	"LPS 4 hr Mouse 1"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"LPS 4 hr Mouse 1"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"LPS 4 hr Mouse 1 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20050310_03_C57_A-240	A-AFFY-45	ArrayExpress	20050310_03_C57_A-240	20050310_03_ [...]
+"LPS 4 hr Mouse 2"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235			wild_type	"MGED Ontology"	"Mus musculus"	mixed_sex	"MGED Ontology"	C57BL/6	P-TABM-gilchrist1	P-TABM-gilchrist2	"LPS 4 hr Mouse 2"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"LPS 4 hr Mouse 2"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"LPS 4 hr Mouse 2 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20050505_09_C57_LPS_B_240	A-AFFY-45	ArrayExpress	20050505_09_C57_LPS_B_240	2005 [...]
+"LPS 4 hr Mouse 3"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235			wild_type	"MGED Ontology"	"Mus musculus"	mixed_sex	"MGED Ontology"	C57BL/6	P-TABM-gilchrist1	P-TABM-gilchrist2	"LPS 4 hr Mouse 3"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"LPS 4 hr Mouse 3"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"LPS 4 hr Mouse 3 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20050623_03_C57_LPS_C_240min	A-AFFY-45	ArrayExpress	20050623_03_C57_LPS_C_240mi [...]
+"LPS 8 hr Mouse 1"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235			wild_type	"MGED Ontology"	"Mus musculus"	mixed_sex	"MGED Ontology"	C57BL/6	P-TABM-gilchrist1	P-TABM-gilchrist2	"LPS 8 hr Mouse 1"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"LPS 8 hr Mouse 1"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"LPS 8 hr Mouse 1 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20050622_09_LPS_A1_8_hr	A-AFFY-45	ArrayExpress	20050622_09_LPS_A1_8_hr	20050622 [...]
+"LPS 8 hr Mouse 2"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235			wild_type	"MGED Ontology"	"Mus musculus"	mixed_sex	"MGED Ontology"	C57BL/6	P-TABM-gilchrist1	P-TABM-gilchrist2	"LPS 8 hr Mouse 2"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"LPS 8 hr Mouse 2"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"LPS 8 hr Mouse 2 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20050622_11_LPS_B1_8_hr	A-AFFY-45	ArrayExpress	20050622_11_LPS_B1_8_hr	20050622 [...]
+"LPS 8 hr Mouse 3"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235			wild_type	"MGED Ontology"	"Mus musculus"	mixed_sex	"MGED Ontology"	C57BL/6	P-TABM-gilchrist1	P-TABM-gilchrist2	"LPS 8 hr Mouse 3"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"LPS 8 hr Mouse 3"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"LPS 8 hr Mouse 3 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20050623_01_LPS_C1_8hr	A-AFFY-45	ArrayExpress	20050623_01_LPS_C1_8hr	20050623_0 [...]
+"LPS 24 hr Mouse 1"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235			wild_type	"MGED Ontology"	"Mus musculus"	mixed_sex	"MGED Ontology"	C57BL/6	P-TABM-gilchrist1	P-TABM-gilchrist2	"LPS 24 hr Mouse 1"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"LPS 24 hr Mouse 1"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"LPS 24 hr Mouse 1 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20050622_10_LPS_A1_24_hr	A-AFFY-45	ArrayExpress	20050622_10_LPS_A1_24_hr	20 [...]
+"LPS 24 hr Mouse 2"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235			wild_type	"MGED Ontology"	"Mus musculus"	mixed_sex	"MGED Ontology"	C57BL/6	P-TABM-gilchrist1	P-TABM-gilchrist2	"LPS 24 hr Mouse 2"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"LPS 24 hr Mouse 2"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"LPS 24 hr Mouse 2 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20050622_12_LPS_B1_24_hr	A-AFFY-45	ArrayExpress	20050622_12_LPS_B1_24_hr	20 [...]
+"LPS 24 hr Mouse 3"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235			wild_type	"MGED Ontology"	"Mus musculus"	mixed_sex	"MGED Ontology"	C57BL/6	P-TABM-gilchrist1	P-TABM-gilchrist2	"LPS 24 hr Mouse 3"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"LPS 24 hr Mouse 3"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"LPS 24 hr Mouse 3 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20050623_04_LPS_C2_24hr	A-AFFY-45	ArrayExpress	20050623_04_LPS_C2_24hr	2005 [...]
+"ATF3 -/- LPS 0 hr Mouse 1"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235	gene_knock_out	"MGED Ontology"	"ATF3 -/-"		"Mus musculus"	mixed_sex	"MGED Ontology"	"ATF3 -/- C57BL/6"	P-TABM-gilchrist1	P-TABM-gilchrist2	"ATF3 -/- LPS 0 hr Mouse 1"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"ATF3 -/- LPS 0 hr Mouse 1"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"ATF3 -/- LPS 0 hr Mouse 1 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20050310_04_ATF3- [...]
+"ATF3 -/- LPS 0 hr Mouse 2"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235	gene_knock_out	"MGED Ontology"	"ATF3 -/-"		"Mus musculus"	mixed_sex	"MGED Ontology"	"ATF3 -/- C57BL/6"	P-TABM-gilchrist1	P-TABM-gilchrist2	"ATF3 -/- LPS 0 hr Mouse 2"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"ATF3 -/- LPS 0 hr Mouse 2"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"ATF3 -/- LPS 0 hr Mouse 2 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20050331_08_ATF3- [...]
+"ATF3 -/- LPS 0 hr Mouse 3"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235	gene_knock_out	"MGED Ontology"	"ATF3 -/-"		"Mus musculus"	mixed_sex	"MGED Ontology"	"ATF3 -/- C57BL/6"	P-TABM-gilchrist1	P-TABM-gilchrist2	"ATF3 -/- LPS 0 hr Mouse 3"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"ATF3 -/- LPS 0 hr Mouse 3"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"ATF3 -/- LPS 0 hr Mouse 3 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20050504_09_ATF3_ [...]
+"ATF3 -/- LPS 1 hr Mouse 1"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235	gene_knock_out	"MGED Ontology"	"ATF3 -/-"		"Mus musculus"	mixed_sex	"MGED Ontology"	"ATF3 -/- C57BL/6"	P-TABM-gilchrist1	P-TABM-gilchrist2	"ATF3 -/- LPS 1 hr Mouse 1"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"ATF3 -/- LPS 1 hr Mouse 1"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"ATF3 -/- LPS 1 hr Mouse 1 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20050310_05_ATF3- [...]
+"ATF3 -/- LPS 1 hr Mouse 2"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235	gene_knock_out	"MGED Ontology"	"ATF3 -/-"		"Mus musculus"	mixed_sex	"MGED Ontology"	"ATF3 -/- C57BL/6"	P-TABM-gilchrist1	P-TABM-gilchrist2	"ATF3 -/- LPS 1 hr Mouse 2"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"ATF3 -/- LPS 1 hr Mouse 2"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"ATF3 -/- LPS 1 hr Mouse 2 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20050411_04_ATF3- [...]
+"ATF3 -/- LPS 1 hr Mouse 3"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235	gene_knock_out	"MGED Ontology"	"ATF3 -/-"		"Mus musculus"	mixed_sex	"MGED Ontology"	"ATF3 -/- C57BL/6"	P-TABM-gilchrist1	P-TABM-gilchrist2	"ATF3 -/- LPS 1 hr Mouse 3"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"ATF3 -/- LPS 1 hr Mouse 3"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"ATF3 -/- LPS 1 hr Mouse 3 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20050504_10_ATF3_ [...]
+"ATF3 -/- LPS 2 hr Mouse 1"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235	gene_knock_out	"MGED Ontology"	"ATF3 -/-"		"Mus musculus"	mixed_sex	"MGED Ontology"	"ATF3 -/- C57BL/6"	P-TABM-gilchrist1	P-TABM-gilchrist2	"ATF3 -/- LPS 2 hr Mouse 1"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"ATF3 -/- LPS 2 hr Mouse 1"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"ATF3 -/- LPS 2 hr Mouse 1 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20050310_06_ATF3- [...]
+"ATF3 -/- LPS 2 hr Mouse 2"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235	gene_knock_out	"MGED Ontology"	"ATF3 -/-"		"Mus musculus"	mixed_sex	"MGED Ontology"	"ATF3 -/- C57BL/6"	P-TABM-gilchrist1	P-TABM-gilchrist2	"ATF3 -/- LPS 2 hr Mouse 2"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"ATF3 -/- LPS 2 hr Mouse 2"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"ATF3 -/- LPS 2 hr Mouse 2 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20050411_05_ATF3- [...]
+"ATF3 -/- LPS 2 hr Mouse 3"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235	gene_knock_out	"MGED Ontology"	"ATF3 -/-"		"Mus musculus"	mixed_sex	"MGED Ontology"	"ATF3 -/- C57BL/6"	P-TABM-gilchrist1	P-TABM-gilchrist2	"ATF3 -/- LPS 2 hr Mouse 3"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"ATF3 -/- LPS 2 hr Mouse 3"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"ATF3 -/- LPS 2 hr Mouse 3 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20050504_11_ATF3_ [...]
+"ATF3 -/- LPS 4 hr Mouse 1"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235	gene_knock_out	"MGED Ontology"	"ATF3 -/-"		"Mus musculus"	mixed_sex	"MGED Ontology"	"ATF3 -/- C57BL/6"	P-TABM-gilchrist1	P-TABM-gilchrist2	"ATF3 -/- LPS 4 hr Mouse 1"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"ATF3 -/- LPS 4 hr Mouse 1"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"ATF3 -/- LPS 4 hr Mouse 1 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20050310_07_ATF3- [...]
+"ATF3 -/- LPS 4 hr Mouse 2"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235	gene_knock_out	"MGED Ontology"	"ATF3 -/-"		"Mus musculus"	mixed_sex	"MGED Ontology"	"ATF3 -/- C57BL/6"	P-TABM-gilchrist1	P-TABM-gilchrist2	"ATF3 -/- LPS 4 hr Mouse 2"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"ATF3 -/- LPS 4 hr Mouse 2"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"ATF3 -/- LPS 4 hr Mouse 2 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20050623_02_ATF3_ [...]
+"ATF3 -/- LPS 4 hr Mouse 3"	cell	"MGED Ontology"	macrophage	"Cell Type Ontology"	CL:0000235	gene_knock_out	"MGED Ontology"	"ATF3 -/-"		"Mus musculus"	mixed_sex	"MGED Ontology"	"ATF3 -/- C57BL/6"	P-TABM-gilchrist1	P-TABM-gilchrist2	"ATF3 -/- LPS 4 hr Mouse 3"	organism_part	"MGED Ontology"	P-AFFY-1	ArrayExpress	"ATF3 -/- LPS 4 hr Mouse 3"	total_RNA	"MGED Ontology"	P-AFFY-2	ArrayExpress	"ATF3 -/- LPS 4 hr Mouse 3 biotin"	synthetic_DNA	"MGED Ontology"	biotin	"MGED Ontology"	20050504_12_ATF3_ [...]
diff --git a/examples/magetab/real/E-TABM-134.idf b/examples/magetab/real/E-TABM-134.idf
new file mode 100644
index 0000000..a8b0141
--- /dev/null
+++ b/examples/magetab/real/E-TABM-134.idf
@@ -0,0 +1,27 @@
+"Investigation Title"	"WGA-LCM and Genomewide Survey of Lung Cancer"						
+"Experimental Design"	"genotyping_design	disease_state_design	optimization_design"				
+"Experimental Factor Name"	DISEASESTAGING	INDIVIDUAL	DISEASESTATE	LINEAR_AMPLIFICATION			
+"Experimental Factor Type"	disease_staging	individual	disease_state	linear_amplification			
+							
+"Person Last Name"	Bird						
+"Person First Name"	Helen						
+"Person Mid Initial"							
+"Person Email"	H.E.Bird at warwick.ac.uk						
+"Person Affiliation"	"Warwick University"						
+"Person Roles"	submitter						
+							
+"Public Release Date"	"2006-10-01"						
+							
+Comment[ArrayExpressSubmissionDate]	"2006-08-11"						
+							
+"Publication Author List"	"Bird H; Snead DRJ; Hey Y; Brown PE; Davis SA; James S; Knowles MA; Hurst CD; Blanks A; Pepper SD & Snead DRJ; Brown PE; James S; Davis SA; Suortamo S; Pandey S; Blanks A; Grammatopoulos D; Vohra H; Bird H"						
+"Publication Title"	"Validation of a method for processing laser captures microdissected samples for analysis using high density single nucleotide polymorphism arrays & Genomewide loss of heterozygosity in small cell and non-small cell lung cancer and precursor bronchial epithelium"						
+"Experiment Description"	"A series of experiments were performed to validate whole genome amplification of genomic DNA from laser captured microdissectates and the technique was then applied to serial samples of developing lung cancers to identify early events.  10K Affymetrix SNP arrays were used."						
+							
+"Protocol Name"	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-6	P-EXML-8	P-EXML-9	P-EXML-10
+"Protocol Type"	grow	specified_biomaterial_action	nucleic_acid_extraction	labeling	hybridization	hybridization	image_acquisition
+"Protocol Description"	"Resected tumour, tissue of bronchial origin and  lymph node were snap frozen in liquid nitrogen. Frozen sections were cut and stained with haematoxylin and eosin."	"LCM was performed using the Nikon Slᄉ cut instrument (Nikon UK Ltd.) on frozen sections which had been stored at -80ᄎC. Target lesions were visualised directly, excised and captured on to the adhesive cap of the LCM tubes (Nikon UK Ltd.). Captured cells were stored at -80ᄎC prior to WGA and microarray  [...]
+"Protocol Parameters"			amount_of_starting_material;amplification_method;processing_centre				
+"Protocol Software"							
+							
+"SDRF File"	E-TABM-134_sdrf.txt						
diff --git a/examples/magetab/real/E-TABM-134_sdrf.txt b/examples/magetab/real/E-TABM-134_sdrf.txt
new file mode 100644
index 0000000..18555bf
--- /dev/null
+++ b/examples/magetab/real/E-TABM-134_sdrf.txt
@@ -0,0 +1,26 @@
+"Source Name"	"Material Type"	"Characteristics [Age]"	"Characteristics [ClinicalHistory]"	"Characteristics [DiseaseStaging]"	"Characteristics [DiseaseState]"	"Characteristics [Individual]"	"Characteristics [OrganismPart]"	"Characteristics [Organism]"	"Characteristics [Sex]"	"Characteristics [TimeUnit]"	"Characteristics [TumourGrading]"	"Protocol REF"	"Protocol REF"	"Sample Name"	"Material Type"	"Protocol REF"	"Parameter Value [amount_of_starting_material]"	"Parameter Value [amplification [...]
+nJ82_P	cell_line				"tumour cell line"	"patient J82"		"Homo sapiens"						nJ82_P	organism_part	P-EXML-4	"250ng DNA"	none	PICR	nJ82_P	total_RNA	P-EXML-6	"nJ82_P biotin"	synthetic_DNA	biotin	P-EXML-8	nJ82_P	A-AFFY-64	P-EXML-10	nJ82_P	nJ82_P.CEL	nJ82_P	nJ82_P.CHP	Mapping10K_Xba131.CDF	"not applicable"	"tumour cell line"	"patient J82"	none
+nJ82ebv_P	cell_line				"immortalised cell line"	"patient J82"		"Homo sapiens"						nJ82ebv_P	organism_part	P-EXML-4	"250ng DNA"	none	PICR	nJ82ebv_P	total_RNA	P-EXML-6	"nJ82ebv_P biotin"	synthetic_DNA	biotin	P-EXML-8	nJ82ebv_P	A-AFFY-64	P-EXML-10	nJ82ebv_P	nJ82ebv_P.CEL	nJ82ebv_P	nJ82ebv_P.CHP	Mapping10K_Xba131.CDF	"not applicable"	"immortalised cell line"	"patient J82"	none
+aJ82_1_W	cell_line				"tumour cell line"	"patient J82"		"Homo sapiens"						aJ82_1_W	organism_part	P-EXML-4	"1ng DNA"	WGA	Warwick	aJ82_1_W	total_RNA	P-EXML-6	"aJ82_1_W biotin"	synthetic_DNA	biotin	P-EXML-8	aJ82_1_W	A-AFFY-64	P-EXML-10	aJ82_1_W	aJ82_1_W.CEL	aJ82_1_W	aJ82_1_W.CHP	Mapping10K_Xba131.CDF	"not applicable"	"tumour cell line"	"patient J82"	WGA
+aJ82ebv_1_W	cell_line				"immortalised cell line"	"patient J82"		"Homo sapiens"						aJ82ebv_1_W	organism_part	P-EXML-4	"1ng DNA"	WGA	Warwick	aJ82ebv_1_W	total_RNA	P-EXML-6	"aJ82ebv_1_W biotin"	synthetic_DNA	biotin	P-EXML-8	aJ82ebv_1_W	A-AFFY-64	P-EXML-10	aJ82ebv_1_W	aJ82ebv_1_W.CEL	aJ82ebv_1_W	aJ82ebv_1_W.CHP	Mapping10K_Xba131.CDF	"not applicable"	"immortalised cell line"	"patient J82"	WGA
+aJ82_3_W	cell_line				"tumour cell line"	"patient J82"		"Homo sapiens"						aJ82_3_W	organism_part	P-EXML-4	"3ng DNA"	WGA	Warwick	aJ82_3_W	total_RNA	P-EXML-6	"aJ82_3_W biotin"	synthetic_DNA	biotin	P-EXML-8	aJ82_3_W	A-AFFY-64	P-EXML-10	aJ82_3_W	aJ82_3_W.CEL	aJ82_3_W	aJ82_3_W.CHP	Mapping10K_Xba131.CDF	"not applicable"	"tumour cell line"	"patient J82"	WGA
+nJ82_W	cell_line				"tumour cell line"	"patient J82"		"Homo sapiens"						nJ82_W	organism_part	P-EXML-4	"250ng DNA"	none	Warwick	nJ82_W	total_RNA	P-EXML-6	"nJ82_W biotin"	synthetic_DNA	biotin	P-EXML-8	nJ82_W	A-AFFY-64	P-EXML-10	nJ82_W	nJ82_W.CEL	nJ82_W	nJ82_W.CHP	Mapping10K_Xba131.CDF	"not applicable"	"tumour cell line"	"patient J82"	none
+0904_MK18_10K_90J82	cell_line				"mixed source cell line"	"patient J82"		"Homo sapiens"						0904_MK18_10K_90J82	organism_part	P-EXML-4	"250ng DNA"	none	PICR	0904_MK18_10K_90J82	total_RNA	P-EXML-6	"0904_MK18_10K_90J82 biotin"	synthetic_DNA	biotin	P-EXML-8	0904_MK18_10K_90J82	A-AFFY-64	P-EXML-10	0904_MK18_10K_90J82	0904_MK18_10K_90J82.CEL	0904_MK18_10K_90J82	0904_MK18_10K_90J82.CHP	Mapping10K_Xba131.CDF	"not applicable"	"mixed source cell line"	"patient J82"	none
+0904_MK19_10K_80J82	cell_line				"mixed source cell line"	"patient J82"		"Homo sapiens"						0904_MK19_10K_80J82	organism_part	P-EXML-4	"250ng DNA"	none	PICR	0904_MK19_10K_80J82	total_RNA	P-EXML-6	"0904_MK19_10K_80J82 biotin"	synthetic_DNA	biotin	P-EXML-8	0904_MK19_10K_80J82	A-AFFY-64	P-EXML-10	0904_MK19_10K_80J82	0904_MK19_10K_80J82.CEL	0904_MK19_10K_80J82	0904_MK19_10K_80J82.CHP	Mapping10K_Xba131.CDF	"not applicable"	"mixed source cell line"	"patient J82"	none
+0904_MK20_10K_70J82	cell_line				"mixed source cell line"	"patient J82"		"Homo sapiens"						0904_MK20_10K_70J82	organism_part	P-EXML-4	"250ng DNA"	none	PICR	0904_MK20_10K_70J82	total_RNA	P-EXML-6	"0904_MK20_10K_70J82 biotin"	synthetic_DNA	biotin	P-EXML-8	0904_MK20_10K_70J82	A-AFFY-64	P-EXML-10	0904_MK20_10K_70J82	0904_MK20_10K_70J82.CEL	0904_MK20_10K_70J82	0904_MK20_10K_70J82.CHP	Mapping10K_Xba131.CDF	"not applicable"	"mixed source cell line"	"patient J82"	none
+Case1_aLN	organism_part	65	"smoking history: 50 pack years"	aLN	"squamous cell carcinoma"	"case 1"	"lymph node"	"Homo sapiens"	male	years	"T2, N0, stage 1b"	P-EXML-2	P-EXML-3	Case1_aLN	organism_part	P-EXML-4	"1035 cells"	WGA	Warwick	Case1_aLN	total_RNA	P-EXML-6	"Case1_aLN biotin"	synthetic_DNA	biotin	P-EXML-8	Case1_aLN	A-AFFY-64	P-EXML-10	Case1_aLN	Case1_aLN.CEL	Case1_aLN	Case1_aLN.CHP	Mapping10K_Xba131.CDF	aLN	"squamous cell carcinoma"	"case 1"	WGA
+Case1_aHYP	organism_part	65	"smoking history: 50 pack years"	aHYP	"squamous cell carcinoma"	"case 1"	lung	"Homo sapiens"	male	years	"T2, N0, stage 1b"	P-EXML-2	P-EXML-3	Case1_aHYP	organism_part	P-EXML-4	"816 cells"	WGA	Warwick	Case1_aHYP	total_RNA	P-EXML-6	"Case1_aHYP biotin"	synthetic_DNA	biotin	P-EXML-8	Case1_aHYP	A-AFFY-64	P-EXML-10	Case1_aHYP	Case1_aHYP.CEL	Case1_aHYP	Case1_aHYP.CHP	Mapping10K_Xba131.CDF	aHYP	"squamous cell carcinoma"	"case 1"	WGA
+Case1_aTUM	organism_part	65	"smoking history: 50 pack years"	aTUM	"squamous cell carcinoma"	"case 1"	lung	"Homo sapiens"	male	years	"T2, N0, stage 1b"	P-EXML-2	P-EXML-3	Case1_aTUM	organism_part	P-EXML-4	"1136 cells"	WGA	Warwick	Case1_aTUM	total_RNA	P-EXML-6	"Case1_aTUM biotin"	synthetic_DNA	biotin	P-EXML-8	Case1_aTUM	A-AFFY-64	P-EXML-10	Case1_aTUM	Case1_aTUM.CEL	Case1_aTUM	Case1_aTUM.CHP	Mapping10K_Xba131.CDF	aTUM	"squamous cell carcinoma"	"case 1"	WGA
+Case2_aAVM	organism_part	70	"smoking history: 30 pack years"	aAVM	"large cell neuroendocrine carcinoma"	"case 2"	macrophage	"Homo sapiens"	female	years	"T2, N0, M0, stage 1b"	P-EXML-2	P-EXML-3	Case2_aAVM	organism_part	P-EXML-4	"558 cells"	WGA	Warwick	Case2_aAVM	total_RNA	P-EXML-6	"Case2_aAVM biotin"	synthetic_DNA	biotin	P-EXML-8	Case2_aAVM	A-AFFY-64	P-EXML-10	Case2_aAVM	Case2_aAVM.CEL	Case2_aAVM	Case2_aAVM.CHP	Mapping10K_Xba131.CDF	aAVM	"large cell neuroendocrine carcinoma"	"case 2"	WGA
+Case2_aBE	organism_part	70	"smoking history: 30 pack years"	aBE	"large cell neuroendocrine carcinoma"	"case 2"	lung	"Homo sapiens"	female	years	"T2, N0, M0, stage 1b"	P-EXML-2	P-EXML-3	Case2_aBE	organism_part	P-EXML-4	nk	WGA	Warwick	Case2_aBE	total_RNA	P-EXML-6	"Case2_aBE biotin"	synthetic_DNA	biotin	P-EXML-8	Case2_aBE	A-AFFY-64	P-EXML-10	Case2_aBE	Case2_aBE.CEL	Case2_aBE	Case2_aBE.CHP	Mapping10K_Xba131.CDF	aBE	"large cell neuroendocrine carcinoma"	"case 2"	WGA
+Case2_aOE	organism_part	70	"smoking history: 30 pack years"	aOE	"large cell neuroendocrine carcinoma"	"case 2"	lung	"Homo sapiens"	female	years	"T2, N0, M0, stage 1b"	P-EXML-2	P-EXML-3	Case2_aOE	organism_part	P-EXML-4	"419 cells"	WGA	Warwick	Case2_aOE	total_RNA	P-EXML-6	"Case2_aOE biotin"	synthetic_DNA	biotin	P-EXML-8	Case2_aOE	A-AFFY-64	P-EXML-10	Case2_aOE	Case2_aOE.CEL	Case2_aOE	Case2_aOE.CHP	Mapping10K_Xba131.CDF	aOE	"large cell neuroendocrine carcinoma"	"case 2"	WGA
+Case2_aTUM1	organism_part	70	"smoking history: 30 pack years"	aTUM1	"large cell neuroendocrine carcinoma"	"case 2"	lung	"Homo sapiens"	female	years	"T2, N0, M0, stage 1b"	P-EXML-2	P-EXML-3	Case2_aTUM1	organism_part	P-EXML-4	"446 cells"	WGA	Warwick	Case2_aTUM1	total_RNA	P-EXML-6	"Case2_aTUM1 biotin"	synthetic_DNA	biotin	P-EXML-8	Case2_aTUM1	A-AFFY-64	P-EXML-10	Case2_aTUM1	Case2_aTUM1.CEL	Case2_aTUM1	Case2_aTUM1.CHP	Mapping10K_Xba131.CDF	aTUM1	"large cell neuroendocrine carcinoma"	"case 2"	WGA
+Case2_aTUM2	organism_part	70	"smoking history: 30 pack years"	aTUM2	"large cell neuroendocrine carcinoma"	"case 2"	lung	"Homo sapiens"	female	years	"T2, N0, M0, stage 1b"	P-EXML-2	P-EXML-3	Case2_aTUM2	organism_part	P-EXML-4	"498 cells"	WGA	Warwick	Case2_aTUM2	total_RNA	P-EXML-6	"Case2_aTUM2 biotin"	synthetic_DNA	biotin	P-EXML-9	Case2_aTUM2	A-AFFY-65	P-EXML-10	Case2_aTUM2	Case2_aTUM2.CEL	Case2_aTUM2	Case2_aTUM2.CHP	Mapping10K_Xba142.CDF	aTUM2	"large cell neuroendocrine carcinoma"	"case 2"	WGA
+Case3_nLN	organism_part	47	"smoking history: 30 pack years"	nLN	"small cell lung cancer"	"case 3"	"lymph node"	"Homo sapiens"	female	years	"T1, N2, M0, stage 3"	P-EXML-2		Case3_nLN	organism_part	P-EXML-4	"250ng DNA"	none	Warwick	Case3_nLN	total_RNA	P-EXML-6	"Case3_nLN biotin"	synthetic_DNA	biotin	P-EXML-8	Case3_nLN	A-AFFY-64	P-EXML-10	Case3_nLN	Case3_nLN.CEL	Case3_nLN	Case3_nLN.CHP	Mapping10K_Xba131.CDF	nLN	"small cell lung cancer"	"case 3"	none
+Case3_aLN	organism_part	47	"smoking history: 30 pack years"	aLN	"small cell lung cancer"	"case 3"	"lymph node"	"Homo sapiens"	female	years	"T1, N2, M0, stage 3"	P-EXML-2	P-EXML-3	Case3_aLN	organism_part	P-EXML-4	"545 cells"	WGA	Warwick	Case3_aLN	total_RNA	P-EXML-6	"Case3_aLN biotin"	synthetic_DNA	biotin	P-EXML-8	Case3_aLN	A-AFFY-64	P-EXML-10	Case3_aLN	Case3_aLN.CEL	Case3_aLN	Case3_aLN.CHP	Mapping10K_Xba131.CDF	aLN	"small cell lung cancer"	"case 3"	WGA
+Case3_aBE	organism_part	47	"smoking history: 30 pack years"	aBE	"small cell lung cancer"	"case 3"	lung	"Homo sapiens"	female	years	"T1, N2, M0, stage 3"	P-EXML-2	P-EXML-3	Case3_aBE	organism_part	P-EXML-4	"843 cells"	WGA	Warwick	Case3_aBE	total_RNA	P-EXML-6	"Case3_aBE biotin"	synthetic_DNA	biotin	P-EXML-8	Case3_aBE	A-AFFY-64	P-EXML-10	Case3_aBE	Case3_aBE.CEL	Case3_aBE	Case3_aBE.CHP	Mapping10K_Xba131.CDF	aBE	"small cell lung cancer"	"case 3"	WGA
+Case3_aOE	organism_part	47	"smoking history: 30 pack years"	aOE	"small cell lung cancer"	"case 3"	lung	"Homo sapiens"	female	years	"T1, N2, M0, stage 3"	P-EXML-2	P-EXML-3	Case3_aOE	organism_part	P-EXML-4	"200 cells"	WGA	Warwick	Case3_aOE	total_RNA	P-EXML-6	"Case3_aOE biotin"	synthetic_DNA	biotin	P-EXML-9	Case3_aOE	A-AFFY-65	P-EXML-10	Case3_aOE	Case3_aOE.CEL	Case3_aOE	Case3_aOE.CHP	Mapping10K_Xba142.CDF	aOE	"small cell lung cancer"	"case 3"	WGA
+Case3_nTB	organism_part	47	"smoking history: 30 pack years"	nTB	"small cell lung cancer"	"case 3"	lung	"Homo sapiens"	female	years	"T1, N2, M0, stage 3"	P-EXML-2		Case3_nTB	organism_part	P-EXML-4	"250ng DNA"	none	Warwick	Case3_nTB	total_RNA	P-EXML-6	"Case3_nTB biotin"	synthetic_DNA	biotin	P-EXML-8	Case3_nTB	A-AFFY-64	P-EXML-10	Case3_nTB	Case3_nTB.CEL	Case3_nTB	Case3_nTB.CHP	Mapping10K_Xba131.CDF	nTB	"small cell lung cancer"	"case 3"	none
+Case3_aTUM1	organism_part	47	"smoking history: 30 pack years"	aTUM1	"small cell lung cancer"	"case 3"	lung	"Homo sapiens"	female	years	"T1, N2, M0, stage 3"	P-EXML-2	P-EXML-3	Case3_aTUM1	organism_part	P-EXML-4	"1300 cells"	WGA	Warwick	Case3_aTUM1	total_RNA	P-EXML-6	"Case3_aTUM1 biotin"	synthetic_DNA	biotin	P-EXML-8	Case3_aTUM1	A-AFFY-64	P-EXML-10	Case3_aTUM1	Case3_aTUM1.CEL	Case3_aTUM1	Case3_aTUM1.CHP	Mapping10K_Xba131.CDF	aTUM1	"small cell lung cancer"	"case 3"	WGA
+Case3_aTUM2	organism_part	47	"smoking history: 30 pack years"	aTUM2	"small cell lung cancer"	"case 3"	lung	"Homo sapiens"	female	years	"T1, N2, M0, stage 3"	P-EXML-2	P-EXML-3	Case3_aTUM2	organism_part	P-EXML-4	"997 cells"	WGA	Warwick	Case3_aTUM2	total_RNA	P-EXML-6	"Case3_aTUM2 biotin"	synthetic_DNA	biotin	P-EXML-8	Case3_aTUM2	A-AFFY-64	P-EXML-10	Case3_aTUM2	Case3_aTUM2.CEL	Case3_aTUM2	Case3_aTUM2.CHP	Mapping10K_Xba131.CDF	aTUM2	"small cell lung cancer"	"case 3"	WGA
+Case3_aTUM3	organism_part	47	"smoking history: 30 pack years"	aTUM3	"small cell lung cancer"	"case 3"	lung	"Homo sapiens"	female	years	"T1, N2, M0, stage 3"	P-EXML-2	P-EXML-3	Case3_aTUM3	organism_part	P-EXML-4	"350 cells"	WGA	Warwick	Case3_aTUM3	total_RNA	P-EXML-6	"Case3_aTUM3 biotin"	synthetic_DNA	biotin	P-EXML-8	Case3_aTUM3	A-AFFY-64	P-EXML-10	Case3_aTUM3	Case3_aTUM3.CEL	Case3_aTUM3	Case3_aTUM3.CHP	Mapping10K_Xba131.CDF	aTUM3	"small cell lung cancer"	"case 3"	WGA
diff --git a/examples/magetab/real/E-TABM-136.idf b/examples/magetab/real/E-TABM-136.idf
new file mode 100644
index 0000000..9a31ee5
--- /dev/null
+++ b/examples/magetab/real/E-TABM-136.idf
@@ -0,0 +1,24 @@
+"Investigation Title"	"Functionality of Intergenic Transcription: An Evolutionary Comparison"		
+"Experimental Design"	organism_part_comparison_design	species_comparison_design	
+"Experimental Factor Name"	ORGANISM	ORGANISMPART	"LYMPHOBLASTOID CELLLINE"
+"Experimental Factor Type"	organism	organism_part	"lymphoblastoid cell_line"
+			
+"Person Last Name"	Khaitovich		
+"Person First Name"	Philipp		
+"Person Mid Initial"			
+"Person Email"	khaitovich at eva.mpg.de		
+"Person Roles"	submitter		
+			
+"Quality Control Types"	biological_replicate_design		
+"Public Release Date"	"2006-09-30"		
+			
+Comment[ArrayExpressSubmissionDate]	"2006-08-18"		
+			
+"Publication Author List"	"Philipp Khaitovich; Janet Kelso; Henriette Franz; Johann Visagie; Thomas Giger; Sabrina Joerchel; Ekkehard Petzold; Richard E. Green; Michael Lachmann; Svante P��bo"		
+"Publication Title"	"Functionality of Intergenic Transcription: An Evolutionary Comparison"		
+"Experiment Description"	"Although a large proportion of human transcription occurs outside the boundaries of known genes, the functional significance of this transcription remains unknown. We have compared the expression patterns of known genes as well as intergenic transcripts within the ENCODE regions between humans and chimpanzees in brain, heart, testis and lymphoblastoid cell lines. We find that intergenic transcripts show patterns of tissue-specific conservation of their expressio [...]
+			
+"SDRF File"	E-TABM-136_sdrf.txt		
+			
+"Term Source Name"	ArrayExpress		
+"Term Source File"	http://www.ebi.ac.uk/arrayexpress/		
diff --git a/examples/magetab/real/E-TABM-136_sdrf.txt b/examples/magetab/real/E-TABM-136_sdrf.txt
new file mode 100644
index 0000000..dbe3d26
--- /dev/null
+++ b/examples/magetab/real/E-TABM-136_sdrf.txt
@@ -0,0 +1,81 @@
+"Source Name"	"Material Type"	"Characteristics [Age(years)]"	"Characteristics [OrganismStatus]"	"Characteristics [Organism]"	"Characteristics [Sex]"	"Material Type"	"Sample Name"	"Characteristics [OrganismPart]"	"Material Type"	"Protocol REF"	"Term Source REF"	"Extract Name"	"Material Type"	"Protocol REF"	"Term Source REF"	"Labeled Extract Name"	"Material Type"	Label	"Protocol REF:Affymetrix:Protocol:"	"Hybridization Name"	"Array Design REF"	"Protocol REF"	"Term Source REF"	"Scan Name"	" [...]
+c01	organism_part	7	postmortem	"Pan troglodytes"	male	whole_organism	c01: brain, prefrontal cortex	"brain, prefrontal cortex"	organism_part	P-TABM-44	ArrayExpress	c01: brain, prefrontal cortex	total_RNA	P-TABM-47	ArrayExpress	c01: brain, prefrontal cortex	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CB1F	A-AFFY-61	P-TABM-46	ArrayExpress	CB1F	CB1F.CEL	CB1F.EXP	brain	"Pan troglodytes"	
+c02	organism_part	12	postmortem	"Pan troglodytes"	male	whole_organism	c02: brain, prefrontal cortex	"brain, prefrontal cortex"	organism_part	P-TABM-44	ArrayExpress	c02: brain, prefrontal cortex	total_RNA	P-TABM-47	ArrayExpress	c02: brain, prefrontal cortex	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CB2F	A-AFFY-61	P-TABM-46	ArrayExpress	CB2F	CB2F.CEL	CB2F.EXP	brain	"Pan troglodytes"	
+c03	organism_part	12	postmortem	"Pan troglodytes"	male	whole_organism	c03: brain, prefrontal cortex	"brain, prefrontal cortex"	organism_part	P-TABM-44	ArrayExpress	c03: brain, prefrontal cortex	total_RNA	P-TABM-47	ArrayExpress	c03: brain, prefrontal cortex	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CB3F	A-AFFY-61	P-TABM-46	ArrayExpress	CB3F	CB3F.CEL	CB3F.EXP	brain	"Pan troglodytes"	
+c04	organism_part	12	postmortem	"Pan troglodytes"	male	whole_organism	c04: brain, prefrontal cortex	"brain, prefrontal cortex"	organism_part	P-TABM-44	ArrayExpress	c04: brain, prefrontal cortex	total_RNA	P-TABM-47	ArrayExpress	c04: brain, prefrontal cortex	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CB4F	A-AFFY-61	P-TABM-46	ArrayExpress	CB4F	CB4F.CEL	CB4F.EXP	brain	"Pan troglodytes"	
+c05	organism_part	>40	postmortem	"Pan troglodytes"	male	whole_organism	c05: brain, prefrontal cortex	"brain, prefrontal cortex"	organism_part	P-TABM-44	ArrayExpress	c05: brain, prefrontal cortex	total_RNA	P-TABM-47	ArrayExpress	c05: brain, prefrontal cortex	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CB5F	A-AFFY-61	P-TABM-46	ArrayExpress	CB5F	CB5F.CEL	CB5F.EXP	brain	"Pan troglodytes"	
+c06	organism_part	19	postmortem	"Pan troglodytes"	female	whole_organism	c06: lymphoblastoid cell line	"lymphoblastoid cell line"	organism_part	P-TABM-44	ArrayExpress	c06: lymphoblastoid cell line	total_RNA	P-TABM-47	ArrayExpress	c06: lymphoblastoid cell line	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CC1F	A-AFFY-61	P-TABM-46	ArrayExpress	CC1F	CC1F.CEL	CC1F.EXP		"Pan troglodytes"	"lymphoblastoid cell line"
+c07	organism_part	20	postmortem	"Pan troglodytes"	female	whole_organism	c07: lymphoblastoid cell line	"lymphoblastoid cell line"	organism_part	P-TABM-44	ArrayExpress	c07: lymphoblastoid cell line	total_RNA	P-TABM-47	ArrayExpress	c07: lymphoblastoid cell line	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CC2F	A-AFFY-61	P-TABM-46	ArrayExpress	CC2F	CC2F.CEL	CC2F.EXP		"Pan troglodytes"	"lymphoblastoid cell line"
+c08	organism_part	32	postmortem	"Pan troglodytes"	female	whole_organism	c08: lymphoblastoid cell line	"lymphoblastoid cell line"	organism_part	P-TABM-44	ArrayExpress	c08: lymphoblastoid cell line	total_RNA	P-TABM-47	ArrayExpress	c08: lymphoblastoid cell line	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CC3F	A-AFFY-61	P-TABM-46	ArrayExpress	CC3F	CC3F.CEL	CC3F.EXP		"Pan troglodytes"	"lymphoblastoid cell line"
+c09	organism_part	12	postmortem	"Pan troglodytes"	female	whole_organism	c09: lymphoblastoid cell line	"lymphoblastoid cell line"	organism_part	P-TABM-44	ArrayExpress	c09: lymphoblastoid cell line	total_RNA	P-TABM-47	ArrayExpress	c09: lymphoblastoid cell line	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CC4F	A-AFFY-61	P-TABM-46	ArrayExpress	CC4F	CC4F.CEL	CC4F.EXP		"Pan troglodytes"	"lymphoblastoid cell line"
+c10	organism_part	37	postmortem	"Pan troglodytes"	female	whole_organism	c10: lymphoblastoid cell line	"lymphoblastoid cell line"	organism_part	P-TABM-44	ArrayExpress	c10: lymphoblastoid cell line	total_RNA	P-TABM-47	ArrayExpress	c10: lymphoblastoid cell line	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CC5F	A-AFFY-61	P-TABM-46	ArrayExpress	CC5F	CC5F.CEL	CC5F.EXP		"Pan troglodytes"	"lymphoblastoid cell line"
+c01	organism_part	7	postmortem	"Pan troglodytes"	male	whole_organism	c01: heart muscle	"heart muscle"	organism_part	P-TABM-44	ArrayExpress	c01: heart muscle	total_RNA	P-TABM-47	ArrayExpress	c01: heart muscle	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CH1F	A-AFFY-61	P-TABM-46	ArrayExpress	CH1F	CH1F.CEL	CH1F.EXP	heart	"Pan troglodytes"	
+c11	organism_part	34	postmortem	"Pan troglodytes"	male	whole_organism	c11: heart muscle	"heart muscle"	organism_part	P-TABM-44	ArrayExpress	c11: heart muscle	total_RNA	P-TABM-47	ArrayExpress	c11: heart muscle	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CH2F	A-AFFY-61	P-TABM-46	ArrayExpress	CH2F	CH2F.CEL	CH2F.EXP	heart	"Pan troglodytes"	
+c03	organism_part	12	postmortem	"Pan troglodytes"	male	whole_organism	c03: heart muscle	"heart muscle"	organism_part	P-TABM-44	ArrayExpress	c03: heart muscle	total_RNA	P-TABM-47	ArrayExpress	c03: heart muscle	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CH3F	A-AFFY-61	P-TABM-46	ArrayExpress	CH3F	CH3F.CEL	CH3F.EXP	heart	"Pan troglodytes"	
+c12	organism_part	26	postmortem	"Pan troglodytes"	male	whole_organism	c12: heart muscle	"heart muscle"	organism_part	P-TABM-44	ArrayExpress	c12: heart muscle	total_RNA	P-TABM-47	ArrayExpress	c12: heart muscle	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CH4F	A-AFFY-61	P-TABM-46	ArrayExpress	CH4F	CH4F.CEL	CH4F.EXP	heart	"Pan troglodytes"	
+c02	organism_part	12	postmortem	"Pan troglodytes"	male	whole_organism	c02: heart muscle	"heart muscle"	organism_part	P-TABM-44	ArrayExpress	c02: heart muscle	total_RNA	P-TABM-47	ArrayExpress	c02: heart muscle	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CH5F	A-AFFY-61	P-TABM-46	ArrayExpress	CH5F	CH5F.CEL	CH5F.EXP	heart	"Pan troglodytes"	
+c13	organism_part	20	postmortem	"Pan troglodytes"	male	whole_organism	c13: testis	testis	organism_part	P-TABM-44	ArrayExpress	c13: testis	total_RNA	P-TABM-47	ArrayExpress	c13: testis	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CT1F	A-AFFY-61	P-TABM-46	ArrayExpress	CT1F	CT1F.CEL	CT1F.EXP	testis	"Pan troglodytes"	
+c02	organism_part	12	postmortem	"Pan troglodytes"	male	whole_organism	c02: testis	testis	organism_part	P-TABM-44	ArrayExpress	c02: testis	total_RNA	P-TABM-47	ArrayExpress	c02: testis	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CT2F	A-AFFY-61	P-TABM-46	ArrayExpress	CT2F	CT2F.CEL	CT2F.EXP	testis	"Pan troglodytes"	
+c03	organism_part	12	postmortem	"Pan troglodytes"	male	whole_organism	c03: testis	testis	organism_part	P-TABM-44	ArrayExpress	c03: testis	total_RNA	P-TABM-47	ArrayExpress	c03: testis	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CT3F	A-AFFY-61	P-TABM-46	ArrayExpress	CT3F	CT3F.CEL	CT3F.EXP	testis	"Pan troglodytes"	
+c04	organism_part	12	postmortem	"Pan troglodytes"	male	whole_organism	c04: testis	testis	organism_part	P-TABM-44	ArrayExpress	c04: testis	total_RNA	P-TABM-47	ArrayExpress	c04: testis	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CT4F	A-AFFY-61	P-TABM-46	ArrayExpress	CT4F	CT4F.CEL	CT4F.EXP	testis	"Pan troglodytes"	
+c05	organism_part	>40	postmortem	"Pan troglodytes"	male	whole_organism	c05: testis	testis	organism_part	P-TABM-44	ArrayExpress	c05: testis	total_RNA	P-TABM-47	ArrayExpress	c05: testis	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CT5F	A-AFFY-61	P-TABM-46	ArrayExpress	CT5F	CT5F.CEL	CT5F.EXP	testis	"Pan troglodytes"	
+h01	organism_part	45	postmortem	"Homo sapiens"	male	whole_organism	h01: brain, prefrontal cortex	"brain, prefrontal cortex"	organism_part	P-TABM-44	ArrayExpress	h01: brain, prefrontal cortex	total_RNA	P-TABM-47	ArrayExpress	h01: brain, prefrontal cortex	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HB1F	A-AFFY-61	P-TABM-46	ArrayExpress	HB1F	HB1F.CEL	HB1F.EXP	brain	"Homo sapiens"	
+h02	organism_part	45	postmortem	"Homo sapiens"	male	whole_organism	h02: brain, prefrontal cortex	"brain, prefrontal cortex"	organism_part	P-TABM-44	ArrayExpress	h02: brain, prefrontal cortex	total_RNA	P-TABM-47	ArrayExpress	h02: brain, prefrontal cortex	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HB2F	A-AFFY-61	P-TABM-46	ArrayExpress	HB2F	HB2F.CEL	HB2F.EXP	brain	"Homo sapiens"	
+h03	organism_part	48	postmortem	"Homo sapiens"	male	whole_organism	h03: brain, prefrontal cortex	"brain, prefrontal cortex"	organism_part	P-TABM-44	ArrayExpress	h03: brain, prefrontal cortex	total_RNA	P-TABM-47	ArrayExpress	h03: brain, prefrontal cortex	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HB3F	A-AFFY-61	P-TABM-46	ArrayExpress	HB3F	HB3F.CEL	HB3F.EXP	brain	"Homo sapiens"	
+h04	organism_part	56	postmortem	"Homo sapiens"	male	whole_organism	h04: brain, prefrontal cortex	"brain, prefrontal cortex"	organism_part	P-TABM-44	ArrayExpress	h04: brain, prefrontal cortex	total_RNA	P-TABM-47	ArrayExpress	h04: brain, prefrontal cortex	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HB4F	A-AFFY-61	P-TABM-46	ArrayExpress	HB4F	HB4F.CEL	HB4F.EXP	brain	"Homo sapiens"	
+h05	organism_part	67	postmortem	"Homo sapiens"	male	whole_organism	h05: brain, prefrontal cortex	"brain, prefrontal cortex"	organism_part	P-TABM-44	ArrayExpress	h05: brain, prefrontal cortex	total_RNA	P-TABM-47	ArrayExpress	h05: brain, prefrontal cortex	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HB5F	A-AFFY-61	P-TABM-46	ArrayExpress	HB5F	HB5F.CEL	HB5F.EXP	brain	"Homo sapiens"	
+h06	organism_part	13	postmortem	"Homo sapiens"	female	whole_organism	h06: lymphoblastoid cell line	"lymphoblastoid cell line"	organism_part	P-TABM-44	ArrayExpress	h06: lymphoblastoid cell line	total_RNA	P-TABM-47	ArrayExpress	h06: lymphoblastoid cell line	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HC1F	A-AFFY-61	P-TABM-46	ArrayExpress	HC1F	HC1F.CEL	HC1F.EXP		"Homo sapiens"	"lymphoblastoid cell line"
+h07	organism_part	12	postmortem	"Homo sapiens"	female	whole_organism	h07: lymphoblastoid cell line	"lymphoblastoid cell line"	organism_part	P-TABM-44	ArrayExpress	h07: lymphoblastoid cell line	total_RNA	P-TABM-47	ArrayExpress	h07: lymphoblastoid cell line	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HC2F	A-AFFY-61	P-TABM-46	ArrayExpress	HC2F	HC2F.CEL	HC2F.EXP		"Homo sapiens"	"lymphoblastoid cell line"
+h08	organism_part	20	postmortem	"Homo sapiens"	female	whole_organism	h08: lymphoblastoid cell line	"lymphoblastoid cell line"	organism_part	P-TABM-44	ArrayExpress	h08: lymphoblastoid cell line	total_RNA	P-TABM-47	ArrayExpress	h08: lymphoblastoid cell line	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HC3F	A-AFFY-61	P-TABM-46	ArrayExpress	HC3F	HC3F.CEL	HC3F.EXP		"Homo sapiens"	"lymphoblastoid cell line"
+h09	organism_part	22	postmortem	"Homo sapiens"	female	whole_organism	h09: lymphoblastoid cell line	"lymphoblastoid cell line"	organism_part	P-TABM-44	ArrayExpress	h09: lymphoblastoid cell line	total_RNA	P-TABM-47	ArrayExpress	h09: lymphoblastoid cell line	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HC4F	A-AFFY-61	P-TABM-46	ArrayExpress	HC4F	HC4F.CEL	HC4F.EXP		"Homo sapiens"	"lymphoblastoid cell line"
+h10	organism_part	32	postmortem	"Homo sapiens"	female	whole_organism	h10: lymphoblastoid cell line	"lymphoblastoid cell line"	organism_part	P-TABM-44	ArrayExpress	h10: lymphoblastoid cell line	total_RNA	P-TABM-47	ArrayExpress	h10: lymphoblastoid cell line	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HC5F	A-AFFY-61	P-TABM-46	ArrayExpress	HC5F	HC5F.CEL	HC5F.EXP		"Homo sapiens"	"lymphoblastoid cell line"
+h11	organism_part	71	postmortem	"Homo sapiens"	male	whole_organism	h11: heart muscle	"heart muscle"	organism_part	P-TABM-44	ArrayExpress	h11: heart muscle	total_RNA	P-TABM-47	ArrayExpress	h11: heart muscle	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HH1F	A-AFFY-61	P-TABM-46	ArrayExpress	HH1F	HH1F.CEL	HH1F.EXP	heart	"Homo sapiens"	
+h12	organism_part	74	postmortem	"Homo sapiens"	male	whole_organism	h12: heart muscle	"heart muscle"	organism_part	P-TABM-44	ArrayExpress	h12: heart muscle	total_RNA	P-TABM-47	ArrayExpress	h12: heart muscle	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HH2F	A-AFFY-61	P-TABM-46	ArrayExpress	HH2F	HH2F.CEL	HH2F.EXP	heart	"Homo sapiens"	
+h13	organism_part	57	postmortem	"Homo sapiens"	male	whole_organism	h13: heart muscle	"heart muscle"	organism_part	P-TABM-44	ArrayExpress	h13: heart muscle	total_RNA	P-TABM-47	ArrayExpress	h13: heart muscle	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HH3F	A-AFFY-61	P-TABM-46	ArrayExpress	HH3F	HH3F.CEL	HH3F.EXP	heart	"Homo sapiens"	
+h14	organism_part	77	postmortem	"Homo sapiens"	male	whole_organism	h14: heart muscle	"heart muscle"	organism_part	P-TABM-44	ArrayExpress	h14: heart muscle	total_RNA	P-TABM-47	ArrayExpress	h14: heart muscle	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HH4F	A-AFFY-61	P-TABM-46	ArrayExpress	HH4F	HH4F.CEL	HH4F.EXP	heart	"Homo sapiens"	
+h15	organism_part	51	postmortem	"Homo sapiens"	male	whole_organism	h15: heart muscle	"heart muscle"	organism_part	P-TABM-44	ArrayExpress	h15: heart muscle	total_RNA	P-TABM-47	ArrayExpress	h15: heart muscle	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HH5F	A-AFFY-61	P-TABM-46	ArrayExpress	HH5F	HH5F.CEL	HH5F.EXP		"Homo sapiens"	"lymphoblastoid cell line"
+h16	organism_part	27	postmortem	"Homo sapiens"	male	whole_organism	h16: testis	testis	organism_part	P-TABM-44	ArrayExpress	h16: testis	total_RNA	P-TABM-47	ArrayExpress	h16: testis	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HT1F	A-AFFY-61	P-TABM-46	ArrayExpress	HT1F	HT1F.CEL	HT1F.EXP	testis	"Homo sapiens"	
+h17	organism_part	32	postmortem	"Homo sapiens"	male	whole_organism	h17: testis	testis	organism_part	P-TABM-44	ArrayExpress	h17: testis	total_RNA	P-TABM-47	ArrayExpress	h17: testis	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HT2F	A-AFFY-61	P-TABM-46	ArrayExpress	HT2F	HT2F.CEL	HT2F.EXP	testis	"Homo sapiens"	
+h18	organism_part	25	postmortem	"Homo sapiens"	male	whole_organism	h18: testis	testis	organism_part	P-TABM-44	ArrayExpress	h18: testis	total_RNA	P-TABM-47	ArrayExpress	h18: testis	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HT3F	A-AFFY-61	P-TABM-46	ArrayExpress	HT3F	HT3F.CEL	HT3F.EXP	testis	"Homo sapiens"	
+h19	organism_part	41	postmortem	"Homo sapiens"	male	whole_organism	h19: testis	testis	organism_part	P-TABM-44	ArrayExpress	h19: testis	total_RNA	P-TABM-47	ArrayExpress	h19: testis	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HT4F	A-AFFY-61	P-TABM-46	ArrayExpress	HT4F	HT4F.CEL	HT4F.EXP	testis	"Homo sapiens"	
+h20	organism_part	27	postmortem	"Homo sapiens"	male	whole_organism	h20: testis	testis	organism_part	P-TABM-44	ArrayExpress	h20: testis	total_RNA	P-TABM-47	ArrayExpress	h20: testis	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HT5F	A-AFFY-61	P-TABM-46	ArrayExpress	HT5F	HT5F.CEL	HT5F.EXP	testis	"Homo sapiens"	
+c01	organism_part	7	postmortem	"Pan troglodytes"	male	whole_organism	c01: brain, prefrontal cortex	"brain, prefrontal cortex"	organism_part	P-TABM-44	ArrayExpress	c01: brain, prefrontal cortex	total_RNA	P-TABM-47	ArrayExpress	c01: brain, prefrontal cortex	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CB1R	A-AFFY-67	P-TABM-46	ArrayExpress	CB1R	CB1R.CEL	CB1R.EXP	brain	"Pan troglodytes"	
+c02	organism_part	12	postmortem	"Pan troglodytes"	male	whole_organism	c02: brain, prefrontal cortex	"brain, prefrontal cortex"	organism_part	P-TABM-44	ArrayExpress	c02: brain, prefrontal cortex	total_RNA	P-TABM-47	ArrayExpress	c02: brain, prefrontal cortex	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CB2R	A-AFFY-67	P-TABM-46	ArrayExpress	CB2R	CB2R.CEL	CB2R.EXP	brain	"Pan troglodytes"	
+c03	organism_part	12	postmortem	"Pan troglodytes"	male	whole_organism	c03: brain, prefrontal cortex	"brain, prefrontal cortex"	organism_part	P-TABM-44	ArrayExpress	c03: brain, prefrontal cortex	total_RNA	P-TABM-47	ArrayExpress	c03: brain, prefrontal cortex	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CB3R	A-AFFY-67	P-TABM-46	ArrayExpress	CB3R	CB3R.CEL	CB3R.EXP	brain	"Pan troglodytes"	
+c04	organism_part	12	postmortem	"Pan troglodytes"	male	whole_organism	c04: brain, prefrontal cortex	"brain, prefrontal cortex"	organism_part	P-TABM-44	ArrayExpress	c04: brain, prefrontal cortex	total_RNA	P-TABM-47	ArrayExpress	c04: brain, prefrontal cortex	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CB4R	A-AFFY-67	P-TABM-46	ArrayExpress	CB4R	CB4R.CEL	CB4R.EXP	brain	"Pan troglodytes"	
+c05	organism_part	>40	postmortem	"Pan troglodytes"	male	whole_organism	c05: brain, prefrontal cortex	"brain, prefrontal cortex"	organism_part	P-TABM-44	ArrayExpress	c05: brain, prefrontal cortex	total_RNA	P-TABM-47	ArrayExpress	c05: brain, prefrontal cortex	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CB5R	A-AFFY-67	P-TABM-46	ArrayExpress	CB5R	CB5R.CEL	CB5R.EXP	brain	"Pan troglodytes"	
+c06	organism_part	19	postmortem	"Pan troglodytes"	female	whole_organism	c06: lymphoblastoid cell line	"lymphoblastoid cell line"	organism_part	P-TABM-44	ArrayExpress	c06: lymphoblastoid cell line	total_RNA	P-TABM-47	ArrayExpress	c06: lymphoblastoid cell line	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CC1R	A-AFFY-67	P-TABM-46	ArrayExpress	CC1R	CC1R.CEL	CC1R.EXP		"Pan troglodytes"	"lymphoblastoid cell line"
+c07	organism_part	20	postmortem	"Pan troglodytes"	female	whole_organism	c07: lymphoblastoid cell line	"lymphoblastoid cell line"	organism_part	P-TABM-44	ArrayExpress	c07: lymphoblastoid cell line	total_RNA	P-TABM-47	ArrayExpress	c07: lymphoblastoid cell line	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CC2R	A-AFFY-67	P-TABM-46	ArrayExpress	CC2R	CC2R.CEL	CC2R.EXP		"Pan troglodytes"	"lymphoblastoid cell line"
+c08	organism_part	32	postmortem	"Pan troglodytes"	female	whole_organism	c08: lymphoblastoid cell line	"lymphoblastoid cell line"	organism_part	P-TABM-44	ArrayExpress	c08: lymphoblastoid cell line	total_RNA	P-TABM-47	ArrayExpress	c08: lymphoblastoid cell line	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CC3R	A-AFFY-67	P-TABM-46	ArrayExpress	CC3R	CC3R.CEL	CC3R.EXP		"Pan troglodytes"	"lymphoblastoid cell line"
+c09	organism_part	12	postmortem	"Pan troglodytes"	female	whole_organism	c09: lymphoblastoid cell line	"lymphoblastoid cell line"	organism_part	P-TABM-44	ArrayExpress	c09: lymphoblastoid cell line	total_RNA	P-TABM-47	ArrayExpress	c09: lymphoblastoid cell line	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CC4R	A-AFFY-67	P-TABM-46	ArrayExpress	CC4R	CC4R.CEL	CC4R.EXP		"Pan troglodytes"	"lymphoblastoid cell line"
+c10	organism_part	37	postmortem	"Pan troglodytes"	female	whole_organism	c10: lymphoblastoid cell line	"lymphoblastoid cell line"	organism_part	P-TABM-44	ArrayExpress	c10: lymphoblastoid cell line	total_RNA	P-TABM-47	ArrayExpress	c10: lymphoblastoid cell line	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CC5R	A-AFFY-67	P-TABM-46	ArrayExpress	CC5R	CC5R.CEL	CC5R.EXP		"Pan troglodytes"	"lymphoblastoid cell line"
+c01	organism_part	7	postmortem	"Pan troglodytes"	male	whole_organism	c01: heart muscle	"heart muscle"	organism_part	P-TABM-44	ArrayExpress	c01: heart muscle	total_RNA	P-TABM-47	ArrayExpress	c01: heart muscle	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CH1R	A-AFFY-67	P-TABM-46	ArrayExpress	CH1R	CH1R.CEL	CH1R.EXP	heart	"Pan troglodytes"	
+c11	organism_part	34	postmortem	"Pan troglodytes"	male	whole_organism	c11: heart muscle	"heart muscle"	organism_part	P-TABM-44	ArrayExpress	c11: heart muscle	total_RNA	P-TABM-47	ArrayExpress	c11: heart muscle	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CH2R	A-AFFY-67	P-TABM-46	ArrayExpress	CH2R	CH2R.CEL	CH2R.EXP	heart	"Pan troglodytes"	
+c03	organism_part	12	postmortem	"Pan troglodytes"	male	whole_organism	c03: heart muscle	"heart muscle"	organism_part	P-TABM-44	ArrayExpress	c03: heart muscle	total_RNA	P-TABM-47	ArrayExpress	c03: heart muscle	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CH3R	A-AFFY-67	P-TABM-46	ArrayExpress	CH3R	CH3R.CEL	CH3R.EXP	heart	"Pan troglodytes"	
+c12	organism_part	26	postmortem	"Pan troglodytes"	male	whole_organism	c12: heart muscle	"heart muscle"	organism_part	P-TABM-44	ArrayExpress	c12: heart muscle	total_RNA	P-TABM-47	ArrayExpress	c12: heart muscle	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CH4R	A-AFFY-67	P-TABM-46	ArrayExpress	CH4R	CH4R.CEL	CH4R.EXP	heart	"Pan troglodytes"	
+c02	organism_part	12	postmortem	"Pan troglodytes"	male	whole_organism	c02: heart muscle	"heart muscle"	organism_part	P-TABM-44	ArrayExpress	c02: heart muscle	total_RNA	P-TABM-47	ArrayExpress	c02: heart muscle	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CH5R	A-AFFY-67	P-TABM-46	ArrayExpress	CH5R	CH5R.CEL	CH5R.EXP	heart	"Pan troglodytes"	
+c13	organism_part	20	postmortem	"Pan troglodytes"	male	whole_organism	c13: testis	testis	organism_part	P-TABM-44	ArrayExpress	c13: testis	total_RNA	P-TABM-47	ArrayExpress	c13: testis	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CT1R	A-AFFY-67	P-TABM-46	ArrayExpress	CT1R	CT1R.CEL	CT1R.EXP	testis	"Pan troglodytes"	
+c02	organism_part	12	postmortem	"Pan troglodytes"	male	whole_organism	c02: testis	testis	organism_part	P-TABM-44	ArrayExpress	c02: testis	total_RNA	P-TABM-47	ArrayExpress	c02: testis	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CT2R	A-AFFY-67	P-TABM-46	ArrayExpress	CT2R	CT2R.CEL	CT2R.EXP	testis	"Pan troglodytes"	
+c03	organism_part	12	postmortem	"Pan troglodytes"	male	whole_organism	c03: testis	testis	organism_part	P-TABM-44	ArrayExpress	c03: testis	total_RNA	P-TABM-47	ArrayExpress	c03: testis	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CT3R	A-AFFY-67	P-TABM-46	ArrayExpress	CT3R	CT3R.CEL	CT3R.EXP	testis	"Pan troglodytes"	
+c04	organism_part	12	postmortem	"Pan troglodytes"	male	whole_organism	c04: testis	testis	organism_part	P-TABM-44	ArrayExpress	c04: testis	total_RNA	P-TABM-47	ArrayExpress	c04: testis	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CT4R	A-AFFY-67	P-TABM-46	ArrayExpress	CT4R	CT4R.CEL	CT4R.EXP	testis	"Pan troglodytes"	
+c05	organism_part	>40	postmortem	"Pan troglodytes"	male	whole_organism	c05: testis	testis	organism_part	P-TABM-44	ArrayExpress	c05: testis	total_RNA	P-TABM-47	ArrayExpress	c05: testis	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	CT5R	A-AFFY-67	P-TABM-46	ArrayExpress	CT5R	CT5R.CEL	CT5R.EXP	testis	"Pan troglodytes"	
+h01	organism_part	45	postmortem	"Homo sapiens"	male	whole_organism	h01: brain, prefrontal cortex	"brain, prefrontal cortex"	organism_part	P-TABM-44	ArrayExpress	h01: brain, prefrontal cortex	total_RNA	P-TABM-47	ArrayExpress	h01: brain, prefrontal cortex	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HB1R	A-AFFY-67	P-TABM-46	ArrayExpress	HB1R	HB1R.CEL	HB1R.EXP	brain	"Homo sapiens"	
+h02	organism_part	45	postmortem	"Homo sapiens"	male	whole_organism	h02: brain, prefrontal cortex	"brain, prefrontal cortex"	organism_part	P-TABM-44	ArrayExpress	h02: brain, prefrontal cortex	total_RNA	P-TABM-47	ArrayExpress	h02: brain, prefrontal cortex	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HB2R	A-AFFY-67	P-TABM-46	ArrayExpress	HB2R	HB2R.CEL	HB2R.EXP	brain	"Homo sapiens"	
+h03	organism_part	48	postmortem	"Homo sapiens"	male	whole_organism	h03: brain, prefrontal cortex	"brain, prefrontal cortex"	organism_part	P-TABM-44	ArrayExpress	h03: brain, prefrontal cortex	total_RNA	P-TABM-47	ArrayExpress	h03: brain, prefrontal cortex	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HB3R	A-AFFY-67	P-TABM-46	ArrayExpress	HB3R	HB3R.CEL	HB3R.EXP	brain	"Homo sapiens"	
+h04	organism_part	56	postmortem	"Homo sapiens"	male	whole_organism	h04: brain, prefrontal cortex	"brain, prefrontal cortex"	organism_part	P-TABM-44	ArrayExpress	h04: brain, prefrontal cortex	total_RNA	P-TABM-47	ArrayExpress	h04: brain, prefrontal cortex	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HB4R	A-AFFY-67	P-TABM-46	ArrayExpress	HB4R	HB4R.CEL	HB4R.EXP	brain	"Homo sapiens"	
+h05	organism_part	67	postmortem	"Homo sapiens"	male	whole_organism	h05: brain, prefrontal cortex	"brain, prefrontal cortex"	organism_part	P-TABM-44	ArrayExpress	h05: brain, prefrontal cortex	total_RNA	P-TABM-47	ArrayExpress	h05: brain, prefrontal cortex	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HB5R	A-AFFY-67	P-TABM-46	ArrayExpress	HB5R	HB5R.CEL	HB5R.EXP	brain	"Homo sapiens"	
+h06	organism_part	13	postmortem	"Homo sapiens"	female	whole_organism	h06: lymphoblastoid cell line	"lymphoblastoid cell line"	organism_part	P-TABM-44	ArrayExpress	h06: lymphoblastoid cell line	total_RNA	P-TABM-47	ArrayExpress	h06: lymphoblastoid cell line	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HC1R	A-AFFY-67	P-TABM-46	ArrayExpress	HC1R	HC1R.CEL	HC1R.EXP		"Homo sapiens"	"lymphoblastoid cell line"
+h07	organism_part	12	postmortem	"Homo sapiens"	female	whole_organism	h07: lymphoblastoid cell line	"lymphoblastoid cell line"	organism_part	P-TABM-44	ArrayExpress	h07: lymphoblastoid cell line	total_RNA	P-TABM-47	ArrayExpress	h07: lymphoblastoid cell line	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HC2R	A-AFFY-67	P-TABM-46	ArrayExpress	HC2R	HC2R.CEL	HC2R.EXP		"Homo sapiens"	"lymphoblastoid cell line"
+h08	organism_part	20	postmortem	"Homo sapiens"	female	whole_organism	h08: lymphoblastoid cell line	"lymphoblastoid cell line"	organism_part	P-TABM-44	ArrayExpress	h08: lymphoblastoid cell line	total_RNA	P-TABM-47	ArrayExpress	h08: lymphoblastoid cell line	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HC3R	A-AFFY-67	P-TABM-46	ArrayExpress	HC3R	HC3R.CEL	HC3R.EXP		"Homo sapiens"	"lymphoblastoid cell line"
+h09	organism_part	22	postmortem	"Homo sapiens"	female	whole_organism	h09: lymphoblastoid cell line	"lymphoblastoid cell line"	organism_part	P-TABM-44	ArrayExpress	h09: lymphoblastoid cell line	total_RNA	P-TABM-47	ArrayExpress	h09: lymphoblastoid cell line	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HC4R	A-AFFY-67	P-TABM-46	ArrayExpress	HC4R	HC4R.CEL	HC4R.EXP		"Homo sapiens"	"lymphoblastoid cell line"
+h10	organism_part	32	postmortem	"Homo sapiens"	female	whole_organism	h10: lymphoblastoid cell line	"lymphoblastoid cell line"	organism_part	P-TABM-44	ArrayExpress	h10: lymphoblastoid cell line	total_RNA	P-TABM-47	ArrayExpress	h10: lymphoblastoid cell line	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HC5R	A-AFFY-67	P-TABM-46	ArrayExpress	HC5R	HC5R.CEL	HC5R.EXP		"Homo sapiens"	"lymphoblastoid cell line"
+h11	organism_part	71	postmortem	"Homo sapiens"	male	whole_organism	h11: heart muscle	"heart muscle"	organism_part	P-TABM-44	ArrayExpress	h11: heart muscle	total_RNA	P-TABM-47	ArrayExpress	h11: heart muscle	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HH1R	A-AFFY-67	P-TABM-46	ArrayExpress	HH1R	HH1R.CEL	HH1R.EXP	heart	"Homo sapiens"	
+h12	organism_part	74	postmortem	"Homo sapiens"	male	whole_organism	h12: heart muscle	"heart muscle"	organism_part	P-TABM-44	ArrayExpress	h12: heart muscle	total_RNA	P-TABM-47	ArrayExpress	h12: heart muscle	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HH2R	A-AFFY-67	P-TABM-46	ArrayExpress	HH2R	HH2R.CEL	HH2R.EXP	heart	"Homo sapiens"	
+h13	organism_part	57	postmortem	"Homo sapiens"	male	whole_organism	h13: heart muscle	"heart muscle"	organism_part	P-TABM-44	ArrayExpress	h13: heart muscle	total_RNA	P-TABM-47	ArrayExpress	h13: heart muscle	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HH3R	A-AFFY-67	P-TABM-46	ArrayExpress	HH3R	HH3R.CEL	HH3R.EXP	heart	"Homo sapiens"	
+h14	organism_part	77	postmortem	"Homo sapiens"	male	whole_organism	h14: heart muscle	"heart muscle"	organism_part	P-TABM-44	ArrayExpress	h14: heart muscle	total_RNA	P-TABM-47	ArrayExpress	h14: heart muscle	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HH4R	A-AFFY-67	P-TABM-46	ArrayExpress	HH4R	HH4R.CEL	HH4R.EXP	heart	"Homo sapiens"	
+h15	organism_part	51	postmortem	"Homo sapiens"	male	whole_organism	h15: heart muscle	"heart muscle"	organism_part	P-TABM-44	ArrayExpress	h15: heart muscle	total_RNA	P-TABM-47	ArrayExpress	h15: heart muscle	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HH5R	A-AFFY-67	P-TABM-46	ArrayExpress	HH5R	HH5R.CEL	HH5R.EXP	heart	"Homo sapiens"	
+h16	organism_part	27	postmortem	"Homo sapiens"	male	whole_organism	h16: testis	testis	organism_part	P-TABM-44	ArrayExpress	h16: testis	total_RNA	P-TABM-47	ArrayExpress	h16: testis	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HT1R	A-AFFY-67	P-TABM-46	ArrayExpress	HT1R	HT1R.CEL	HT1R.EXP	testis	"Homo sapiens"	
+h17	organism_part	32	postmortem	"Homo sapiens"	male	whole_organism	h17: testis	testis	organism_part	P-TABM-44	ArrayExpress	h17: testis	total_RNA	P-TABM-47	ArrayExpress	h17: testis	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HT2R	A-AFFY-67	P-TABM-46	ArrayExpress	HT2R	HT2R.CEL	HT2R.EXP	testis	"Homo sapiens"	
+h18	organism_part	25	postmortem	"Homo sapiens"	male	whole_organism	h18: testis	testis	organism_part	P-TABM-44	ArrayExpress	h18: testis	total_RNA	P-TABM-47	ArrayExpress	h18: testis	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HT3R	A-AFFY-67	P-TABM-46	ArrayExpress	HT3R	HT3R.CEL	HT3R.EXP	testis	"Homo sapiens"	
+h19	organism_part	41	postmortem	"Homo sapiens"	male	whole_organism	h19: testis	testis	organism_part	P-TABM-44	ArrayExpress	h19: testis	total_RNA	P-TABM-47	ArrayExpress	h19: testis	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HT4R	A-AFFY-67	P-TABM-46	ArrayExpress	HT4R	HT4R.CEL	HT4R.EXP	testis	"Homo sapiens"	
+h20	organism_part	27	postmortem	"Homo sapiens"	male	whole_organism	h20: testis	testis	organism_part	P-TABM-44	ArrayExpress	h20: testis	total_RNA	P-TABM-47	ArrayExpress	h20: testis	synthetic_DNA	biotin	Hybridization-MES_EukGE-WS2v5_450-DEV	HT5R	A-AFFY-67	P-TABM-46	ArrayExpress	HT5R	HT5R.CEL	HT5R.EXP	testis	"Homo sapiens"	
diff --git a/examples/magetab/real/E-TABM-140.idf b/examples/magetab/real/E-TABM-140.idf
new file mode 100644
index 0000000..33fb7ae
--- /dev/null
+++ b/examples/magetab/real/E-TABM-140.idf
@@ -0,0 +1,32 @@
+"Investigation Title"	"Chromatin immunoprecipitation microarray (ChIP-chip) study using human erythroleukemia cell line K-562 and H3K4me2 (ab7766); H3K4me3 (ab8580); H3ac (06-599); H4ac (06-866): Histone H2B(ab1790) and Histone H3 (ab1791) antibodies (ENCODE)."						
+"Experimental Design"	binding_site_identification_design						
+"Experimental Factor Name"	STRAINORLINE	IMMUNOPRECIPITATE					
+"Experimental Factor Type"	strain_or_line	immunoprecipitate					
+							
+"Person Last Name"	Vetrie						
+"Person First Name"	David						
+"Person Mid Initial"							
+"Person Email"	scd at sanger.ac.uk						
+"Person Address"	"Wellcome Trust Genome Campus;Hinxton; Cambridge; CB10 1SA; UK"						
+"Person Affiliation"	"The Wellcome Trust Sanger Institute"						
+"Person Roles"	investigator;submitter						
+							
+"Quality Control Types"	biological_replicate						
+"Public Release Date"	"2006-09-01"						
+							
+Comment[ArrayExpressSubmissionDate]	"2006-08-18"						
+							
+"Publication Author List"	"Christoph M. Koch; Robert M. Andrews; Paul Flicek; Shane C. Dillon; Ula Kara�z; Gayle K. Clelland; Sarah Wilcox; David M. Beare; Joanna C. Fowler; Phillippe Couttet; Keith D. James; Gregory C. Lefebvre; Alexander W. Bruce; Oliver M. Dovey; Peter D. Ellis; Pawandeep Dhami; Cordelia F. Langford; Zhiping Weng; Ewan Birney; Nigel P. Carter; David Vetrie; Ian Dunham"						
+"Publication Title"	"The Landscape of Activating Histone Modifications across 1% of the Human Genome."						
+"Experiment Description"	"ENCODE ChIP-chip study using human myelogenous leukemia cell line K-562 and anti histone H3K4me2 (Abcam; ab7766); H3K4me3 (Abcam; ab8580); H3ac (Upstate; 06-599); H4ac (Upstate; 06-866); Histone H2B (Abcam: ab1790) and Histone H3 (Abcam: ab1791) antibodies. Each antibody experiment was conducted in three biological replicates, with two technical replicates performed for each biological replicate"						
+							
+"Protocol Name"	GROW_K-562	CROSSLINK_K-562_1	CROSSLINK_K-562_2	CHROMATIN_IMMUNOPRECIPITATION	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION
+"Protocol Type"	grow	specified_biomaterial_action	specified_biomaterial_action	nucleic_acid_extraction	hybridization	image_acquisition	bioassay_data_transformation
+"Protocol Description"	"Cells were grown in DMEM supplemented with 10% FBS and 1% Penicillin/Streptomycin to a total number of ~ 3x10^8 cells. Dilute cell density to 3 x10^5/ml every two to three days. Split and feed 16 hours before cross-linking the cells."	"Day 1 Cross linking cells and chromatin extraction/preparation<br>1. Grow or harvest 1x10^8 cells. Collect the cells by centrifuging at 1200 rpm for 5-8 minutes.<br>2. Resuspend the cells in pre-warmed 50 ml serum free media in a gl [...]
+"Protocol Parameters"							
+"Protocol Software"							
+							
+"SDRF File"	E-TABM-140_sdrf.txt						
+							
+"Term Source Name"	ArrayExpress						
+"Term Source File"	http://www.ebi.ac.uk/arrayexpress/						
diff --git a/examples/magetab/real/E-TABM-140_sdrf.txt b/examples/magetab/real/E-TABM-140_sdrf.txt
new file mode 100644
index 0000000..8defa7c
--- /dev/null
+++ b/examples/magetab/real/E-TABM-140_sdrf.txt
@@ -0,0 +1,73 @@
+"Source Name"	"Material Type"	"Characteristics [BioSourceType]"	"Characteristics [CellType]"	"Characteristics [Organism]"	"Characteristics [Sex]"	"Characteristics [StrainOrLine]"	"Growth Protocol REF"	"Chromatin Extraction Protocol REF"	"Sample Name"	"Material Type"	"Immunoprecipitation Protocol REF"	"Extract Name"	"Material Type"	"Labeling Protocol REF"	Term Source REF	"Labeled Extract Name"	"Material Type"	Label	"Hybridization Protocol REF"	"Hybridization Name"	"Array Design REF"	"Scan [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP1	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP1_techrep1	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP1 with anti H3K4me2 (Abcam, ab7766) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP1_techrep1 ChIP H3K4me2 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1106-16_raw	1106-16_raw.tx [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP1	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP1_techrep1	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP1 Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP1_techrep1 ChIP H3K4me2 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1106-16_raw	1106-16_raw.txt	TRANSFORMATION	FG [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP1	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP1_techrep2	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP1 with anti H3K4me2 (Abcam, ab7766) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP1_techrep2 ChIP H3K4me2 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1106-17_raw	1106-17_raw.tx [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP1	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP1_techrep2	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP1 Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP1_techrep2 ChIP H3K4me2 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1106-17_raw	1106-17_raw.txt	TRANSFORMATION	FG [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP2	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP2_techrep1	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP2 with anti H3K4me2 (Abcam, ab7766) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP2_techrep1 ChIP H3K4me2 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1140-6_raw	1140-6_raw.txt	 [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP2	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP2_techrep1	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP2 Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP2_techrep1 ChIP H3K4me2 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1140-6_raw	1140-6_raw.txt	TRANSFORMATION	FGED [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP2	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP2_techrep2	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP2 with anti H3K4me2 (Abcam, ab7766) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP2_techrep2 ChIP H3K4me2 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1140-7_raw	1140-7_raw.txt	 [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP2	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP2_techrep2	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP2 Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP2_techrep2 ChIP H3K4me2 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1140-7_raw	1140-7_raw.txt	TRANSFORMATION	FGED [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP3	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP3_techrep1	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP3 with anti H3K4me2 (Abcam, ab7766) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP3_techrep1 ChIP H3K4me2 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1140-12_raw	1140-12_raw.tx [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP3	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP3_techrep1	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP3 Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP3_techrep1 ChIP H3K4me2 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1140-12_raw	1140-12_raw.txt	TRANSFORMATION	FG [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP3	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP3_techrep2	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP3 with anti H3K4me2 (Abcam, ab7766) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP3_techrep2 ChIP H3K4me2 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1140-13_raw	1140-13_raw.tx [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP3	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP3_techrep2	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP3 Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP3_techrep2 ChIP H3K4me2 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1140-13_raw	1140-13_raw.txt	TRANSFORMATION	FG [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP1	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP1_techrep1	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP1 with anti H3K4me3 (Abcam, ab8895) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP1_techrep1 ChIP H3K4me3 hyb"	A-MEXP-156	SCANARRAYEXPRESS-2	1023-16_raw	1023-16_raw.tx [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP1	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP1_techrep1	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP1 Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP1_techrep1 ChIP H3K4me3 hyb"	A-MEXP-156	SCANARRAYEXPRESS-2	1023-16_raw	1023-16_raw.txt	TRANSFORMATION	FG [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP1	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP1_techrep2	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP1 with anti H3K4me3 (Abcam, ab8895) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP1_techrep2 ChIP H3K4me3 hyb"	A-MEXP-156	SCANARRAYEXPRESS-2	1023-17_raw	1023-17_raw.tx [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP1	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP1_techrep2	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP1 Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP1_techrep2 ChIP H3K4me3 hyb"	A-MEXP-156	SCANARRAYEXPRESS-2	1023-17_raw	1023-17_raw.txt	TRANSFORMATION	FG [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP2	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP2_techrep1	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP2 with anti H3K4me3 (Abcam, ab8895) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP2_techrep1 ChIP H3K4me3 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1106-10_raw	1106-10_raw.tx [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP2	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP2_techrep1	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP2 Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP2_techrep1 ChIP H3K4me3 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1106-10_raw	1106-10_raw.txt	TRANSFORMATION	FG [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP2	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP2_techrep2	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP2 with anti H3K4me3 (Abcam, ab8895) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP2_techrep2 ChIP H3K4me3 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1106-11_raw	1106-11_raw.tx [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP2	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP2_techrep2	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP2 Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP2_techrep2 ChIP H3K4me3 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1106-11_raw	1106-11_raw.txt	TRANSFORMATION	FG [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP3	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP3_techrep1	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP3 with anti H3K4me3 (Abcam, ab8895) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP3_techrep1 ChIP H3K4me3 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1163-8_raw	1163-8_raw.txt	 [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP3	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP3_techrep1	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP3 Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP3_techrep1 ChIP H3K4me3 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1163-8_raw	1163-8_raw.txt	TRANSFORMATION	FGED [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP3	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP3_techrep2	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP3 with anti H3K4me3 (Abcam, ab8895) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP3_techrep2 ChIP H3K4me3 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1163-9_raw	1163-9_raw.txt	 [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP3	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP3_techrep2	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP3 Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP3_techrep2 ChIP H3K4me3 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1163-9_raw	1163-9_raw.txt	TRANSFORMATION	FGED [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP1	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP1_techrep1	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP1 with anti H3ac (Upstate, 06-599) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP1_techrep1 ChIP H3ac hyb"	A-MEXP-156	SCANARRAYEXPRESS-2	1023-13_raw	1023-13_raw.txt	TR [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP1	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP1_techrep1	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP1 Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP1_techrep1 ChIP H3ac hyb"	A-MEXP-156	SCANARRAYEXPRESS-2	1023-13_raw	1023-13_raw.txt	TRANSFORMATION	FGEDM [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP1	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP1_techrep2	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP1 with anti H3ac (Upstate, 06-599) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP1_techrep2 ChIP H3ac hyb"	A-MEXP-156	SCANARRAYEXPRESS-2	1023-18_raw	1023-18_raw.txt	TR [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP1	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP1_techrep2	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP1 Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP1_techrep2 ChIP H3ac hyb"	A-MEXP-156	SCANARRAYEXPRESS-2	1023-18_raw	1023-18_raw.txt	TRANSFORMATION	FGEDM [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP2	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP2_techrep1	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP2 with anti H3ac (Upstate, 06-599) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP2_techrep1 ChIP H3ac hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1106-2_raw	1106-2_raw.txt	TRAN [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP2	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP2_techrep1	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP2 Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP2_techrep1 ChIP H3ac hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1106-2_raw	1106-2_raw.txt	TRANSFORMATION	FGEDM_c [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP2	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP2_techrep2	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP2 with anti H3ac (Upstate, 06-599) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP2_techrep2 ChIP H3ac hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1106-3_raw	1106-3_raw.txt	TRAN [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP2	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP2_techrep2	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP2 Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP2_techrep2 ChIP H3ac hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1106-3_raw	1106-3_raw.txt	TRANSFORMATION	FGEDM_c [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP3	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP3_techrep1	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP3 with anti H3ac (Upstate, 06-599) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP3_techrep1 ChIP H3ac hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1140-2_raw	1140-2_raw.txt	TRAN [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP3	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP3_techrep1	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP3 Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP3_techrep1 ChIP H3ac hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1140-2_raw	1140-2_raw.txt	TRANSFORMATION	FGEDM_c [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP3	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP3_techrep2	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP3 with anti H3ac (Upstate, 06-599) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP3_techrep2 ChIP H3ac hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1140-3_raw	1140-3_raw.txt	TRAN [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP3	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP3_techrep2	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP3 Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP3_techrep2 ChIP H3ac hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1140-3_raw	1140-3_raw.txt	TRANSFORMATION	FGEDM_c [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP1	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP1_techrep1	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP1 with anti H4ac (Upstate, 06-866) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP1_techrep1 ChIP H4ac hyb"	A-MEXP-156	SCANARRAYEXPRESS-2	1043-6_raw	1043-6_raw.txt	TRAN [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP1	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP1_techrep1	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP1 Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP1_techrep1 ChIP H4ac hyb"	A-MEXP-156	SCANARRAYEXPRESS-2	1043-6_raw	1043-6_raw.txt	TRANSFORMATION	FGEDM_c [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP1	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP1_techrep2	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP1 with anti H4ac (Upstate, 06-866) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP1_techrep2 ChIP H4ac hyb"	A-MEXP-156	SCANARRAYEXPRESS-2	1043-7_raw	1043-7_raw.txt	TRAN [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP1	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP1_techrep2	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP1 Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP1_techrep2 ChIP H4ac hyb"	A-MEXP-156	SCANARRAYEXPRESS-2	1043-7_raw	1043-7_raw.txt	TRANSFORMATION	FGEDM_c [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP2	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP2_techrep1	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP2 with anti H4ac (Upstate, 06-866) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP2_techrep1 ChIP H4ac hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1106-4_raw	1106-4_raw.txt	TRAN [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP2	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP2_techrep1	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP2 Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP2_techrep1 ChIP H4ac hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1106-4_raw	1106-4_raw.txt	TRANSFORMATION	FGEDM_c [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP2	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP2_techrep2	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP2 with anti H4ac (Upstate, 06-866) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP2_techrep2 ChIP H4ac hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1106-5_raw	1106-5_raw.txt	TRAN [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP2	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP2_techrep2	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP2 Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP2_techrep2 ChIP H4ac hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1106-5_raw	1106-5_raw.txt	TRANSFORMATION	FGEDM_c [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP3	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP3_techrep1	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP3 with anti H4ac (Upstate, 06-866) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP3_techrep1 ChIP H4ac hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1140-20_raw	1140-20_raw.txt	TR [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP3	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP3_techrep1	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP3 Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP3_techrep1 ChIP H4ac hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1140-20_raw	1140-20_raw.txt	TRANSFORMATION	FGEDM [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP3	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP3_techrep2	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP3 with anti H4ac (Upstate, 06-866) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP3_techrep2 ChIP H4ac hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1163-2_raw	1163-2_raw.txt	TRAN [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_1	K-562_BIOREP3	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP3_techrep2	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP3 Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP3_techrep2 ChIP H4ac hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1163-2_raw	1163-2_raw.txt	TRANSFORMATION	FGEDM_c [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_2	K-562_BIOREP1a	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP1a_techrep1a	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP1a with anti H2B (Abcam, ab1790) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP1a_techrep1a ChIP H2B hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1604-84_raw	1604-84_raw.txt	 [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_2	K-562_BIOREP1a	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP1a_techrep1a	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP1a Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP1a_techrep1a ChIP H2B hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1604-84_raw	1604-84_raw.txt	TRANSFORMATION	 [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_2	K-562_BIOREP1a	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP1a_techrep2a	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP1a with anti H2B (Abcam, ab1790) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP1a_techrep2a ChIP H2B hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1604-89_raw	1604-89_raw.txt	 [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_2	K-562_BIOREP1a	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP1a_techrep2a	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP1a Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP1a_techrep2a ChIP H2B hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1604-89_raw	1604-89_raw.txt	TRANSFORMATION	 [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_2	K-562_BIOREP2a	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP2a_techrep1a	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP2a with anti H2B (Abcam, ab1790) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP2a_techrep1a ChIP H2B hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1604-97_raw	1604-97_raw.txt	 [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_2	K-562_BIOREP2a	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP2a_techrep1a	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP2a Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP2a_techrep1a ChIP H2B hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1604-97_raw	1604-97_raw.txt	TRANSFORMATION	 [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_2	K-562_BIOREP2a	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP2a_techrep2a	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP2a with anti H2B (Abcam, ab1790) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP2a_techrep2a ChIP H2B hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1604-98_raw	1604-98_raw.txt	 [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_2	K-562_BIOREP2a	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP2a_techrep2a	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP2a Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP2a_techrep2a ChIP H2B hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1604-98_raw	1604-98_raw.txt	TRANSFORMATION	 [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_2	K-562_BIOREP3a	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP3a_techrep1a	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP3a with anti H2B (Abcam, ab1790) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP3a_techrep1a ChIP H2B hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1604-88_raw	1604-88_raw.txt	 [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_2	K-562_BIOREP3a	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP3a_techrep1a	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP3a Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP3a_techrep1a ChIP H2B hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1604-88_raw	1604-88_raw.txt	TRANSFORMATION	 [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_2	K-562_BIOREP3a	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP3a_techrep2a	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP3a with anti H2B (Abcam, ab1790) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP3a_techrep2a ChIP H2B hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1604-99_raw	1604-99_raw.txt	 [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_2	K-562_BIOREP3a	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP3a_techrep2a	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP3a Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP3a_techrep2a ChIP H2B hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1604-99_raw	1604-99_raw.txt	TRANSFORMATION	 [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_2	K-562_BIOREP1a	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP1a_techrep1a	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP1a with anti H3 (Upstate, ab1791) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP1a_techrep1a ChIP H3 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1604-85_raw	1604-85_raw.txt	 [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_2	K-562_BIOREP1a	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP1a_techrep1a	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP1a Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP1a_techrep1a ChIP H3 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1604-85_raw	1604-85_raw.txt	TRANSFORMATION	F [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_2	K-562_BIOREP1a	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP1a_techrep2a	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP1a with anti H3 (Upstate, ab1791) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP1a_techrep2a ChIP H3 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1604-91_raw	1604-91_raw.txt	 [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_2	K-562_BIOREP1a	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP1a_techrep2a	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP1a Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP1a_techrep2a ChIP H3 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1604-91_raw	1604-91_raw.txt	TRANSFORMATION	F [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_2	K-562_BIOREP2a	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP2a_techrep1a	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP2a with anti H3 (Upstate, ab1791) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP2a_techrep1a ChIP H3 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1604-95_raw	1604-95_raw.txt	 [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_2	K-562_BIOREP2a	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP2a_techrep1a	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP2a Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP2a_techrep1a ChIP H3 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1604-95_raw	1604-95_raw.txt	TRANSFORMATION	F [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_2	K-562_BIOREP2a	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP2a_techrep2a	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP2a with anti H3 (Upstate, ab1791) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP2a_techrep2a ChIP H3 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1604-96_raw	1604-96_raw.txt	 [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_2	K-562_BIOREP2a	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP2a_techrep2a	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP2a Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP2a_techrep2a ChIP H3 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1604-96_raw	1604-96_raw.txt	TRANSFORMATION	F [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_2	K-562_BIOREP3a	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP3a_techrep1a	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP3a with anti H3 (Upstate, ab1791) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP3a_techrep1a ChIP H3 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1604-90_raw	1604-90_raw.txt	 [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_2	K-562_BIOREP3a	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP3a_techrep1a	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP3a Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP3a_techrep1a ChIP H3 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1604-90_raw	1604-90_raw.txt	TRANSFORMATION	F [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_2	K-562_BIOREP3a	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP3a_techrep2a	genomic_DNA	P-MEXP-7423	ArrayExpress	"IP of K-562_BIOREP3a with anti H3 (Upstate, ab1791) Cy3"	synthetic_DNA	Cy3	HYBRIDISATION_TECAN	"K-562 BIOREP3a_techrep2a ChIP H3 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1604-94_raw	1604-94_raw.txt	 [...]
+K-562	cell	frozen_sample	"human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis"	"Homo sapiens"	female	K-562	GROW_K-562	CROSSLINK_K-562_2	K-562_BIOREP3a	genomic_DNA	CHROMATIN_IMMUNOPRECIPITATION	K-562_BIOREP3a_techrep2a	genomic_DNA	P-MEXP-7422	ArrayExpress	"Input control DNA of K-562_BIOREP3a Cy5"	synthetic_DNA	Cy5	HYBRIDISATION_TECAN	"K-562 BIOREP3a_techrep2a ChIP H3 hyb"	A-MEXP-215	SCANARRAYEXPRESS-2	1604-94_raw	1604-94_raw.txt	TRANSFORMATION	F [...]
diff --git a/examples/magetab/real/E-TABM-16.idf b/examples/magetab/real/E-TABM-16.idf
new file mode 100644
index 0000000..d3db90f
--- /dev/null
+++ b/examples/magetab/real/E-TABM-16.idf
@@ -0,0 +1,26 @@
+"Investigation Title"	"FDA-CDER MTS RNA reagent cross platform test"																														
+"Experimental Design"	"quality_control_testing_design"																														
+"Experimental Factor Name"	"PLATFORM"	"INDIVIDUAL"	"MIX"	"OPERATOR"																											
+"Experimental Factor Type"	"platform"	"individual"	"mix"	"operator"																											
+																															
+"Person Last Name"	"Pine"																														
+"Person First Name"	"Scott"																														
+"Person Mid Initial"																															
+"Person Affiliation"	"FDA-CDER"																														
+"Person Roles"	"submitter"																														
+																															
+"Public Release Date"	"2005-11-23"																														
+																															
+"Comment[ArrayExpressSubmissionDate]"	"2005-06-01"																														
+																															
+"Publication Author List"																															
+"Publication Title"																															
+"Experiment Description"	"This experiment is a test of a reagent that can provide a basis for performance assessments on Affymetrix RAE230A,  CodeLink UniSet Rat I, and Agilent G4130A rat oligonucleotide arrays.   The reagent is a set of two mixed tissue RNA samples formulated to contain different ratios of RNA for each of four rat tissues (brain, liver, kidney, and testes).   The analytes in this assay bind to a subset of gene probes that were matched across platforms to the same UniGen [...]
+																															
+"Protocol Name"	6377	6380	"6396-mix1"	"6396-mix2"	6407	6409	6486	6489	6491	6552	6553	6672	6673	"6841-mix1"	"6841-mix2"	7671	7672	8188	8189	8190	8309	8532	8534	8535	8536	8537	8538	8573	8588	8589	8590
+"Protocol Type"	"grow"	"nucleic_acid_extraction"	"pool"	"pool"	"image_aquisition"	"image_aquisition"	"labeling"	"labeling"	"hybridization"	"bioassay_data_transformation"	"bioassay_data_transformation"	"nucleic_acid_extraction"	"grow"	"pool"	"pool"	"labeling"	"labeling"	"hybridization"	"hybridization"	"image_aquisition"	"image_aquisition"	"labeling"	"labeling"	"labeling"	"labeling"	"labeling"	"labeling"	"hybridization"	"hybridization"	"hybridization"	"image_aquisition"
+"Protocol Description"	"Rodents received certified rodent diet #5002C ad lib and drinking water purified by reverse osmosis.   The animals were acclimated for 6 days prior to euthanasia.   The animals were on a 12 hr light/dark cycle and euthanasia was performed consistently within 4-6  hr after the start of the light cycle. Each batch was made up of 8 animals from the same shipment. "	"After euthanasia in a slow charged CO2 chamber, the rats were immediately decapitated to allow for rap [...]
+"Protocol Parameters"																															
+"Protocol Software"																															
+																															
+"SDRF File"	"E-TABM-16_sdrf.txt"																														
diff --git a/examples/magetab/real/E-TABM-163.idf b/examples/magetab/real/E-TABM-163.idf
new file mode 100644
index 0000000..d7be75d
--- /dev/null
+++ b/examples/magetab/real/E-TABM-163.idf
@@ -0,0 +1,27 @@
+"Investigation Title"	"Identification of cyclic genes of the mouse segmentation clock"						
+"Experimental Design"	time_series_design	development_or_differentiation_design	co_expression_design				
+"Experimental Factor Name"	ORGANISMPART	INDIVIDUAL	TIME				
+"Experimental Factor Type"	organism_part	individual	time				
+							
+"Person Last Name"	"Dequツant"						
+"Person First Name"	Mary-Lee						
+"Person Mid Initial"							
+"Person Affiliation"	"Stowers Institute For Medical Research"						
+"Person Roles"	submitter						
+							
+"Quality Control Types"	biological_replicate						
+"Public Release Date"	"2006-10-30"						
+							
+Comment[ArrayExpressSubmissionDate]	"2006-10-30"						
+							
+"Publication Author List"	"Mary-Lee Dequツant, Earl Glynn, Karin Gaudenz, Matthias Wahl, Jie Chen, Arcady Mushegian, Olivier Pourquiツ"						
+"Publication Title"	"A complex oscillating network of signaling genes underlies the mouse segmentation clock"						
+"Experiment Description"	"A microarray time series was generated to identify cyclic genes of the segmentation clock in the mouse. The right posterior half presomitic mesoderms (PSM) from 17 mouse embryos were dissected while the contralateral side of the embryo containing the left PSM was immediately fixed to be analyzed by in situ hybridization using a Lfng probe to order the samples along the segmentation clock oscillation cycle. Probes were produced from RNA extracted from the 17 diss [...]
+							
+"Protocol Name"	P-MLD6-1	P-MLD6-2	P-MLD6-3	P-MLD6-4	P-MLD6-5	P-MLD6-6	P-MLD6-7
+"Protocol Type"	specified_biomaterial_action	nucleic_acid_extraction	labeling	hybridization	image_acquisition	feature_extraction	bioassay_data_transformation
+"Protocol Description"	"CD-1 mouse embryos (9.0 days post-coitus [dpc]) were dissected in cold Tyrode's solution prepared in RNAse-free conditions. Mouse embryos from each litter were dissected one by one, while the rest of the embryos were kept on ice. No more than three embryos per litter were dissected in order to limit the time between removal of the uterus and embryo dissection to reduce the risk of RNA degradation. The time spent for dissection per embryo was limited to 15-20 min.  [...]
+"Protocol Parameters"							
+"Protocol Software"							
+							
+"SDRF File"	E-TABM-163_sdrf.txt						
diff --git a/examples/magetab/real/E-TABM-163_sdrf.txt b/examples/magetab/real/E-TABM-163_sdrf.txt
new file mode 100644
index 0000000..a44d562
--- /dev/null
+++ b/examples/magetab/real/E-TABM-163_sdrf.txt
@@ -0,0 +1,24 @@
+"Source Name"	"Material Type"	"Characteristics [Age(days)]"	"Characteristics [DevelopmentalStage]"	"Characteristics [Individual]"	"Characteristics [OrganismPart]"	"Characteristics [Organism]"	"Characteristics [StrainOrLine]"	"Protocol REF"	"Sample Name"	"Material Type"	"Protocol REF"	"Extract Name"	"Material Type"	"Protocol REF"	"Labeled Extract Name"	"Material Type"	Label	"Protocol REF"	"Hybridization Name"	"Array Design REF"	"Protocol REF"	"Scan Name"	"Protocol REF"	"Array Data File"	C [...]
+"Charles River # 022"	organism_part	9.5	embryo	"Embryo 1"	"right or left presomitic mesoderm (posterior half)(a)"	"Mus musculus"	CD-1	P-MLD6-1	"Charles River # 022"	organism_part	P-MLD6-2	"Charles River # 022"	total_RNA	P-MLD6-3	"Charles River # 022 biotin"	synthetic_RNA	biotin	P-MLD6-4	Emb1a-5D2_MGU74Av2	A-AFFY-6	P-MLD6-5	Emb1a-5D2_MGU74Av2	P-MLD6-6	Emb1a-5D2_MGU74Av2.CEL	Emb1a-5D2_MGU74Av2.EXP	P-MLD6-7	Emb1a-5D2_MGU74Av2	Emb1a-5D2_MGU74Av2.CHP	MG_U74Av2.CDF	"Embryo 1"	"right or left pr [...]
+"Charles River # 023"	organism_part	9.5	embryo	"Embryo 1"	"right or left presomitic mesoderm (posterior half)(b)"	"Mus musculus"	CD-1	P-MLD6-1	"Charles River # 023"	organism_part	P-MLD6-2	"Charles River # 023"	total_RNA	P-MLD6-3	"Charles River # 023 biotin"	synthetic_RNA	biotin	P-MLD6-4	Emb1b-5G1_MGU74Av2	A-AFFY-6	P-MLD6-5	Emb1b-5G1_MGU74Av2	P-MLD6-6	Emb1b-5G1_MGU74Av2.CEL	Emb1b-5G1_MGU74Av2.EXP	P-MLD6-7	Emb1b-5G1_MGU74Av2	Emb1b-5G1_MGU74Av2.CHP	MG_U74Av2.CDF	"Embryo 1"	"right or left pr [...]
+"Charles River # 024"	organism_part	9.5	embryo	"Embryo 2"	"right or left presomitic mesoderm (posterior half)(a)"	"Mus musculus"	CD-1	P-MLD6-1	"Charles River # 024"	organism_part	P-MLD6-2	"Charles River # 024"	total_RNA	P-MLD6-3	"Charles River # 024 biotin"	synthetic_RNA	biotin	P-MLD6-4	Emb2a-3_MGU74Av2	A-AFFY-6	P-MLD6-5	Emb2a-3_MGU74Av2	P-MLD6-6	Emb2a-3_MGU74Av2.CEL	Emb2a-3_MGU74Av2.EXP	P-MLD6-7	Emb2a-3_MGU74Av2	Emb2a-3_MGU74Av2.CHP	MG_U74Av2.CDF	"Embryo 2"	"right or left presomitic mes [...]
+"Charles River # 025"	organism_part	9.5	embryo	"Embryo 2"	"right or left presomitic mesoderm (posterior half)(b)"	"Mus musculus"	CD-1	P-MLD6-1	"Charles River # 025"	organism_part	P-MLD6-2	"Charles River # 025"	total_RNA	P-MLD6-3	"Charles River # 025 biotin"	synthetic_RNA	biotin	P-MLD6-4	Emb2b-4_MGU74Av2	A-AFFY-6	P-MLD6-5	Emb2b-4_MGU74Av2	P-MLD6-6	Emb2b-4_MGU74Av2.CEL	Emb2b-4_MGU74Av2.EXP	P-MLD6-7	Emb2b-4_MGU74Av2	Emb2b-4_MGU74Av2.CHP	MG_U74Av2.CDF	"Embryo 2"	"right or left presomitic mes [...]
+"Charles River # 026"	organism_part	9.5	embryo	"Embryo 3"	"right or left presomitic mesoderm (posterior half)(a)"	"Mus musculus"	CD-1	P-MLD6-1	"Charles River # 026"	organism_part	P-MLD6-2	"Charles River # 026"	total_RNA	P-MLD6-3	"Charles River # 026 biotin"	synthetic_RNA	biotin	P-MLD6-4	Emb3a-D_MGU74Av2	A-AFFY-6	P-MLD6-5	Emb3a-D_MGU74Av2	P-MLD6-6	Emb3a-D_MGU74Av2.CEL	Emb3a-D_MGU74Av2.EXP	P-MLD6-7	Emb3a-D_MGU74Av2	Emb3a-D_MGU74Av2.CHP	MG_U74Av2.CDF	"Embryo 3"	"right or left presomitic mes [...]
+"Charles River # 027"	organism_part	9.5	embryo	"Embryo 3"	"right or left presomitic mesoderm (posterior half)(b)"	"Mus musculus"	CD-1	P-MLD6-1	"Charles River # 027"	organism_part	P-MLD6-2	"Charles River # 027"	total_RNA	P-MLD6-3	"Charles River # 027 biotin"	synthetic_RNA	biotin	P-MLD6-4	Emb3b-E_MGU74Av2	A-AFFY-6	P-MLD6-5	Emb3b-E_MGU74Av2	P-MLD6-6	Emb3b-E_MGU74Av2.CEL	Emb3b-E_MGU74Av2.EXP	P-MLD6-7	Emb3b-E_MGU74Av2	Emb3b-E_MGU74Av2.CHP	MG_U74Av2.CDF	"Embryo 3"	"right or left presomitic mes [...]
+"Charles River # 028"	organism_part	9.5	embryo	"Embryo 01"	"right presomitic mesoderm (posterior half)"	"Mus musculus"	CD-1	P-MLD6-1	"Charles River # 028"	organism_part	P-MLD6-2	"Charles River # 028"	total_RNA	P-MLD6-3	"Charles River # 028 biotin"	synthetic_RNA	biotin	P-MLD6-4	Emb01-6.3_MOE430A	A-AFFY-23	P-MLD6-5	Emb01-6.3_MOE430A	P-MLD6-6	Emb01-6.3_MOE430A.CEL	Emb01-6.3_MOE430A.EXP	P-MLD6-7	Emb01-6.3_MOE430A	Emb01-6.3_MOE430A.CHP	MOE430A.CDF	"Embryo 01"	"right presomitic mesoderm (poste [...]
+"Charles River # 029"	organism_part	9.5	embryo	"Embryo 02"	"right presomitic mesoderm (posterior half)"	"Mus musculus"	CD-1	P-MLD6-1	"Charles River # 029"	organism_part	P-MLD6-2	"Charles River # 029"	total_RNA	P-MLD6-3	"Charles River # 029 biotin"	synthetic_RNA	biotin	P-MLD6-4	Emb02-10.3_MOE430A	A-AFFY-23	P-MLD6-5	Emb02-10.3_MOE430A	P-MLD6-6	Emb02-10.3_MOE430A.CEL	Emb02-10.3_MOE430A.EXP	P-MLD6-7	Emb02-10.3_MOE430A	Emb02-10.3_MOE430A.CHP	MOE430A.CDF	"Embryo 02"	"right presomitic mesoderm  [...]
+"Charles River # 030"	organism_part	9.5	embryo	"Embryo 03"	"right presomitic mesoderm (posterior half)"	"Mus musculus"	CD-1	P-MLD6-1	"Charles River # 030"	organism_part	P-MLD6-2	"Charles River # 030"	total_RNA	P-MLD6-3	"Charles River # 030 biotin"	synthetic_RNA	biotin	P-MLD6-4	Emb03-7.3_MOE430A	A-AFFY-23	P-MLD6-5	Emb03-7.3_MOE430A	P-MLD6-6	Emb03-7.3_MOE430A.CEL	Emb03-7.3_MOE430A.EXP	P-MLD6-7	Emb03-7.3_MOE430A	Emb03-7.3_MOE430A.CHP	MOE430A.CDF	"Embryo 03"	"right presomitic mesoderm (poste [...]
+"Charles River # 031"	organism_part	9.5	embryo	"Embryo 04"	"right presomitic mesoderm (posterior half)"	"Mus musculus"	CD-1	P-MLD6-1	"Charles River # 031"	organism_part	P-MLD6-2	"Charles River # 031"	total_RNA	P-MLD6-3	"Charles River # 031 biotin"	synthetic_RNA	biotin	P-MLD6-4	Emb04-11.3_MOE430A	A-AFFY-23	P-MLD6-5	Emb04-11.3_MOE430A	P-MLD6-6	Emb04-11.3_MOE430A.CEL	Emb04-11.3_MOE430A.EXP	P-MLD6-7	Emb04-11.3_MOE430A	Emb04-11.3_MOE430A.CHP	MOE430A.CDF	"Embryo 04"	"right presomitic mesoderm  [...]
+"Charles River # 032"	organism_part	9.5	embryo	"Embryo 05"	"right presomitic mesoderm (posterior half)"	"Mus musculus"	CD-1	P-MLD6-1	"Charles River # 032"	organism_part	P-MLD6-2	"Charles River # 032"	total_RNA	P-MLD6-3	"Charles River # 032 biotin"	synthetic_RNA	biotin	P-MLD6-4	Emb05-12.1_MOE430A	A-AFFY-23	P-MLD6-5	Emb05-12.1_MOE430A	P-MLD6-6	Emb05-12.1_MOE430A.CEL	Emb05-12.1_MOE430A.EXP	P-MLD6-7	Emb05-12.1_MOE430A	Emb05-12.1_MOE430A.CHP	MOE430A.CDF	"Embryo 05"	"right presomitic mesoderm  [...]
+"Charles River # 033"	organism_part	9.5	embryo	"Embryo 06"	"right presomitic mesoderm (posterior half)"	"Mus musculus"	CD-1	P-MLD6-1	"Charles River # 033"	organism_part	P-MLD6-2	"Charles River # 033"	total_RNA	P-MLD6-3	"Charles River # 033 biotin"	synthetic_RNA	biotin	P-MLD6-4	Emb06-19.1_MOE430A	A-AFFY-23	P-MLD6-5	Emb06-19.1_MOE430A	P-MLD6-6	Emb06-19.1_MOE430A.CEL	Emb06-19.1_MOE430A.EXP	P-MLD6-7	Emb06-19.1_MOE430A	Emb06-19.1_MOE430A.CHP	MOE430A.CDF	"Embryo 06"	"right presomitic mesoderm  [...]
+"Charles River # 034"	organism_part	9.5	embryo	"Embryo 07"	"right presomitic mesoderm (posterior half)"	"Mus musculus"	CD-1	P-MLD6-1	"Charles River # 034"	organism_part	P-MLD6-2	"Charles River # 034"	total_RNA	P-MLD6-3	"Charles River # 034 biotin"	synthetic_RNA	biotin	P-MLD6-4	Emb07-20.1_MOE430A	A-AFFY-23	P-MLD6-5	Emb07-20.1_MOE430A	P-MLD6-6	Emb07-20.1_MOE430A.CEL	Emb07-20.1_MOE430A.EXP	P-MLD6-7	Emb07-20.1_MOE430A	Emb07-20.1_MOE430A.CHP	MOE430A.CDF	"Embryo 07"	"right presomitic mesoderm  [...]
+"Charles River # 035"	organism_part	9.5	embryo	"Embryo 08"	"right presomitic mesoderm (posterior half)"	"Mus musculus"	CD-1	P-MLD6-1	"Charles River # 035"	organism_part	P-MLD6-2	"Charles River # 035"	total_RNA	P-MLD6-3	"Charles River # 035 biotin"	synthetic_RNA	biotin	P-MLD6-4	Emb08-4.1_MOE430A	A-AFFY-23	P-MLD6-5	Emb08-4.1_MOE430A	P-MLD6-6	Emb08-4.1_MOE430A.CEL	Emb08-4.1_MOE430A.EXP	P-MLD6-7	Emb08-4.1_MOE430A	Emb08-4.1_MOE430A.CHP	MOE430A.CDF	"Embryo 08"	"right presomitic mesoderm (poste [...]
+"Charles River # 036"	organism_part	9.5	embryo	"Embryo 09"	"right presomitic mesoderm (posterior half)"	"Mus musculus"	CD-1	P-MLD6-1	"Charles River # 036"	organism_part	P-MLD6-2	"Charles River # 036"	total_RNA	P-MLD6-3	"Charles River # 036 biotin"	synthetic_RNA	biotin	P-MLD6-4	Emb09-22.1_MOE430A	A-AFFY-23	P-MLD6-5	Emb09-22.1_MOE430A	P-MLD6-6	Emb09-22.1_MOE430A.CEL	Emb09-22.1_MOE430A.EXP	P-MLD6-7	Emb09-22.1_MOE430A	Emb09-22.1_MOE430A.CHP	MOE430A.CDF	"Embryo 09"	"right presomitic mesoderm  [...]
+"Charles River # 037"	organism_part	9.5	embryo	"Embryo 10"	"right presomitic mesoderm (posterior half)"	"Mus musculus"	CD-1	P-MLD6-1	"Charles River # 037"	organism_part	P-MLD6-2	"Charles River # 037"	total_RNA	P-MLD6-3	"Charles River # 037 biotin"	synthetic_RNA	biotin	P-MLD6-4	Emb10-9.1_MOE430A	A-AFFY-23	P-MLD6-5	Emb10-9.1_MOE430A	P-MLD6-6	Emb10-9.1_MOE430A.CEL	Emb10-9.1_MOE430A.EXP	P-MLD6-7	Emb10-9.1_MOE430A	Emb10-9.1_MOE430A.CHP	MOE430A.CDF	"Embryo 10"	"right presomitic mesoderm (poste [...]
+"Charles River # 038"	organism_part	9.5	embryo	"Embryo 11"	"right presomitic mesoderm (posterior half)"	"Mus musculus"	CD-1	P-MLD6-1	"Charles River # 038"	organism_part	P-MLD6-2	"Charles River # 038"	total_RNA	P-MLD6-3	"Charles River # 038 biotin"	synthetic_RNA	biotin	P-MLD6-4	Emb11-23.2_MOE430A	A-AFFY-23	P-MLD6-5	Emb11-23.2_MOE430A	P-MLD6-6	Emb11-23.2_MOE430A.CEL	Emb11-23.2_MOE430A.EXP	P-MLD6-7	Emb11-23.2_MOE430A	Emb11-23.2_MOE430A.CHP	MOE430A.CDF	"Embryo 11"	"right presomitic mesoderm  [...]
+"Charles River # 039"	organism_part	9.5	embryo	"Embryo 12"	"right presomitic mesoderm (posterior half)"	"Mus musculus"	CD-1	P-MLD6-1	"Charles River # 039"	organism_part	P-MLD6-2	"Charles River # 039"	total_RNA	P-MLD6-3	"Charles River # 039 biotin"	synthetic_RNA	biotin	P-MLD6-4	Emb12-13.2_MOE430A	A-AFFY-23	P-MLD6-5	Emb12-13.2_MOE430A	P-MLD6-6	Emb12-13.2_MOE430A.CEL	Emb12-13.2_MOE430A.EXP	P-MLD6-7	Emb12-13.2_MOE430A	Emb12-13.2_MOE430A.CHP	MOE430A.CDF	"Embryo 12"	"right presomitic mesoderm  [...]
+"Charles River # 040"	organism_part	9.5	embryo	"Embryo 13"	"right presomitic mesoderm (posterior half)"	"Mus musculus"	CD-1	P-MLD6-1	"Charles River # 040"	organism_part	P-MLD6-2	"Charles River # 040"	total_RNA	P-MLD6-3	"Charles River # 040 biotin"	synthetic_RNA	biotin	P-MLD6-4	Emb13-14.2_MOE430A	A-AFFY-23	P-MLD6-5	Emb13-14.2_MOE430A	P-MLD6-6	Emb13-14.2_MOE430A.CEL	Emb13-14.2_MOE430A.EXP	P-MLD6-7	Emb13-14.2_MOE430A	Emb13-14.2_MOE430A.CHP	MOE430A.CDF	"Embryo 13"	"right presomitic mesoderm  [...]
+"Charles River # 041"	organism_part	9.5	embryo	"Embryo 14"	"right presomitic mesoderm (posterior half)"	"Mus musculus"	CD-1	P-MLD6-1	"Charles River # 041"	organism_part	P-MLD6-2	"Charles River # 041"	total_RNA	P-MLD6-3	"Charles River # 041 biotin"	synthetic_RNA	biotin	P-MLD6-4	Emb14-17.2_MOE430A	A-AFFY-23	P-MLD6-5	Emb14-17.2_MOE430A	P-MLD6-6	Emb14-17.2_MOE430A.CEL	Emb14-17.2_MOE430A.EXP	P-MLD6-7	Emb14-17.2_MOE430A	Emb14-17.2_MOE430A.CHP	MOE430A.CDF	"Embryo 14"	"right presomitic mesoderm  [...]
+"Charles River # 042"	organism_part	9.5	embryo	"Embryo 15"	"right presomitic mesoderm (posterior half)"	"Mus musculus"	CD-1	P-MLD6-1	"Charles River # 042"	organism_part	P-MLD6-2	"Charles River # 042"	total_RNA	P-MLD6-3	"Charles River # 042 biotin"	synthetic_RNA	biotin	P-MLD6-4	Emb15-16.2_MOE430A	A-AFFY-23	P-MLD6-5	Emb15-16.2_MOE430A	P-MLD6-6	Emb15-16.2_MOE430A.CEL	Emb15-16.2_MOE430A.EXP	P-MLD6-7	Emb15-16.2_MOE430A	Emb15-16.2_MOE430A.CHP	MOE430A.CDF	"Embryo 15"	"right presomitic mesoderm  [...]
+"Charles River # 043"	organism_part	9.5	embryo	"Embryo 16"	"right presomitic mesoderm (posterior half)"	"Mus musculus"	CD-1	P-MLD6-1	"Charles River # 043"	organism_part	P-MLD6-2	"Charles River # 043"	total_RNA	P-MLD6-3	"Charles River # 043 biotin"	synthetic_RNA	biotin	P-MLD6-4	Emb16-15.2_MOE430A	A-AFFY-23	P-MLD6-5	Emb16-15.2_MOE430A	P-MLD6-6	Emb16-15.2_MOE430A.CEL	Emb16-15.2_MOE430A.EXP	P-MLD6-7	Emb16-15.2_MOE430A	Emb16-15.2_MOE430A.CHP	MOE430A.CDF	"Embryo 16"	"right presomitic mesoderm  [...]
+"Charles River # 044"	organism_part	9.5	embryo	"Embryo 17"	"right presomitic mesoderm (posterior half)"	"Mus musculus"	CD-1	P-MLD6-1	"Charles River # 044"	organism_part	P-MLD6-2	"Charles River # 044"	total_RNA	P-MLD6-3	"Charles River # 044 biotin"	synthetic_RNA	biotin	P-MLD6-4	Emb17-18.2_MOE430A	A-AFFY-23	P-MLD6-5	Emb17-18.2_MOE430A	P-MLD6-6	Emb17-18.2_MOE430A.CEL	Emb17-18.2_MOE430A.EXP	P-MLD6-7	Emb17-18.2_MOE430A	Emb17-18.2_MOE430A.CHP	MOE430A.CDF	"Embryo 17"	"right presomitic mesoderm  [...]
diff --git a/examples/magetab/real/E-TABM-16_sdrf.txt b/examples/magetab/real/E-TABM-16_sdrf.txt
new file mode 100644
index 0000000..5baa0e2
--- /dev/null
+++ b/examples/magetab/real/E-TABM-16_sdrf.txt
@@ -0,0 +1,105 @@
+"Source Name"	"Material Type"	"Characteristics [Age]"	"Characteristics [BioSourceProvider]"	"Characteristics [Organism]"	"Characteristics [Sex]"	"Characteristics [StrainOrLine]"	"Protocol REF"	"Protocol REF"	"Sample Name"	"Material Type"	"Protocol REF"	"Extract Name"	"Material Type"	"Protocol REF"	"Labeled Extract Name"	"Material Type"	Label	"Protocol REF"	"Hybridization Name"	"Array Design REF"	"Protocol REF"	"Scan Name"	"Array Data File"	Comment[EXP]	"Protocol REF"	"Normalization Name" [...]
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix1	"MTS Batch1 Mix1"	total_RNA	6486	"MTS Site1 Batch1 Mix1 biotin"	synthetic_DNA	biotin	6491	"MTS Site1 Batch1 Mix1"	A-AFFY-25	6407	Site1_Batch1_Mix1	Site1_Batch1_Mix1.CEL	Site1_Batch1_Mix1.EXP	6553	Site1_Batch1_Mix1_PLIER	Site1_Batch1_Mix1_PLIER.CHP	RAE230A.CDF	Batch1	Mix1	Site1	Affymetrix
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix2	"MTS Batch1 Mix2"	total_RNA	6486	"MTS Site1 Batch1 Mix2 biotin"	synthetic_DNA	biotin	6491	"MTS Site1 Batch1 Mix2"	A-AFFY-25	6407	Site1_Batch1_Mix2	Site1_Batch1_Mix2.CEL	Site1_Batch1_Mix2.EXP	6553	Site1_Batch1_Mix2_PLIER	Site1_Batch1_Mix2_PLIER.CHP	RAE230A.CDF	Batch1	Mix2	Site1	Affymetrix
+"MTS Batch2"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch2"	organism_part	6396-mix1	"MTS Batch2 Mix1"	total_RNA	6486	"MTS Site1 Batch2 Mix1 biotin"	synthetic_DNA	biotin	6491	"MTS Site1 Batch2 Mix1"	A-AFFY-25	6407	Site1_Batch2_Mix1	Site1_Batch2_Mix1.CEL	Site1_Batch2_Mix1.EXP	6553	Site1_Batch2_Mix1_PLIER	Site1_Batch2_Mix1_PLIER.CHP	RAE230A.CDF	Batch2	Mix1	Site1	Affymetrix
+"MTS Batch2"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch2"	organism_part	6396-mix2	"MTS Batch2 Mix2"	total_RNA	6486	"MTS Site1 Batch2 Mix2 biotin"	synthetic_DNA	biotin	6491	"MTS Site1 Batch2 Mix2"	A-AFFY-25	6407	Site1_Batch2_Mix2	Site1_Batch2_Mix2.CEL	Site1_Batch2_Mix2.EXP	6553	Site1_Batch2_Mix2_PLIER	Site1_Batch2_Mix2_PLIER.CHP	RAE230A.CDF	Batch2	Mix2	Site1	Affymetrix
+"MTS Batch3"	whole_organism	"6-7 weeks"	"Harlan, Frederick, MD"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch3"	organism_part	6396-mix1	"MTS Batch3 Mix1"	total_RNA	6486	"MTS Site1 Batch3 Mix1 biotin"	synthetic_DNA	biotin	6491	"MTS Site1 Batch3 Mix1"	A-AFFY-25	6407	Site1_Batch3_Mix1	Site1_Batch3_Mix1.CEL	Site1_Batch3_Mix1.EXP	6553	Site1_Batch3_Mix1_PLIER	Site1_Batch3_Mix1_PLIER.CHP	RAE230A.CDF	Batch3	Mix1	Site1	Affymetrix
+"MTS Batch3"	whole_organism	"6-7 weeks"	"Harlan, Frederick, MD"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch3"	organism_part	6396-mix2	"MTS Batch3 Mix2"	total_RNA	6486	"MTS Site1 Batch3 Mix2 biotin"	synthetic_DNA	biotin	6491	"MTS Site1 Batch3 Mix2"	A-AFFY-25	6407	Site1_Batch3_Mix2	Site1_Batch3_Mix2.CEL	Site1_Batch3_Mix2.EXP	6553	Site1_Batch3_Mix2_PLIER	Site1_Batch3_Mix2_PLIER.CHP	RAE230A.CDF	Batch3	Mix2	Site1	Affymetrix
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix1	"MTS Batch1 Mix1"	total_RNA	6489	"MTS Site2 Batch1 Mix1 biotin"	synthetic_DNA	biotin	6491	"MTS Site2 Batch1 Mix1"	A-AFFY-25	6407	Site2_Batch1_Mix1	Site2_Batch1_Mix1.CEL	Site2_Batch1_Mix1.EXP	6553	Site2_Batch1_Mix1_PLIER	Site2_Batch1_Mix1_PLIER.CHP	RAE230A.CDF	Batch1	Mix1	Site2	Affymetrix
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix2	"MTS Batch1 Mix2"	total_RNA	6489	"MTS Site2 Batch1 Mix2 biotin"	synthetic_DNA	biotin	6491	"MTS Site2 Batch1 Mix2"	A-AFFY-25	6407	Site2_Batch1_Mix2	Site2_Batch1_Mix2.CEL	Site2_Batch1_Mix2.EXP	6553	Site2_Batch1_Mix2_PLIER	Site2_Batch1_Mix2_PLIER.CHP	RAE230A.CDF	Batch1	Mix2	Site2	Affymetrix
+"MTS Batch2"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch2"	organism_part	6396-mix1	"MTS Batch2 Mix1"	total_RNA	6489	"MTS Site2 Batch2 Mix1 biotin"	synthetic_DNA	biotin	6491	"MTS Site2 Batch2 Mix1"	A-AFFY-25	6407	Site2_Batch2_Mix1	Site2_Batch2_Mix1.CEL	Site2_Batch2_Mix1.EXP	6553	Site2_Batch2_Mix1_PLIER	Site2_Batch2_Mix1_PLIER.CHP	RAE230A.CDF	Batch2	Mix1	Site2	Affymetrix
+"MTS Batch2"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch2"	organism_part	6396-mix2	"MTS Batch2 Mix2"	total_RNA	6489	"MTS Site2 Batch2 Mix2 biotin"	synthetic_DNA	biotin	6491	"MTS Site2 Batch2 Mix2"	A-AFFY-25	6407	Site2_Batch2_Mix2	Site2_Batch2_Mix2.CEL	Site2_Batch2_Mix2.EXP	6553	Site2_Batch2_Mix2_PLIER	Site2_Batch2_Mix2_PLIER.CHP	RAE230A.CDF	Batch2	Mix2	Site2	Affymetrix
+"MTS Batch3"	whole_organism	"6-7 weeks"	"Harlan, Frederick, MD"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch3"	organism_part	6396-mix1	"MTS Batch3 Mix1"	total_RNA	6489	"MTS Site2 Batch3 Mix1 biotin"	synthetic_DNA	biotin	6491	"MTS Site2 Batch3 Mix1"	A-AFFY-25	6407	Site2_Batch3_Mix1	Site2_Batch3_Mix1.CEL	Site2_Batch3_Mix1.EXP	6553	Site2_Batch3_Mix1_PLIER	Site2_Batch3_Mix1_PLIER.CHP	RAE230A.CDF	Batch3	Mix1	Site2	Affymetrix
+"MTS Batch3"	whole_organism	"6-7 weeks"	"Harlan, Frederick, MD"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch3"	organism_part	6396-mix2	"MTS Batch3 Mix2"	total_RNA	6489	"MTS Site2 Batch3 Mix2 biotin"	synthetic_DNA	biotin	6491	"MTS Site2 Batch3 Mix2"	A-AFFY-25	6407	Site2_Batch3_Mix2	Site2_Batch3_Mix2.CEL	Site2_Batch3_Mix2.EXP	6553	Site2_Batch3_Mix2_PLIER	Site2_Batch3_Mix2_PLIER.CHP	RAE230A.CDF	Batch3	Mix2	Site2	Affymetrix
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix1	"MTS Batch1 Mix1"	total_RNA	6486	"MTS Site3 Batch1 Mix1 biotin"	synthetic_DNA	biotin	6491	"MTS Site3 Batch1 Mix1"	A-AFFY-25	6409	Site3_Batch1_Mix1	Site3_Batch1_Mix1.CEL	Site3_Batch1_Mix1.EXP	6553	Site3_Batch1_Mix1_PLIER	Site3_Batch1_Mix1_PLIER.CHP	RAE230A.CDF	Batch1	Mix1	Site3	Affymetrix
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix2	"MTS Batch1 Mix2"	total_RNA	6486	"MTS Site3 Batch1 Mix2 biotin"	synthetic_DNA	biotin	6491	"MTS Site3 Batch1 Mix2"	A-AFFY-25	6409	Site3_Batch1_Mix2	Site3_Batch1_Mix2.CEL	Site3_Batch1_Mix2.EXP	6553	Site3_Batch1_Mix2_PLIER	Site3_Batch1_Mix2_PLIER.CHP	RAE230A.CDF	Batch1	Mix2	Site3	Affymetrix
+"MTS Batch2"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch2"	organism_part	6396-mix1	"MTS Batch2 Mix1"	total_RNA	6486	"MTS Site3 Batch2 Mix1 biotin"	synthetic_DNA	biotin	6491	"MTS Site3 Batch2 Mix1"	A-AFFY-25	6409	Site3_Batch2_Mix1	Site3_Batch2_Mix1.CEL	Site3_Batch2_Mix1.EXP	6553	Site3_Batch2_Mix1_PLIER	Site3_Batch2_Mix1_PLIER.CHP	RAE230A.CDF	Batch2	Mix1	Site3	Affymetrix
+"MTS Batch2"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch2"	organism_part	6396-mix2	"MTS Batch2 Mix2"	total_RNA	6486	"MTS Site3 Batch2 Mix2 biotin"	synthetic_DNA	biotin	6491	"MTS Site3 Batch2 Mix2"	A-AFFY-25	6409	Site3_Batch2_Mix2	Site3_Batch2_Mix2.CEL	Site3_Batch2_Mix2.EXP	6553	Site3_Batch2_Mix2_PLIER	Site3_Batch2_Mix2_PLIER.CHP	RAE230A.CDF	Batch2	Mix2	Site3	Affymetrix
+"MTS Batch3"	whole_organism	"6-7 weeks"	"Harlan, Frederick, MD"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch3"	organism_part	6396-mix1	"MTS Batch3 Mix1"	total_RNA	6486	"MTS Site3 Batch3 Mix1 biotin"	synthetic_DNA	biotin	6491	"MTS Site3 Batch3 Mix1"	A-AFFY-25	6409	Site3_Batch3_Mix1	Site3_Batch3_Mix1.CEL	Site3_Batch3_Mix1.EXP	6553	Site3_Batch3_Mix1_PLIER	Site3_Batch3_Mix1_PLIER.CHP	RAE230A.CDF	Batch3	Mix1	Site3	Affymetrix
+"MTS Batch3"	whole_organism	"6-7 weeks"	"Harlan, Frederick, MD"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch3"	organism_part	6396-mix2	"MTS Batch3 Mix2"	total_RNA	6486	"MTS Site3 Batch3 Mix2 biotin"	synthetic_DNA	biotin	6491	"MTS Site3 Batch3 Mix2"	A-AFFY-25	6409	Site3_Batch3_Mix2	Site3_Batch3_Mix2.CEL	Site3_Batch3_Mix2.EXP	6553	Site3_Batch3_Mix2_PLIER	Site3_Batch3_Mix2_PLIER.CHP	RAE230A.CDF	Batch3	Mix2	Site3	Affymetrix
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix1	"MTS Batch1 Mix1"	total_RNA	6486	"MTS Site1 Batch1 Mix1 1YR biotin"	synthetic_DNA	biotin	6491	"MTS Site1 Batch1 Mix1 1YR"	A-AFFY-25	6407	Site1_Batch1_Mix1_1YR	Site1_Batch1_Mix1_1YR.CEL	Site1_Batch1_Mix1_1YR.EXP	6553	Site1_Batch1_Mix1_1YR_PLIER	Site1_Batch1_Mix1_1YR_PLIER.CHP	RAE230A.CDF	Batch1	Mix1	Site1	Affymetrix
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix2	"MTS Batch1 Mix2"	total_RNA	6486	"MTS Site1 Batch1 Mix2 1YR biotin"	synthetic_DNA	biotin	6491	"MTS Site1 Batch1 Mix2 1YR"	A-AFFY-25	6407	Site1_Batch1_Mix2_1YR	Site1_Batch1_Mix2_1YR.CEL	Site1_Batch1_Mix2_1YR.EXP	6553	Site1_Batch1_Mix2_1YR_PLIER	Site1_Batch1_Mix2_1YR_PLIER.CHP	RAE230A.CDF	Batch1	Mix2	Site1	Affymetrix
+"MTS Batch4"	total_RNA		"Ambion (Austin, TX)"	"Rattus norvegicus"	unknown_sex	Sprague-Dawley	6673	6672	"MTS Batch4"	organism_part	6841-mix1	"MTS Batch4 Mix1"	total_RNA	6486	"MTS Site1 Batch4 Mix1 biotin"	synthetic_DNA	biotin	6491	"MTS Site1 Batch4 Mix1"	A-AFFY-25	6407	Site1_Batch4_Mix1	Site1_Batch4_Mix1.CEL	Site1_Batch4_Mix1.EXP	6553	Site1_Batch4_Mix1_PLIER	Site1_Batch4_Mix1_PLIER.CHP	RAE230A.CDF	Batch4	Mix1	Site1	Affymetrix
+"MTS Batch4"	total_RNA		"Ambion (Austin, TX)"	"Rattus norvegicus"	unknown_sex	Sprague-Dawley	6673	6672	"MTS Batch4"	organism_part	6841-mix2	"MTS Batch4 Mix2"	total_RNA	6486	"MTS Site1 Batch4 Mix2 biotin"	synthetic_DNA	biotin	6491	"MTS Site1 Batch4 Mix2"	A-AFFY-25	6407	Site1_Batch4_Mix2	Site1_Batch4_Mix2.CEL	Site1_Batch4_Mix2.EXP	6553	Site1_Batch4_Mix2_PLIER	Site1_Batch4_Mix2_PLIER.CHP	RAE230A.CDF	Batch4	Mix2	Site1	Affymetrix
+"MTS Batch5"	total_RNA		"Stratagene (La Jolla, CA)"	"Rattus norvegicus"	unknown_sex	Sprague-Dawley	6673	6672	"MTS Batch5"	organism_part	6841-mix1	"MTS Batch5 Mix1"	total_RNA	6486	"MTS Site1 Batch5 Mix1 biotin"	synthetic_DNA	biotin	6491	"MTS Site1 Batch5 Mix1"	A-AFFY-25	6407	Site1_Batch5_Mix1	Site1_Batch5_Mix1.CEL	Site1_Batch5_Mix1.EXP	6553	Site1_Batch5_Mix1_PLIER	Site1_Batch5_Mix1_PLIER.CHP	RAE230A.CDF	Batch5	Mix1	Site1	Affymetrix
+"MTS Batch5"	total_RNA		"Stratagene (La Jolla, CA)"	"Rattus norvegicus"	unknown_sex	Sprague-Dawley	6673	6672	"MTS Batch5"	organism_part	6841-mix2	"MTS Batch5 Mix2"	total_RNA	6486	"MTS Site1 Batch5 Mix2 biotin"	synthetic_DNA	biotin	6491	"MTS Site1 Batch5 Mix2"	A-AFFY-25	6407	Site1_Batch5_Mix2	Site1_Batch5_Mix2.CEL	Site1_Batch5_Mix2.EXP	6553	Site1_Batch5_Mix2_PLIER	Site1_Batch5_Mix2_PLIER.CHP	RAE230A.CDF	Batch5	Mix2	Site1	Affymetrix
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix1	"MTS Batch1 Mix1"	total_RNA	7672	"MTS Site4 Batch1 Mix1 biotin"	synthetic_DNA	biotin	8188	"MTS Site4 Batch1 Mix1"	A-MEXP-124	8309	Site4_Batch1_Mix1_Data	Site4_Batch1_Mix1_Data.txt			Site4_Batch1_Mix1_Data			Batch1	Mix1	Site4	Codelink
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix2	"MTS Batch1 Mix2"	total_RNA	7672	"MTS Site4 Batch1 Mix2 biotin"	synthetic_DNA	biotin	8188	"MTS Site4 Batch1 Mix2"	A-MEXP-124	8309	Site4_Batch1_Mix2_Data	Site4_Batch1_Mix2_Data.txt			Site4_Batch1_Mix2_Data			Batch1	Mix2	Site4	Codelink
+"MTS Batch2"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch2"	organism_part	6396-mix1	"MTS Batch2 Mix1"	total_RNA	7672	"MTS Site4 Batch2 Mix1 biotin"	synthetic_DNA	biotin	8188	"MTS Site4 Batch2 Mix1"	A-MEXP-124	8309	Site4_Batch2_Mix1_Data	Site4_Batch2_Mix1_Data.txt			Site4_Batch2_Mix1_Data			Batch2	Mix1	Site4	Codelink
+"MTS Batch2"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch2"	organism_part	6396-mix2	"MTS Batch2 Mix2"	total_RNA	7672	"MTS Site4 Batch2 Mix2 biotin"	synthetic_DNA	biotin	8188	"MTS Site4 Batch2 Mix2"	A-MEXP-124	8309	Site4_Batch2_Mix2_Data	Site4_Batch2_Mix2_Data.txt			Site4_Batch2_Mix2_Data			Batch2	Mix2	Site4	Codelink
+"MTS Batch3"	whole_organism	"6-7 weeks"	"Harlan, Frederick, MD"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch3"	organism_part	6396-mix1	"MTS Batch3 Mix1"	total_RNA	7672	"MTS Site4 Batch3 Mix1 biotin"	synthetic_DNA	biotin	8188	"MTS Site4 Batch3 Mix1"	A-MEXP-124	8309	Site4_Batch3_Mix1_Data	Site4_Batch3_Mix1_Data.txt			Site4_Batch3_Mix1_Data			Batch3	Mix1	Site4	Codelink
+"MTS Batch3"	whole_organism	"6-7 weeks"	"Harlan, Frederick, MD"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch3"	organism_part	6396-mix2	"MTS Batch3 Mix2"	total_RNA	7672	"MTS Site4 Batch3 Mix2 biotin"	synthetic_DNA	biotin	8188	"MTS Site4 Batch3 Mix2"	A-MEXP-124	8309	Site4_Batch3_Mix2_Data	Site4_Batch3_Mix2_Data.txt			Site4_Batch3_Mix2_Data			Batch3	Mix2	Site4	Codelink
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix1	"MTS Batch1 Mix1"	total_RNA	7671	"MTS Site5 Batch1 Mix1 biotin"	synthetic_DNA	biotin	8189	"MTS Site5 Batch1 Mix1"	A-MEXP-124	8190	Site5_Batch1_Mix1_Data	Site5_Batch1_Mix1_Data.txt			Site5_Batch1_Mix1_Data			Batch1	Mix1	Site5	Codelink
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix2	"MTS Batch1 Mix2"	total_RNA	7671	"MTS Site5 Batch1 Mix2 biotin"	synthetic_DNA	biotin	8189	"MTS Site5 Batch1 Mix2"	A-MEXP-124	8190	Site5_Batch1_Mix2_Data	Site5_Batch1_Mix2_Data.txt			Site5_Batch1_Mix2_Data			Batch1	Mix2	Site5	Codelink
+"MTS Batch2"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch2"	organism_part	6396-mix1	"MTS Batch2 Mix1"	total_RNA	7671	"MTS Site5 Batch2 Mix1 biotin"	synthetic_DNA	biotin	8189	"MTS Site5 Batch2 Mix1"	A-MEXP-124	8190	Site5_Batch2_Mix1_Data	Site5_Batch2_Mix1_Data.txt			Site5_Batch2_Mix1_Data			Batch2	Mix1	Site5	Codelink
+"MTS Batch2"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch2"	organism_part	6396-mix2	"MTS Batch2 Mix2"	total_RNA	7671	"MTS Site5 Batch2 Mix2 biotin"	synthetic_DNA	biotin	8189	"MTS Site5 Batch2 Mix2"	A-MEXP-124	8190	Site5_Batch2_Mix2_Data	Site5_Batch2_Mix2_Data.txt			Site5_Batch2_Mix2_Data			Batch2	Mix2	Site5	Codelink
+"MTS Batch3"	whole_organism	"6-7 weeks"	"Harlan, Frederick, MD"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch3"	organism_part	6396-mix1	"MTS Batch3 Mix1"	total_RNA	7671	"MTS Site5 Batch3 Mix1 biotin"	synthetic_DNA	biotin	8189	"MTS Site5 Batch3 Mix1"	A-MEXP-124	8190	Site5_Batch3_Mix1_Data	Site5_Batch3_Mix1_Data.txt			Site5_Batch3_Mix1_Data			Batch3	Mix1	Site5	Codelink
+"MTS Batch3"	whole_organism	"6-7 weeks"	"Harlan, Frederick, MD"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch3"	organism_part	6396-mix2	"MTS Batch3 Mix2"	total_RNA	7671	"MTS Site5 Batch3 Mix2 biotin"	synthetic_DNA	biotin	8189	"MTS Site5 Batch3 Mix2"	A-MEXP-124	8190	Site5_Batch3_Mix2_Data	Site5_Batch3_Mix2_Data.txt			Site5_Batch3_Mix2_Data			Batch3	Mix2	Site5	Codelink
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix2	"MTS Batch1 Mix2"	total_RNA	8532	"MTS Site7 Batch1 Mix2 (Cy3) Cy3"	synthetic_DNA	Cy3	8573	"MTS Site7 Batch1 rMix1 gMix2"	A-AGIL-6	8590	Site7_Batch1_rMix1_gMix2	Site7_Batch1_rMix1_gMix2.txt			Site7_Batch1_rMix1_gMix2			Batch1	Mix1;Mix2	Site7	Agilent
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix1	"MTS Batch1 Mix1"	total_RNA	8534	"MTS Site7 Batch1 Mix1 (Cy5) Cy5"	synthetic_DNA	Cy5	8573	"MTS Site7 Batch1 rMix1 gMix2"	A-AGIL-6	8590	Site7_Batch1_rMix1_gMix2	Site7_Batch1_rMix1_gMix2.txt			Site7_Batch1_rMix1_gMix2			Batch1	Mix1;Mix2	Site7	Agilent
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix2	"MTS Batch1 Mix2"	total_RNA	8534	"MTS Site7 Batch1 Mix2 (Cy5) Cy5"	synthetic_DNA	Cy5	8573	"MTS Site7 Batch1 rMix2 gMix1"	A-AGIL-6	8590	Site7_Batch1_rMix2_gMix1	Site7_Batch1_rMix2_gMix1.txt			Site7_Batch1_rMix2_gMix1			Batch1	Mix1;Mix2	Site7	Agilent
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix1	"MTS Batch1 Mix1"	total_RNA	8532	"MTS Site7 Batch1 Mix1 (Cy3) Cy3"	synthetic_DNA	Cy3	8573	"MTS Site7 Batch1 rMix2 gMix1"	A-AGIL-6	8590	Site7_Batch1_rMix2_gMix1	Site7_Batch1_rMix2_gMix1.txt			Site7_Batch1_rMix2_gMix1			Batch1	Mix1;Mix2	Site7	Agilent
+"MTS Batch2"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch2"	organism_part	6396-mix1	"MTS Batch2 Mix1"	total_RNA	8534	"MTS Site7 Batch2 Mix1 (Cy5) Cy5"	synthetic_DNA	Cy5	8573	"MTS Site7 Batch2 rMix1 gMix2"	A-AGIL-6	8590	Site7_Batch2_rMix1_gMix2	Site7_Batch2_rMix1_gMix2.txt			Site7_Batch2_rMix1_gMix2			Batch2	Mix1;Mix2	Site7	Agilent
+"MTS Batch2"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch2"	organism_part	6396-mix2	"MTS Batch2 Mix2"	total_RNA	8532	"MTS Site7 Batch2 Mix2 (Cy3) Cy3"	synthetic_DNA	Cy3	8573	"MTS Site7 Batch2 rMix1 gMix2"	A-AGIL-6	8590	Site7_Batch2_rMix1_gMix2	Site7_Batch2_rMix1_gMix2.txt			Site7_Batch2_rMix1_gMix2			Batch2	Mix1;Mix2	Site7	Agilent
+"MTS Batch2"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch2"	organism_part	6396-mix2	"MTS Batch2 Mix2"	total_RNA	8534	"MTS Site7 Batch2 Mix2 (Cy5) Cy5"	synthetic_DNA	Cy5	8573	"MTS Site7 Batch2 rMix2 gMix1"	A-AGIL-6	8590	Site7_Batch2_rMix2_gMix1	Site7_Batch2_rMix2_gMix1.txt			Site7_Batch2_rMix2_gMix1			Batch2	Mix1;Mix2	Site7	Agilent
+"MTS Batch2"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch2"	organism_part	6396-mix1	"MTS Batch2 Mix1"	total_RNA	8532	"MTS Site7 Batch2 Mix1 (Cy3) Cy3"	synthetic_DNA	Cy3	8573	"MTS Site7 Batch2 rMix2 gMix1"	A-AGIL-6	8590	Site7_Batch2_rMix2_gMix1	Site7_Batch2_rMix2_gMix1.txt			Site7_Batch2_rMix2_gMix1			Batch2	Mix1;Mix2	Site7	Agilent
+"MTS Batch3"	whole_organism	"6-7 weeks"	"Harlan, Frederick, MD"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch3"	organism_part	6396-mix2	"MTS Batch3 Mix2"	total_RNA	8532	"MTS Site7 Batch3 Mix2 (Cy3) Cy3"	synthetic_DNA	Cy3	8573	"MTS Site7 Batch3 rMix1 gMix2"	A-AGIL-6	8590	Site7_Batch3_rMix1_gMix2	Site7_Batch3_rMix1_gMix2.txt			Site7_Batch3_rMix1_gMix2			Batch3	Mix1;Mix2	Site7	Agilent
+"MTS Batch3"	whole_organism	"6-7 weeks"	"Harlan, Frederick, MD"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch3"	organism_part	6396-mix1	"MTS Batch3 Mix1"	total_RNA	8534	"MTS Site7 Batch3 Mix1 (Cy5) Cy5"	synthetic_DNA	Cy5	8573	"MTS Site7 Batch3 rMix1 gMix2"	A-AGIL-6	8590	Site7_Batch3_rMix1_gMix2	Site7_Batch3_rMix1_gMix2.txt			Site7_Batch3_rMix1_gMix2			Batch3	Mix1;Mix2	Site7	Agilent
+"MTS Batch3"	whole_organism	"6-7 weeks"	"Harlan, Frederick, MD"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch3"	organism_part	6396-mix2	"MTS Batch3 Mix2"	total_RNA	8534	"MTS Site7 Batch3 Mix2 (Cy5) Cy5"	synthetic_DNA	Cy5	8573	"MTS Site7 Batch3 rMix2 gMix1"	A-AGIL-6	8590	Site7_Batch3_rMix2_gMix1	Site7_Batch3_rMix2_gMix1.txt			Site7_Batch3_rMix2_gMix1			Batch3	Mix1;Mix2	Site7	Agilent
+"MTS Batch3"	whole_organism	"6-7 weeks"	"Harlan, Frederick, MD"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch3"	organism_part	6396-mix1	"MTS Batch3 Mix1"	total_RNA	8532	"MTS Site7 Batch3 Mix1 (Cy3) Cy3"	synthetic_DNA	Cy3	8573	"MTS Site7 Batch3 rMix2 gMix1"	A-AGIL-6	8590	Site7_Batch3_rMix2_gMix1	Site7_Batch3_rMix2_gMix1.txt			Site7_Batch3_rMix2_gMix1			Batch3	Mix1;Mix2	Site7	Agilent
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix2	"MTS Batch1 Mix2"	total_RNA	8535	"MTS Site8 Batch1 Mix2 (Cy3) Cy3"	synthetic_DNA	Cy3	8588	"MTS Site8 Batch1 rMix1 g Mix2"	A-AGIL-6	8590	Site8_Batch1_rMix1_gMix2	Site8_Batch1_rMix1_gMix2.txt			Site8_Batch1_rMix1_gMix2			Batch1	Mix1;Mix2	Site8	Agilent
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix1	"MTS Batch1 Mix1"	total_RNA	8536	"MTS Site8 Batch1 Mix1 (Cy5) Cy5"	synthetic_DNA	Cy5	8588	"MTS Site8 Batch1 rMix1 g Mix2"	A-AGIL-6	8590	Site8_Batch1_rMix1_gMix2	Site8_Batch1_rMix1_gMix2.txt			Site8_Batch1_rMix1_gMix2			Batch1	Mix1;Mix2	Site8	Agilent
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix2	"MTS Batch1 Mix2"	total_RNA	8536	"MTS Site8 Batch1 Mix2 (Cy5) Cy5"	synthetic_DNA	Cy5	8588	"MTS Site8 Batch1 rMix2 gMix1"	A-AGIL-6	8590	Site8_Batch1_rMix2_gMix1	Site8_Batch1_rMix2_gMix1.txt			Site8_Batch1_rMix2_gMix1			Batch1	Mix1;Mix2	Site8	Agilent
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix1	"MTS Batch1 Mix1"	total_RNA	8535	"MTS Site8 Batch1 Mix1 (Cy3) Cy3"	synthetic_DNA	Cy3	8588	"MTS Site8 Batch1 rMix2 gMix1"	A-AGIL-6	8590	Site8_Batch1_rMix2_gMix1	Site8_Batch1_rMix2_gMix1.txt			Site8_Batch1_rMix2_gMix1			Batch1	Mix1;Mix2	Site8	Agilent
+"MTS Batch2"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch2"	organism_part	6396-mix2	"MTS Batch2 Mix2"	total_RNA	8535	"MTS Site8 Batch2 Mix2 (Cy3) Cy3"	synthetic_DNA	Cy3	8588	"MTS Site8 Batch2 rMix1 gMix2"	A-AGIL-6	8590	Site8_Batch2_rMix1_gMix2	Site8_Batch2_rMix1_gMix2.txt			Site8_Batch2_rMix1_gMix2			Batch2	Mix1;Mix2	Site8	Agilent
+"MTS Batch2"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch2"	organism_part	6396-mix1	"MTS Batch2 Mix1"	total_RNA	8536	"MTS Site8 Batch2 Mix1 (Cy5) Cy5"	synthetic_DNA	Cy5	8588	"MTS Site8 Batch2 rMix1 gMix2"	A-AGIL-6	8590	Site8_Batch2_rMix1_gMix2	Site8_Batch2_rMix1_gMix2.txt			Site8_Batch2_rMix1_gMix2			Batch2	Mix1;Mix2	Site8	Agilent
+"MTS Batch2"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch2"	organism_part	6396-mix2	"MTS Batch2 Mix2"	total_RNA	8536	"MTS Site8 Batch2 Mix2 (Cy5) Cy5"	synthetic_DNA	Cy5	8588	"MTS Site8 Batch2 rMix2 gMix1"	A-AGIL-6	8590	Site8_Batch2_rMix2_gMix1	Site8_Batch2_rMix2_gMix1.txt			Site8_Batch2_rMix2_gMix1			Batch2	Mix1;Mix2	Site8	Agilent
+"MTS Batch2"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch2"	organism_part	6396-mix1	"MTS Batch2 Mix1"	total_RNA	8535	"MTS Site8 Batch2 Mix1 (Cy3) Cy3"	synthetic_DNA	Cy3	8588	"MTS Site8 Batch2 rMix2 gMix1"	A-AGIL-6	8590	Site8_Batch2_rMix2_gMix1	Site8_Batch2_rMix2_gMix1.txt			Site8_Batch2_rMix2_gMix1			Batch2	Mix1;Mix2	Site8	Agilent
+"MTS Batch3"	whole_organism	"6-7 weeks"	"Harlan, Frederick, MD"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch3"	organism_part	6396-mix1	"MTS Batch3 Mix1"	total_RNA	8536	"MTS Site8 Batch3 Mix1 (Cy5) Cy5"	synthetic_DNA	Cy5	8588	"MTS Site8 Batch3 rMix1 gMix2"	A-AGIL-6	8590	Site8_Batch3_rMix1_gMix2	Site8_Batch3_rMix1_gMix2.txt			Site8_Batch3_rMix1_gMix2			Batch3	Mix1;Mix2	Site8	Agilent
+"MTS Batch3"	whole_organism	"6-7 weeks"	"Harlan, Frederick, MD"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch3"	organism_part	6396-mix2	"MTS Batch3 Mix2"	total_RNA	8535	"MTS Site8 Batch3 Mix2 (Cy3) Cy3"	synthetic_DNA	Cy3	8588	"MTS Site8 Batch3 rMix1 gMix2"	A-AGIL-6	8590	Site8_Batch3_rMix1_gMix2	Site8_Batch3_rMix1_gMix2.txt			Site8_Batch3_rMix1_gMix2			Batch3	Mix1;Mix2	Site8	Agilent
+"MTS Batch3"	whole_organism	"6-7 weeks"	"Harlan, Frederick, MD"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch3"	organism_part	6396-mix2	"MTS Batch3 Mix2"	total_RNA	8536	"MTS Site8 Batch3 Mix2 (Cy5) Cy5"	synthetic_DNA	Cy5	8588	"MTS Site8 Batch3 rMix2 gMix1"	A-AGIL-6	8590	Site8_Batch3_rMix2_gMix1	Site8_Batch3_rMix2_gMix1.txt			Site8_Batch3_rMix2_gMix1			Batch3	Mix1;Mix2	Site8	Agilent
+"MTS Batch3"	whole_organism	"6-7 weeks"	"Harlan, Frederick, MD"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch3"	organism_part	6396-mix1	"MTS Batch3 Mix1"	total_RNA	8535	"MTS Site8 Batch3 Mix1 (Cy3) Cy3"	synthetic_DNA	Cy3	8588	"MTS Site8 Batch3 rMix2 gMix1"	A-AGIL-6	8590	Site8_Batch3_rMix2_gMix1	Site8_Batch3_rMix2_gMix1.txt			Site8_Batch3_rMix2_gMix1			Batch3	Mix1;Mix2	Site8	Agilent
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix2	"MTS Batch1 Mix2"	total_RNA	8537	"MTS Site9 Batch1 Mix2 (Cy3) Cy3"	synthetic_DNA	Cy3	8589	"MTS Site9 Batch1 rMix1 gMix2"	A-AGIL-6	8590	Site9_Batch1_rMix1_gMix2	Site9_Batch1_rMix1_gMix2.txt			Site9_Batch1_rMix1_gMix2			Batch1	Mix1;Mix2	Site9	Agilent
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix1	"MTS Batch1 Mix1"	total_RNA	8538	"MTS Site9 Batch1 Mix1 (Cy5) Cy5"	synthetic_DNA	Cy5	8589	"MTS Site9 Batch1 rMix1 gMix2"	A-AGIL-6	8590	Site9_Batch1_rMix1_gMix2	Site9_Batch1_rMix1_gMix2.txt			Site9_Batch1_rMix1_gMix2			Batch1	Mix1;Mix2	Site9	Agilent
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix2	"MTS Batch1 Mix2"	total_RNA	8538	"MTS Site9 Batch1 Mix2 (Cy5) Cy5"	synthetic_DNA	Cy5	8589	"MTS Site9 Batch1 rMix2 gMix1"	A-AGIL-6	8590	Site9_Batch1_rMix2_gMix1	Site9_Batch1_rMix2_gMix1.txt			Site9_Batch1_rMix2_gMix1			Batch1	Mix1;Mix2	Site9	Agilent
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix1	"MTS Batch1 Mix1"	total_RNA	8537	"MTS Site9 Batch1 Mix1 (Cy3) Cy3"	synthetic_DNA	Cy3	8589	"MTS Site9 Batch1 rMix2 gMix1"	A-AGIL-6	8590	Site9_Batch1_rMix2_gMix1	Site9_Batch1_rMix2_gMix1.txt			Site9_Batch1_rMix2_gMix1			Batch1	Mix1;Mix2	Site9	Agilent
+"MTS Batch2"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch2"	organism_part	6396-mix2	"MTS Batch2 Mix2"	total_RNA	8537	"MTS Site9 Batch2 Mix2 (Cy3) Cy3"	synthetic_DNA	Cy3	8589	"MTS Site9 Batch2 rMix1 gMix2"	A-AGIL-6	8590	Site9_Batch2_rMix1_gMix2	Site9_Batch2_rMix1_gMix2.txt			Site9_Batch2_rMix1_gMix2			Batch2	Mix1;Mix2	Site9	Agilent
+"MTS Batch2"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch2"	organism_part	6396-mix1	"MTS Batch2 Mix1"	total_RNA	8538	"MTS Site9 Batch2 Mix1 (Cy5) Cy5"	synthetic_DNA	Cy5	8589	"MTS Site9 Batch2 rMix1 gMix2"	A-AGIL-6	8590	Site9_Batch2_rMix1_gMix2	Site9_Batch2_rMix1_gMix2.txt			Site9_Batch2_rMix1_gMix2			Batch2	Mix1;Mix2	Site9	Agilent
+"MTS Batch2"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch2"	organism_part	6396-mix1	"MTS Batch2 Mix1"	total_RNA	8537	"MTS Site9 Batch2 Mix1 (Cy3) Cy3"	synthetic_DNA	Cy3	8589	"MTS Site9 Batch2 rMix2 gMix1"	A-AGIL-6	8590	Site9_Batch2_rMix2_gMix1	Site9_Batch2_rMix2_gMix1.txt			Site9_Batch2_rMix2_gMix1			Batch2	Mix1;Mix2	Site9	Agilent
+"MTS Batch2"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch2"	organism_part	6396-mix2	"MTS Batch2 Mix2"	total_RNA	8538	"MTS Site9 Batch2 Mix2 (Cy5) Cy5"	synthetic_DNA	Cy5	8589	"MTS Site9 Batch2 rMix2 gMix1"	A-AGIL-6	8590	Site9_Batch2_rMix2_gMix1	Site9_Batch2_rMix2_gMix1.txt			Site9_Batch2_rMix2_gMix1			Batch2	Mix1;Mix2	Site9	Agilent
+"MTS Batch3"	whole_organism	"6-7 weeks"	"Harlan, Frederick, MD"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch3"	organism_part	6396-mix2	"MTS Batch3 Mix2"	total_RNA	8537	"MTS Site9 Batch3 Mix2 (Cy3) Cy3"	synthetic_DNA	Cy3	8589	"MTS Site9 Batch3 rMix1 gMix2"	A-AGIL-6	8590	Site9_Batch3_rMix1_gMix2	Site9_Batch3_rMix1_gMix2.txt			Site9_Batch3_rMix1_gMix2			Batch3	Mix1;Mix2	Site9	Agilent
+"MTS Batch3"	whole_organism	"6-7 weeks"	"Harlan, Frederick, MD"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch3"	organism_part	6396-mix1	"MTS Batch3 Mix1"	total_RNA	8538	"MTS Site9 Batch3 Mix1 (Cy5) Cy5"	synthetic_DNA	Cy5	8589	"MTS Site9 Batch3 rMix1 gMix2"	A-AGIL-6	8590	Site9_Batch3_rMix1_gMix2	Site9_Batch3_rMix1_gMix2.txt			Site9_Batch3_rMix1_gMix2			Batch3	Mix1;Mix2	Site9	Agilent
+"MTS Batch3"	whole_organism	"6-7 weeks"	"Harlan, Frederick, MD"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch3"	organism_part	6396-mix2	"MTS Batch3 Mix2"	total_RNA	8538	"MTS Site9 Batch3 Mix2 (Cy5) Cy5"	synthetic_DNA	Cy5	8589	"MTS Site9 Batch3 rMix2 gMix1"	A-AGIL-6	8590	Site9_Batch3_rMix2_gMix1	Site9_Batch3_rMix2_gMix1.txt			Site9_Batch3_rMix2_gMix1			Batch3	Mix1;Mix2	Site9	Agilent
+"MTS Batch3"	whole_organism	"6-7 weeks"	"Harlan, Frederick, MD"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch3"	organism_part	6396-mix1	"MTS Batch3 Mix1"	total_RNA	8537	"MTS Site9 Batch3 Mix1 (Cy3) Cy3"	synthetic_DNA	Cy3	8589	"MTS Site9 Batch3 rMix2 gMix1"	A-AGIL-6	8590	Site9_Batch3_rMix2_gMix1	Site9_Batch3_rMix2_gMix1.txt			Site9_Batch3_rMix2_gMix1			Batch3	Mix1;Mix2	Site9	Agilent
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix1	"MTS Batch1 Mix1"	total_RNA	8538	"MTS Site3 Batch1 Mix1 (Cy5) Cy5"	synthetic_DNA	Cy5	8589	"MTS Site3 Batch1 rMix1 gMix2"	A-AGIL-6	8590	Site3_Batch1_rMix1_gMix2	Site3_Batch1_rMix1_gMix2.txt			Site3_Batch1_rMix1_gMix2			Batch1	Mix1;Mix2	Site3	Agilent
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix2	"MTS Batch1 Mix2"	total_RNA	8537	"MTS Site3 Batch1 Mix2 (Cy3) Cy3"	synthetic_DNA	Cy3	8589	"MTS Site3 Batch1 rMix1 gMix2"	A-AGIL-6	8590	Site3_Batch1_rMix1_gMix2	Site3_Batch1_rMix1_gMix2.txt			Site3_Batch1_rMix1_gMix2			Batch1	Mix1;Mix2	Site3	Agilent
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix2	"MTS Batch1 Mix2"	total_RNA	8538	"MTS Site3 Batch1 Mix2 (Cy5) Cy5"	synthetic_DNA	Cy5	8589	"MTS Site3 Batch1 rMix2 gMix1"	A-AGIL-6	8590	Site3_Batch1_rMix2_gMix1	Site3_Batch1_rMix2_gMix1.txt			Site3_Batch1_rMix2_gMix1			Batch1	Mix1;Mix2	Site3	Agilent
+"MTS Batch1"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch1"	organism_part	6396-mix1	"MTS Batch1 Mix1"	total_RNA	8537	"MTS Site3 Batch1 Mix1 (Cy3) Cy3"	synthetic_DNA	Cy3	8589	"MTS Site3 Batch1 rMix2 gMix1"	A-AGIL-6	8590	Site3_Batch1_rMix2_gMix1	Site3_Batch1_rMix2_gMix1.txt			Site3_Batch1_rMix2_gMix1			Batch1	Mix1;Mix2	Site3	Agilent
+"MTS Batch2"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch2"	organism_part	6396-mix2	"MTS Batch2 Mix2"	total_RNA	8537	"MTS Site3 Batch2 Mix2 (Cy3) Cy3"	synthetic_DNA	Cy3	8589	"MTS Site3 Batch2 rMix1 gMix2"	A-AGIL-6	8590	Site3_Batch2_rMix1_gMix2	Site3_Batch2_rMix1_gMix2.txt			Site3_Batch2_rMix1_gMix2			Batch2	Mix1;Mix2	Site3	Agilent
+"MTS Batch2"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch2"	organism_part	6396-mix1	"MTS Batch2 Mix1"	total_RNA	8538	"MTS Site3 Batch2 Mix1 (Cy5) Cy5"	synthetic_DNA	Cy5	8589	"MTS Site3 Batch2 rMix1 gMix2"	A-AGIL-6	8590	Site3_Batch2_rMix1_gMix2	Site3_Batch2_rMix1_gMix2.txt			Site3_Batch2_rMix1_gMix2			Batch2	Mix1;Mix2	Site3	Agilent
+"MTS Batch2"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch2"	organism_part	6396-mix2	"MTS Batch2 Mix2"	total_RNA	8538	"MTS Site3 Batch2 Mix2 (Cy5) Cy5"	synthetic_DNA	Cy5	8589	"MTS Site3 Batch2 rMix2 gMix1"	A-AGIL-6	8590	Site3_Batch2_rMix2_gMix1	Site3_Batch2_rMix2_gMix1.txt			Site3_Batch2_rMix2_gMix1			Batch2	Mix1;Mix2	Site3	Agilent
+"MTS Batch2"	whole_organism	"6-7 weeks"	"Charles River, Raleigh, NC"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch2"	organism_part	6396-mix1	"MTS Batch2 Mix1"	total_RNA	8537	"MTS Site3 Batch2 Mix1 (Cy3) Cy3"	synthetic_DNA	Cy3	8589	"MTS Site3 Batch2 rMix2 gMix1"	A-AGIL-6	8590	Site3_Batch2_rMix2_gMix1	Site3_Batch2_rMix2_gMix1.txt			Site3_Batch2_rMix2_gMix1			Batch2	Mix1;Mix2	Site3	Agilent
+"MTS Batch3"	whole_organism	"6-7 weeks"	"Harlan, Frederick, MD"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch3"	organism_part	6396-mix2	"MTS Batch3 Mix2"	total_RNA	8537	"MTS Site3 Batch3 Mix2 (Cy3) Cy3"	synthetic_DNA	Cy3	8589	"MTS Site3 Batch3 rMix1 gMix2"	A-AGIL-6	8590	Site3_Batch3_rMix1_gMix2	Site3_Batch3_rMix1_gMix2.txt			Site3_Batch3_rMix1_gMix2			Batch3	Mix1;Mix2	Site3	Agilent
+"MTS Batch3"	whole_organism	"6-7 weeks"	"Harlan, Frederick, MD"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch3"	organism_part	6396-mix1	"MTS Batch3 Mix1"	total_RNA	8538	"MTS Site3 Batch3 Mix1 (Cy5) Cy5"	synthetic_DNA	Cy5	8589	"MTS Site3 Batch3 rMix1 gMix2"	A-AGIL-6	8590	Site3_Batch3_rMix1_gMix2	Site3_Batch3_rMix1_gMix2.txt			Site3_Batch3_rMix1_gMix2			Batch3	Mix1;Mix2	Site3	Agilent
+"MTS Batch3"	whole_organism	"6-7 weeks"	"Harlan, Frederick, MD"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch3"	organism_part	6396-mix1	"MTS Batch3 Mix1"	total_RNA	8537	"MTS Site3 Batch3 Mix1 (Cy3) Cy3"	synthetic_DNA	Cy3	8589	"MTS Site3 Batch3 rMix2 gMix1"	A-AGIL-6	8590	Site3_Batch3_rMix2_gMix1	Site3_Batch3_rMix2_gMix1.txt			Site3_Batch3_rMix2_gMix1			Batch3	Mix1;Mix2	Site3	Agilent
+"MTS Batch3"	whole_organism	"6-7 weeks"	"Harlan, Frederick, MD"	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch3"	organism_part	6396-mix2	"MTS Batch3 Mix2"	total_RNA	8538	"MTS Site3 Batch3 Mix2 (Cy5) Cy5"	synthetic_DNA	Cy5	8589	"MTS Site3 Batch3 rMix2 gMix1"	A-AGIL-6	8590	Site3_Batch3_rMix2_gMix1	Site3_Batch3_rMix2_gMix1.txt			Site3_Batch3_rMix2_gMix1			Batch3	Mix1;Mix2	Site3	Agilent
+"MTS Batch6"	whole_organism	"6-7 weeks"	Site3	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch6"	organism_part	6396-mix1	"MTS Batch6 Mix1"	total_RNA	6486	"MTS Site3 Batch6 Mix1 biotin"	synthetic_DNA	biotin	6491	"MTS Site1 Batch6 Mix1"	A-AFFY-25	6407	Site1_Batch6_Mix1	Site1_Batch6_Mix1.CEL	Site1_Batch6_Mix1.EXP		"MTS Site1 Batch6 Mix1"		RAE230A.CDF	Batch6	Mix1	Site1	Affymetrix
+"MTS Batch6"	whole_organism	"6-7 weeks"	Site3	"Rattus norvegicus"	male	Sprague-Dawley	6377	6380	"MTS Batch6"	organism_part	6396-mix2	"MTS Batch6 Mix2"	total_RNA	6486	"MTS Site1 Batch6 Mix2 biotin"	synthetic_DNA	biotin	6491	"MTS Site1 Batch6 Mix2"	A-AFFY-25	6407	Site1_Batch6_Mix2	Site1_Batch6_Mix2.CEL	Site1_Batch6_Mix2.EXP		"MTS Site1 Batch6 Mix2"		RAE230A.CDF	Batch6	Mix2	Site1	Affymetrix
+Site1_Batch1_Mix1	whole_organism								Site1_Batch1_Mix1	organism_part		Site1_Batch1_Mix1	total_RNA		"Site1_Batch1_Mix1 biotin"	synthetic_DNA	biotin	6491	"MTS Site1 Batch1 Mix1"	A-AFFY-25	6407	Site1_Batch1_Mix1	Site1_Batch1_Mix1.CEL	Site1_Batch1_Mix1.EXP	6552	Site1_Batch1_Mix1_MAS5	Site1_Batch1_Mix1_MAS5.CHP	RAE230A.CDF	Batch1	Mix1	Site1	Affymetrix
+Site1_Batch1_Mix2	whole_organism								Site1_Batch1_Mix2	organism_part		Site1_Batch1_Mix2	total_RNA		"Site1_Batch1_Mix2 biotin"	synthetic_DNA	biotin	6491	"MTS Site1 Batch1 Mix2"	A-AFFY-25	6407	Site1_Batch1_Mix2	Site1_Batch1_Mix2.CEL	Site1_Batch1_Mix2.EXP	6552	Site1_Batch1_Mix2_MAS5	Site1_Batch1_Mix2_MAS5.CHP	RAE230A.CDF	Batch1	Mix2	Site1	Affymetrix
+Site1_Batch2_Mix1	whole_organism								Site1_Batch2_Mix1	organism_part		Site1_Batch2_Mix1	total_RNA		"Site1_Batch2_Mix1 biotin"	synthetic_DNA	biotin	6491	"MTS Site1 Batch2 Mix1"	A-AFFY-25	6407	Site1_Batch2_Mix1	Site1_Batch2_Mix1.CEL	Site1_Batch2_Mix1.EXP	6552	Site1_Batch2_Mix1_MAS5	Site1_Batch2_Mix1_MAS5.CHP	RAE230A.CDF	Batch2	Mix1	Site1	Affymetrix
+Site1_Batch2_Mix2	whole_organism								Site1_Batch2_Mix2	organism_part		Site1_Batch2_Mix2	total_RNA		"Site1_Batch2_Mix2 biotin"	synthetic_DNA	biotin	6491	"MTS Site1 Batch2 Mix2"	A-AFFY-25	6407	Site1_Batch2_Mix2	Site1_Batch2_Mix2.CEL	Site1_Batch2_Mix2.EXP	6552	Site1_Batch2_Mix2_MAS5	Site1_Batch2_Mix2_MAS5.CHP	RAE230A.CDF	Batch2	Mix2	Site1	Affymetrix
+Site1_Batch3_Mix1	whole_organism								Site1_Batch3_Mix1	organism_part		Site1_Batch3_Mix1	total_RNA		"Site1_Batch3_Mix1 biotin"	synthetic_DNA	biotin	6491	"MTS Site1 Batch3 Mix1"	A-AFFY-25	6407	Site1_Batch3_Mix1	Site1_Batch3_Mix1.CEL	Site1_Batch3_Mix1.EXP	6552	Site1_Batch3_Mix1_MAS5	Site1_Batch3_Mix1_MAS5.CHP	RAE230A.CDF	Batch3	Mix1	Site1	Affymetrix
+Site1_Batch3_Mix2	whole_organism								Site1_Batch3_Mix2	organism_part		Site1_Batch3_Mix2	total_RNA		"Site1_Batch3_Mix2 biotin"	synthetic_DNA	biotin	6491	"MTS Site1 Batch3 Mix2"	A-AFFY-25	6407	Site1_Batch3_Mix2	Site1_Batch3_Mix2.CEL	Site1_Batch3_Mix2.EXP	6552	Site1_Batch3_Mix2_MAS5	Site1_Batch3_Mix2_MAS5.CHP	RAE230A.CDF	Batch3	Mix2	Site1	Affymetrix
+Site2_Batch1_Mix1	whole_organism								Site2_Batch1_Mix1	organism_part		Site2_Batch1_Mix1	total_RNA		"Site2_Batch1_Mix1 biotin"	synthetic_DNA	biotin	6491	"MTS Site2 Batch1 Mix1"	A-AFFY-25	6407	Site2_Batch1_Mix1	Site2_Batch1_Mix1.CEL	Site2_Batch1_Mix1.EXP	6552	Site2_Batch1_Mix1_MAS5	Site2_Batch1_Mix1_MAS5.CHP	RAE230A.CDF	Batch1	Mix1	Site2	Affymetrix
+Site2_Batch1_Mix2	whole_organism								Site2_Batch1_Mix2	organism_part		Site2_Batch1_Mix2	total_RNA		"Site2_Batch1_Mix2 biotin"	synthetic_DNA	biotin	6491	"MTS Site2 Batch1 Mix2"	A-AFFY-25	6407	Site2_Batch1_Mix2	Site2_Batch1_Mix2.CEL	Site2_Batch1_Mix2.EXP	6552	Site2_Batch1_Mix2_MAS5	Site2_Batch1_Mix2_MAS5.CHP	RAE230A.CDF	Batch1	Mix2	Site2	Affymetrix
+Site2_Batch2_Mix1	whole_organism								Site2_Batch2_Mix1	organism_part		Site2_Batch2_Mix1	total_RNA		"Site2_Batch2_Mix1 biotin"	synthetic_DNA	biotin	6491	"MTS Site2 Batch2 Mix1"	A-AFFY-25	6407	Site2_Batch2_Mix1	Site2_Batch2_Mix1.CEL	Site2_Batch2_Mix1.EXP	6552	Site2_Batch2_Mix1_MAS5	Site2_Batch2_Mix1_MAS5.CHP	RAE230A.CDF	Batch2	Mix1	Site2	Affymetrix
+Site2_Batch2_Mix2	whole_organism								Site2_Batch2_Mix2	organism_part		Site2_Batch2_Mix2	total_RNA		"Site2_Batch2_Mix2 biotin"	synthetic_DNA	biotin	6491	"MTS Site2 Batch2 Mix2"	A-AFFY-25	6407	Site2_Batch2_Mix2	Site2_Batch2_Mix2.CEL	Site2_Batch2_Mix2.EXP	6552	Site2_Batch2_Mix2_MAS5	Site2_Batch2_Mix2_MAS5.CHP	RAE230A.CDF	Batch2	Mix2	Site2	Affymetrix
+Site2_Batch3_Mix1	whole_organism								Site2_Batch3_Mix1	organism_part		Site2_Batch3_Mix1	total_RNA		"Site2_Batch3_Mix1 biotin"	synthetic_DNA	biotin	6491	"MTS Site2 Batch3 Mix1"	A-AFFY-25	6407	Site2_Batch3_Mix1	Site2_Batch3_Mix1.CEL	Site2_Batch3_Mix1.EXP	6552	Site2_Batch3_Mix1_MAS5	Site2_Batch3_Mix1_MAS5.CHP	RAE230A.CDF	Batch3	Mix1	Site2	Affymetrix
+Site2_Batch3_Mix2	whole_organism								Site2_Batch3_Mix2	organism_part		Site2_Batch3_Mix2	total_RNA		"Site2_Batch3_Mix2 biotin"	synthetic_DNA	biotin	6491	"MTS Site2 Batch3 Mix2"	A-AFFY-25	6407	Site2_Batch3_Mix2	Site2_Batch3_Mix2.CEL	Site2_Batch3_Mix2.EXP	6552	Site2_Batch3_Mix2_MAS5	Site2_Batch3_Mix2_MAS5.CHP	RAE230A.CDF	Batch3	Mix2	Site2	Affymetrix
+Site3_Batch1_Mix1	whole_organism								Site3_Batch1_Mix1	organism_part		Site3_Batch1_Mix1	total_RNA		"Site3_Batch1_Mix1 biotin"	synthetic_DNA	biotin	6491	"MTS Site3 Batch1 Mix1"	A-AFFY-25	6409	Site3_Batch1_Mix1	Site3_Batch1_Mix1.CEL	Site3_Batch1_Mix1.EXP	6552	Site3_Batch1_Mix1_MAS5	Site3_Batch1_Mix1_MAS5.CHP	RAE230A.CDF	Batch1	Mix1	Site3	Affymetrix
+Site3_Batch1_Mix2	whole_organism								Site3_Batch1_Mix2	organism_part		Site3_Batch1_Mix2	total_RNA		"Site3_Batch1_Mix2 biotin"	synthetic_DNA	biotin	6491	"MTS Site3 Batch1 Mix2"	A-AFFY-25	6409	Site3_Batch1_Mix2	Site3_Batch1_Mix2.CEL	Site3_Batch1_Mix2.EXP	6552	Site3_Batch1_Mix2_MAS5	Site3_Batch1_Mix2_MAS5.CHP	RAE230A.CDF	Batch1	Mix2	Site3	Affymetrix
+Site3_Batch2_Mix1	whole_organism								Site3_Batch2_Mix1	organism_part		Site3_Batch2_Mix1	total_RNA		"Site3_Batch2_Mix1 biotin"	synthetic_DNA	biotin	6491	"MTS Site3 Batch2 Mix1"	A-AFFY-25	6409	Site3_Batch2_Mix1	Site3_Batch2_Mix1.CEL	Site3_Batch2_Mix1.EXP	6552	Site3_Batch2_Mix1_MAS5	Site3_Batch2_Mix1_MAS5.CHP	RAE230A.CDF	Batch2	Mix1	Site3	Affymetrix
+Site3_Batch2_Mix2	whole_organism								Site3_Batch2_Mix2	organism_part		Site3_Batch2_Mix2	total_RNA		"Site3_Batch2_Mix2 biotin"	synthetic_DNA	biotin	6491	"MTS Site3 Batch2 Mix2"	A-AFFY-25	6409	Site3_Batch2_Mix2	Site3_Batch2_Mix2.CEL	Site3_Batch2_Mix2.EXP	6552	Site3_Batch2_Mix2_MAS5	Site3_Batch2_Mix2_MAS5.CHP	RAE230A.CDF	Batch2	Mix2	Site3	Affymetrix
+Site3_Batch3_Mix1	whole_organism								Site3_Batch3_Mix1	organism_part		Site3_Batch3_Mix1	total_RNA		"Site3_Batch3_Mix1 biotin"	synthetic_DNA	biotin	6491	"MTS Site3 Batch3 Mix1"	A-AFFY-25	6409	Site3_Batch3_Mix1	Site3_Batch3_Mix1.CEL	Site3_Batch3_Mix1.EXP	6552	Site3_Batch3_Mix1_MAS5	Site3_Batch3_Mix1_MAS5.CHP	RAE230A.CDF	Batch3	Mix1	Site3	Affymetrix
+Site3_Batch3_Mix2	whole_organism								Site3_Batch3_Mix2	organism_part		Site3_Batch3_Mix2	total_RNA		"Site3_Batch3_Mix2 biotin"	synthetic_DNA	biotin	6491	"MTS Site3 Batch3 Mix2"	A-AFFY-25	6409	Site3_Batch3_Mix2	Site3_Batch3_Mix2.CEL	Site3_Batch3_Mix2.EXP	6552	Site3_Batch3_Mix2_MAS5	Site3_Batch3_Mix2_MAS5.CHP	RAE230A.CDF	Batch3	Mix2	Site3	Affymetrix
diff --git a/examples/magetab/real/E-TABM-18.idf b/examples/magetab/real/E-TABM-18.idf
new file mode 100644
index 0000000..47b9cca
--- /dev/null
+++ b/examples/magetab/real/E-TABM-18.idf
@@ -0,0 +1,26 @@
+"Investigation Title"	Ecotype			
+"Experimental Design"	strain_or_line_design			
+"Experimental Factor Name"	ECOTYPE			
+"Experimental Factor Type"	ecotype			
+				
+"Person Last Name"	Lempe			
+"Person First Name"	Janne			
+"Person Mid Initial"				
+"Person Affiliation"	"Max Planck Institute for Developmental Biology, Department of Molecular Biology, Detlef Weigel"			
+"Person Roles"	submitter			
+				
+"Public Release Date"	"2005-06-15"			
+				
+Comment[ArrayExpressSubmissionDate]				
+				
+"Publication Author List"	"Janne Lempe;Sureshkumar Balasubramanian;Sridevi Sureshkumar;Anandita Singh;Markus Schmid;Detlef Weigel"			
+"Publication Title"	"Diversity of flowering responses in wild Arabidopsis thaliana strains"			
+"Experiment Description"	"Seedlings of 35 different Arabidopsis thaliana ecotypes were compared. Triplicates were performed of 10 ecotpyes, single arrays of 25 ecotypes. "			
+				
+"Protocol Name"	P-TABM-Janne5	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4
+"Protocol Type"	growth	extraction	pooling	labeling
+"Protocol Description"	"Plants were grown on soil for 4 days under continuous light  at 23ᄚC and  65% relative humidity. Most replicates were grown at the same time, exceptions to this are indicated by the 'replicate timepoint' parameter."	"pdf of the protocol (Probe preparation Development) can be downloaded at http://www.weigelworld.org/resources/microarray/AtGenExpress/; also at TAIR http://www.arabidopsis.org Protocol:501712557"	"above soil plant material of 15 seedlings was pooled p [...]
+"Protocol Parameters"	"replicate timepoint"			
+"Protocol Software"				
+				
+"SDRF File"	E-TABM-18_sdrf.txt			
diff --git a/examples/magetab/real/E-TABM-18_sdrf.txt b/examples/magetab/real/E-TABM-18_sdrf.txt
new file mode 100644
index 0000000..f12178d
--- /dev/null
+++ b/examples/magetab/real/E-TABM-18_sdrf.txt
@@ -0,0 +1,56 @@
+"Source Name"	"Material Type"	"Characteristics [Ecotype]"	"Characteristics [OrganismPart]"	"Characteristics [Organism]"	"Characteristics [nasc]"	"Protocol REF"	"Parameter Value [replicate timepoint]"	"Sample Name"	"Material Type"	"Protocol REF"	"Protocol REF"	"Extract Name"	"Material Type"	"Protocol REF"	"Labeled Extract Name"	"Material Type"	Label	"Hybridization Name"	"Array Design REF"	"Scan Name"	"Array Data File"	Comment[EXP]	"Normalization Name"	"Derived Array Data File"	Comment[CDF [...]
+AtGE_111_A	organism_part	Bay-0	"seedling, aerial parts"	"Arabidopsis thaliana"	CS954	P-TABM-Janne5	A	AtGE_111_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_111_A	total_RNA	P-TABM-Janne4	"AtGE_111_A biotin"	synthetic_DNA	biotin	ATGE_111_A	A-AFFY-2	ATGE_111_A	ATGE_111_A.CEL	ATGE_111_A.EXP	ATGE_111_A	ATGE_111_A.CHP	ATH1-121501.CDF	Bay-0
+AtGE_111_B	organism_part	Bay-0	"seedling, aerial parts"	"Arabidopsis thaliana"	CS954	P-TABM-Janne5	A	AtGE_111_B	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_111_B	total_RNA	P-TABM-Janne4	"AtGE_111_B biotin"	synthetic_DNA	biotin	ATGE_111_B	A-AFFY-2	ATGE_111_B	ATGE_111_B.CEL	ATGE_111_B.EXP	ATGE_111_B	ATGE_111_B.CHP	ATH1-121501.CDF	Bay-0
+AtGE_111_C	organism_part	Bay-0	"seedling, aerial parts"	"Arabidopsis thaliana"	CS954	P-TABM-Janne5	A	AtGE_111_C	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_111_C	total_RNA	P-TABM-Janne4	"AtGE_111_C biotin"	synthetic_DNA	biotin	ATGE_111_C	A-AFFY-2	ATGE_111_C	ATGE_111_C.CEL	ATGE_111_C.EXP	ATGE_111_C	ATGE_111_C.CHP	ATH1-121501.CDF	Bay-0
+AtGE_112_A	organism_part	C24	"seedling, aerial parts"	"Arabidopsis thaliana"	CS906	P-TABM-Janne5	A	AtGE_112_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_112_A	total_RNA	P-TABM-Janne4	"AtGE_112_A biotin"	synthetic_DNA	biotin	ATGE_112_A	A-AFFY-2	ATGE_112_A	ATGE_112_A.CEL	ATGE_112_A.EXP	ATGE_112_A	ATGE_112_A.CHP	ATH1-121501.CDF	C24
+AtGE_112_C	organism_part	C24	"seedling, aerial parts"	"Arabidopsis thaliana"	CS906	P-TABM-Janne5	A	AtGE_112_C	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_112_C	total_RNA	P-TABM-Janne4	"AtGE_112_C biotin"	synthetic_DNA	biotin	ATGE_112_C	A-AFFY-2	ATGE_112_C	ATGE_112_C.CEL	ATGE_112_C.EXP	ATGE_112_C	ATGE_112_C.CHP	ATH1-121501.CDF	C24
+AtGE_112_D	organism_part	C24	"seedling, aerial parts"	"Arabidopsis thaliana"	CS906	P-TABM-Janne5	A	AtGE_112_D	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_112_D	total_RNA	P-TABM-Janne4	"AtGE_112_D biotin"	synthetic_DNA	biotin	ATGE_112_D	A-AFFY-2	ATGE_112_D	ATGE_112_D.CEL	ATGE_112_D.EXP	ATGE_112_D	ATGE_112_D.CHP	ATH1-121501.CDF	C24
+AtGE_113_A	organism_part	Col-0	"seedling, aerial parts"	"Arabidopsis thaliana"	N1093	P-TABM-Janne5	A	AtGE_113_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_113_A	total_RNA	P-TABM-Janne4	"AtGE_113_A biotin"	synthetic_DNA	biotin	ATGE_113_A	A-AFFY-2	ATGE_113_A	ATGE_113_A.CEL	ATGE_113_A.EXP	ATGE_113_A	ATGE_113_A.CHP	ATH1-121501.CDF	Col-0
+AtGE_113_C	organism_part	Col-0	"seedling, aerial parts"	"Arabidopsis thaliana"	N1093	P-TABM-Janne5	A	AtGE_113_C	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_113_C	total_RNA	P-TABM-Janne4	"AtGE_113_C biotin"	synthetic_DNA	biotin	ATGE_113_C	A-AFFY-2	ATGE_113_C	ATGE_113_C.CEL	ATGE_113_C.EXP	ATGE_113_C	ATGE_113_C.CHP	ATH1-121501.CDF	Col-0
+AtGE_113_D	organism_part	Col-0	"seedling, aerial parts"	"Arabidopsis thaliana"	N1093	P-TABM-Janne5	B	AtGE_113_D	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_113_D	total_RNA	P-TABM-Janne4	"AtGE_113_D biotin"	synthetic_DNA	biotin	ATGE_113_D	A-AFFY-2	ATGE_113_D	ATGE_113_D.CEL	ATGE_113_D.EXP	ATGE_113_D	ATGE_113_D.CHP	ATH1-121501.CDF	Col-0
+AtGE_114_A	organism_part	Cvi	"seedling, aerial parts"	"Arabidopsis thaliana"	CS8580	P-TABM-Janne5	A	AtGE_114_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_114_A	total_RNA	P-TABM-Janne4	"AtGE_114_A biotin"	synthetic_DNA	biotin	ATGE_114_A	A-AFFY-2	ATGE_114_A	ATGE_114_A.CEL	ATGE_114_A.EXP	ATGE_114_A	ATGE_114_A.CHP	ATH1-121501.CDF	Cvi
+AtGE_114_B	organism_part	Cvi	"seedling, aerial parts"	"Arabidopsis thaliana"	CS8580	P-TABM-Janne5	A	AtGE_114_B	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_114_B	total_RNA	P-TABM-Janne4	"AtGE_114_B biotin"	synthetic_DNA	biotin	ATGE_114_B	A-AFFY-2	ATGE_114_B	ATGE_114_B.CEL	ATGE_114_B.EXP	ATGE_114_B	ATGE_114_B.CHP	ATH1-121501.CDF	Cvi
+AtGE_114_C	organism_part	Cvi	"seedling, aerial parts"	"Arabidopsis thaliana"	CS8580	P-TABM-Janne5	A	AtGE_114_C	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_114_C	total_RNA	P-TABM-Janne4	"AtGE_114_C biotin"	synthetic_DNA	biotin	ATGE_114_C	A-AFFY-2	ATGE_114_C	ATGE_114_C.CEL	ATGE_114_C.EXP	ATGE_114_C	ATGE_114_C.CHP	ATH1-121501.CDF	Cvi
+AtGE_115_A	organism_part	Est	"seedling, aerial parts"	"Arabidopsis thaliana"	CS6173	P-TABM-Janne5	A	AtGE_115_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_115_A	total_RNA	P-TABM-Janne4	"AtGE_115_A biotin"	synthetic_DNA	biotin	ATGE_115_A	A-AFFY-2	ATGE_115_A	ATGE_115_A.CEL	ATGE_115_A.EXP	ATGE_115_A	ATGE_115_A.CHP	ATH1-121501.CDF	Est
+AtGE_115_B	organism_part	Est	"seedling, aerial parts"	"Arabidopsis thaliana"	CS6173	P-TABM-Janne5	A	AtGE_115_B	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_115_B	total_RNA	P-TABM-Janne4	"AtGE_115_B biotin"	synthetic_DNA	biotin	ATGE_115_B	A-AFFY-2	ATGE_115_B	ATGE_115_B.CEL	ATGE_115_B.EXP	ATGE_115_B	ATGE_115_B.CHP	ATH1-121501.CDF	Est
+AtGE_115_D	organism_part	Est	"seedling, aerial parts"	"Arabidopsis thaliana"	CS6173	P-TABM-Janne5	B	AtGE_115_D	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_115_D	total_RNA	P-TABM-Janne4	"AtGE_115_D biotin"	synthetic_DNA	biotin	ATGE_115_D	A-AFFY-2	ATGE_115_D	ATGE_115_D.CEL	ATGE_115_D.EXP	ATGE_115_D	ATGE_115_D.CHP	ATH1-121501.CDF	Est
+AtGE_116_A	organism_part	Kin-0	"seedling, aerial parts"	"Arabidopsis thaliana"	CS6755	P-TABM-Janne5	A	AtGE_116_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_116_A	total_RNA	P-TABM-Janne4	"AtGE_116_A biotin"	synthetic_DNA	biotin	ATGE_116_A	A-AFFY-2	ATGE_116_A	ATGE_116_A.CEL	ATGE_116_A.EXP	ATGE_116_A	ATGE_116_A.CHP	ATH1-121501.CDF	Kin-0
+AtGE_116_B	organism_part	Kin-0	"seedling, aerial parts"	"Arabidopsis thaliana"	CS6755	P-TABM-Janne5	A	AtGE_116_B	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_116_B	total_RNA	P-TABM-Janne4	"AtGE_116_B biotin"	synthetic_DNA	biotin	ATGE_116_B	A-AFFY-2	ATGE_116_B	ATGE_116_B.CEL	ATGE_116_B.EXP	ATGE_116_B	ATGE_116_B.CHP	ATH1-121501.CDF	Kin-0
+AtGE_116_C	organism_part	Kin-0	"seedling, aerial parts"	"Arabidopsis thaliana"	CS6755	P-TABM-Janne5	A	AtGE_116_C	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_116_C	total_RNA	P-TABM-Janne4	"AtGE_116_C biotin"	synthetic_DNA	biotin	ATGE_116_C	A-AFFY-2	ATGE_116_C	ATGE_116_C.CEL	ATGE_116_C.EXP	ATGE_116_C	ATGE_116_C.CHP	ATH1-121501.CDF	Kin-0
+AtGE_117_B	organism_part	Ler	"seedling, aerial parts"	"Arabidopsis thaliana"	CS8581	P-TABM-Janne5	A	AtGE_117_B	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_117_B	total_RNA	P-TABM-Janne4	"AtGE_117_B biotin"	synthetic_DNA	biotin	ATGE_117_B	A-AFFY-2	ATGE_117_B	ATGE_117_B.CEL	ATGE_117_B.EXP	ATGE_117_B	ATGE_117_B.CHP	ATH1-121501.CDF	Ler
+AtGE_117_C	organism_part	Ler	"seedling, aerial parts"	"Arabidopsis thaliana"	CS8581	P-TABM-Janne5	A	AtGE_117_C	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_117_C	total_RNA	P-TABM-Janne4	"AtGE_117_C biotin"	synthetic_DNA	biotin	ATGE_117_C	A-AFFY-2	ATGE_117_C	ATGE_117_C.CEL	ATGE_117_C.EXP	ATGE_117_C	ATGE_117_C.CHP	ATH1-121501.CDF	Ler
+AtGE_117_D	organism_part	Ler	"seedling, aerial parts"	"Arabidopsis thaliana"	CS8581	P-TABM-Janne5	B	AtGE_117_D	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_117_D	total_RNA	P-TABM-Janne4	"AtGE_117_D biotin"	synthetic_DNA	biotin	ATGE_117_D	A-AFFY-2	ATGE_117_D	ATGE_117_D.CEL	ATGE_117_D.EXP	ATGE_117_D	ATGE_117_D.CHP	ATH1-121501.CDF	Ler
+AtGE_118_A	organism_part	Nd-1	"seedling, aerial parts"	"Arabidopsis thaliana"	CS1636	P-TABM-Janne5	A	AtGE_118_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_118_A	total_RNA	P-TABM-Janne4	"AtGE_118_A biotin"	synthetic_DNA	biotin	ATGE_118_A	A-AFFY-2	ATGE_118_A	ATGE_118_A.CEL	ATGE_118_A.EXP	ATGE_118_A	ATGE_118_A.CHP	ATH1-121501.CDF	Nd-1
+AtGE_118_B	organism_part	Nd-1	"seedling, aerial parts"	"Arabidopsis thaliana"	CS1636	P-TABM-Janne5	A	AtGE_118_B	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_118_B	total_RNA	P-TABM-Janne4	"AtGE_118_B biotin"	synthetic_DNA	biotin	ATGE_118_B	A-AFFY-2	ATGE_118_B	ATGE_118_B.CEL	ATGE_118_B.EXP	ATGE_118_B	ATGE_118_B.CHP	ATH1-121501.CDF	Nd-1
+AtGE_118_C	organism_part	Nd-1	"seedling, aerial parts"	"Arabidopsis thaliana"	CS1636	P-TABM-Janne5	A	AtGE_118_C	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_118_C	total_RNA	P-TABM-Janne4	"AtGE_118_C biotin"	synthetic_DNA	biotin	ATGE_118_C	A-AFFY-2	ATGE_118_C	ATGE_118_C.CEL	ATGE_118_C.EXP	ATGE_118_C	ATGE_118_C.CHP	ATH1-121501.CDF	Nd-1
+AtGE_119_A	organism_part	Shahdara	"seedling, aerial parts"	"Arabidopsis thaliana"	CS929	P-TABM-Janne5	A	AtGE_119_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_119_A	total_RNA	P-TABM-Janne4	"AtGE_119_A biotin"	synthetic_DNA	biotin	ATGE_119_A	A-AFFY-2	ATGE_119_A	ATGE_119_A.CEL	ATGE_119_A.EXP	ATGE_119_A	ATGE_119_A.CHP	ATH1-121501.CDF	Shahdara
+AtGE_119_C	organism_part	Shahdara	"seedling, aerial parts"	"Arabidopsis thaliana"	CS929	P-TABM-Janne5	A	AtGE_119_C	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_119_C	total_RNA	P-TABM-Janne4	"AtGE_119_C biotin"	synthetic_DNA	biotin	ATGE_119_C	A-AFFY-2	ATGE_119_C	ATGE_119_C.CEL	ATGE_119_C.EXP	ATGE_119_C	ATGE_119_C.CHP	ATH1-121501.CDF	Shahdara
+AtGE_119_D	organism_part	Shahdara	"seedling, aerial parts"	"Arabidopsis thaliana"	CS929	P-TABM-Janne5	A	AtGE_119_D	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_119_D	total_RNA	P-TABM-Janne4	"AtGE_119_D biotin"	synthetic_DNA	biotin	ATGE_119_D	A-AFFY-2	ATGE_119_D	ATGE_119_D.CEL	ATGE_119_D.EXP	ATGE_119_D	ATGE_119_D.CHP	ATH1-121501.CDF	Shahdara
+AtGE_120_A	organism_part	Van-0	"seedling, aerial parts"	"Arabidopsis thaliana"	CS6884	P-TABM-Janne5	A	AtGE_120_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_120_A	total_RNA	P-TABM-Janne4	"AtGE_120_A biotin"	synthetic_DNA	biotin	ATGE_120_A	A-AFFY-2	ATGE_120_A	ATGE_120_A.CEL	ATGE_120_A.EXP	ATGE_120_A	ATGE_120_A.CHP	ATH1-121501.CDF	Van-0
+AtGE_120_B	organism_part	Van-0	"seedling, aerial parts"	"Arabidopsis thaliana"	CS6884	P-TABM-Janne5	A	AtGE_120_B	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_120_B	total_RNA	P-TABM-Janne4	"AtGE_120_B biotin"	synthetic_DNA	biotin	ATGE_120_B	A-AFFY-2	ATGE_120_B	ATGE_120_B.CEL	ATGE_120_B.EXP	ATGE_120_B	ATGE_120_B.CHP	ATH1-121501.CDF	Van-0
+AtGE_120_C	organism_part	Van-0	"seedling, aerial parts"	"Arabidopsis thaliana"	CS6884	P-TABM-Janne5	A	AtGE_120_C	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_120_C	total_RNA	P-TABM-Janne4	"AtGE_120_C biotin"	synthetic_DNA	biotin	ATGE_120_C	A-AFFY-2	ATGE_120_C	ATGE_120_C.CEL	ATGE_120_C.EXP	ATGE_120_C	ATGE_120_C.CHP	ATH1-121501.CDF	Van-0
+AtGE_121_A	organism_part	Ak-1	"seedling, aerial parts"	"Arabidopsis thaliana"	"N939 "	P-TABM-Janne5		AtGE_121_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_121_A	total_RNA	P-TABM-Janne4	"AtGE_121_A biotin"	synthetic_DNA	biotin	ATGE_121_A	A-AFFY-2	ATGE_121_A	ATGE_121_A.CEL	ATGE_121_A.EXP	ATGE_121_A	ATGE_121_A.CHP	ATH1-121501.CDF	Ak-1
+AtGE_124_A	organism_part	Bla-5	"seedling, aerial parts"	"Arabidopsis thaliana"	"N6620 "	P-TABM-Janne5		AtGE_124_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_124_A	total_RNA	P-TABM-Janne4	"AtGE_124_A biotin"	synthetic_DNA	biotin	ATGE_124_A	A-AFFY-2	ATGE_124_A	ATGE_124_A.CEL	ATGE_124_A.EXP	ATGE_124_A	ATGE_124_A.CHP	ATH1-121501.CDF	Bla-5
+AtGE_125_A	organism_part	Can-0	"seedling, aerial parts"	"Arabidopsis thaliana"	"N1065 "	P-TABM-Janne5		AtGE_125_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_125_A	total_RNA	P-TABM-Janne4	"AtGE_125_A biotin"	synthetic_DNA	biotin	ATGE_125_A	A-AFFY-2	ATGE_125_A	ATGE_125_A.CEL	ATGE_125_A.EXP	ATGE_125_A	ATGE_125_A.CHP	ATH1-121501.CDF	Can-0
+AtGE_126_A	organism_part	Cen-0	"seedling, aerial parts"	"Arabidopsis thaliana"	"N1067 "	P-TABM-Janne5		AtGE_126_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_126_A	total_RNA	P-TABM-Janne4	"AtGE_126_A biotin"	synthetic_DNA	biotin	ATGE_126_A	A-AFFY-2	ATGE_126_A	ATGE_126_A.CEL	ATGE_126_A.EXP	ATGE_126_A	ATGE_126_A.CHP	ATH1-121501.CDF	Cen-0
+AtGE_127_A	organism_part	CIBC10	"seedling, aerial parts"	"Arabidopsis thaliana"	"N22229 "	P-TABM-Janne5		AtGE_127_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_127_A	total_RNA	P-TABM-Janne4	"AtGE_127_A biotin"	synthetic_DNA	biotin	ATGE_127_A	A-AFFY-2	ATGE_127_A	ATGE_127_A.CEL	ATGE_127_A.EXP	ATGE_127_A	ATGE_127_A.CHP	ATH1-121501.CDF	CIBC10
+AtGE_128_A	organism_part	Dra-1	"seedling, aerial parts"	"Arabidopsis thaliana"	"N1119 "	P-TABM-Janne5		AtGE_128_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_128_A	total_RNA	P-TABM-Janne4	"AtGE_128_A biotin"	synthetic_DNA	biotin	ATGE_128_A	A-AFFY-2	ATGE_128_A	ATGE_128_A.CEL	ATGE_128_A.EXP	ATGE_128_A	ATGE_128_A.CHP	ATH1-121501.CDF	Dra-1
+AtGE_129_A	organism_part	Enkheim-T	"seedling, aerial parts"	"Arabidopsis thaliana"	"N6176 "	P-TABM-Janne5		AtGE_129_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_129_A	total_RNA	P-TABM-Janne4	"AtGE_129_A biotin"	synthetic_DNA	biotin	ATGE_129_A	A-AFFY-2	ATGE_129_A	ATGE_129_A.CEL	ATGE_129_A.EXP	ATGE_129_A	ATGE_129_A.CHP	ATH1-121501.CDF	Enkheim-T
+AtGE_130_A	organism_part	Er-0	"seedling, aerial parts"	"Arabidopsis thaliana"	"N1143 "	P-TABM-Janne5		AtGE_130_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_130_A	total_RNA	P-TABM-Janne4	"AtGE_130_A biotin"	synthetic_DNA	biotin	ATGE_130_A	A-AFFY-2	ATGE_130_A	ATGE_130_A.CEL	ATGE_130_A.EXP	ATGE_130_A	ATGE_130_A.CHP	ATH1-121501.CDF	Er-0
+AtGE_131_A	organism_part	Fr-2	"seedling, aerial parts"	"Arabidopsis thaliana"	"N1169 "	P-TABM-Janne5		AtGE_131_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_131_A	total_RNA	P-TABM-Janne4	"AtGE_131_A biotin"	synthetic_DNA	biotin	ATGE_131_A	A-AFFY-2	ATGE_131_A	ATGE_131_A.CEL	ATGE_131_A.EXP	ATGE_131_A	ATGE_131_A.CHP	ATH1-121501.CDF	Fr-2
+AtGE_132_A	organism_part	GOT1	"seedling, aerial parts"	"Arabidopsis thaliana"	"N22277 "	P-TABM-Janne5		AtGE_132_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_132_A	total_RNA	P-TABM-Janne4	"AtGE_132_A biotin"	synthetic_DNA	biotin	ATGE_132_A	A-AFFY-2	ATGE_132_A	ATGE_132_A.CEL	ATGE_132_A.EXP	ATGE_132_A	ATGE_132_A.CHP	ATH1-121501.CDF	GOT1
+AtGE_133_A	organism_part	HR-5	"seedling, aerial parts"	"Arabidopsis thaliana"	"N22205 "	P-TABM-Janne5		AtGE_133_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_133_A	total_RNA	P-TABM-Janne4	"AtGE_133_A biotin"	synthetic_DNA	biotin	ATGE_133_A	A-AFFY-2	ATGE_133_A	ATGE_133_A.CEL	ATGE_133_A.EXP	ATGE_133_A	ATGE_133_A.CHP	ATH1-121501.CDF	HR-5
+AtGE_134_A	organism_part	Is-0	"seedling, aerial parts"	"Arabidopsis thaliana"	"N1241 "	P-TABM-Janne5		AtGE_134_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_134_A	total_RNA	P-TABM-Janne4	"AtGE_134_A biotin"	synthetic_DNA	biotin	ATGE_134_A	A-AFFY-2	ATGE_134_A	ATGE_134_A.CEL	ATGE_134_A.EXP	ATGE_134_A	ATGE_134_A.CHP	ATH1-121501.CDF	Is-0
+AtGE_136_A	organism_part	Li-2:1	"seedling, aerial parts"	"Arabidopsis thaliana"	"N1315 "	P-TABM-Janne5		AtGE_136_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_136_A	total_RNA	P-TABM-Janne4	"AtGE_136_A biotin"	synthetic_DNA	biotin	ATGE_136_A	A-AFFY-2	ATGE_136_A	ATGE_136_A.CEL	ATGE_136_A.EXP	ATGE_136_A	ATGE_136_A.CHP	ATH1-121501.CDF	Li-2:1
+AtGE_137_A	organism_part	M7323S	"seedling, aerial parts"	"Arabidopsis thaliana"	"N6184 "	P-TABM-Janne5		AtGE_137_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_137_A	total_RNA	P-TABM-Janne4	"AtGE_137_A biotin"	synthetic_DNA	biotin	ATGE_137_A	A-AFFY-2	ATGE_137_A	ATGE_137_A.CEL	ATGE_137_A.EXP	ATGE_137_A	ATGE_137_A.CHP	ATH1-121501.CDF	M7323S
+AtGE_138_A	organism_part	Ms-0	"seedling, aerial parts"	"Arabidopsis thaliana"	"N1377 "	P-TABM-Janne5		AtGE_138_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_138_A	total_RNA	P-TABM-Janne4	"AtGE_138_A biotin"	synthetic_DNA	biotin	ATGE_138_A	A-AFFY-2	ATGE_138_A	ATGE_138_A.CEL	ATGE_138_A.EXP	ATGE_138_A	ATGE_138_A.CHP	ATH1-121501.CDF	Ms-0
+AtGE_139_A	organism_part	Nc-1	"seedling, aerial parts"	"Arabidopsis thaliana"	"N1389 "	P-TABM-Janne5		AtGE_139_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_139_A	total_RNA	P-TABM-Janne4	"AtGE_139_A biotin"	synthetic_DNA	biotin	ATGE_139_A	A-AFFY-2	ATGE_139_A	ATGE_139_A.CEL	ATGE_139_A.EXP	ATGE_139_A	ATGE_139_A.CHP	ATH1-121501.CDF	Nc-1
+AtGE_140_A	organism_part	NFE1	"seedling, aerial parts"	"Arabidopsis thaliana"	"N22163 "	P-TABM-Janne5		AtGE_140_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_140_A	total_RNA	P-TABM-Janne4	"AtGE_140_A biotin"	synthetic_DNA	biotin	ATGE_140_A	A-AFFY-2	ATGE_140_A	ATGE_140_A.CEL	ATGE_140_A.EXP	ATGE_140_A	ATGE_140_A.CHP	ATH1-121501.CDF	NFE1
+AtGE_141_A	organism_part	Nok-1	"seedling, aerial parts"	"Arabidopsis thaliana"	"N1401 "	P-TABM-Janne5		AtGE_141_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_141_A	total_RNA	P-TABM-Janne4	"AtGE_141_A biotin"	synthetic_DNA	biotin	ATGE_141_A	A-AFFY-2	ATGE_141_A	ATGE_141_A.CEL	ATGE_141_A.EXP	ATGE_141_A	ATGE_141_A.CHP	ATH1-121501.CDF	Nok-1
+AtGE_142_A	organism_part	Nw-1	"seedling, aerial parts"	"Arabidopsis thaliana"	"N1411 "	P-TABM-Janne5		AtGE_142_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_142_A	total_RNA	P-TABM-Janne4	"AtGE_142_A biotin"	synthetic_DNA	biotin	ATGE_142_A	A-AFFY-2	ATGE_142_A	ATGE_142_A.CEL	ATGE_142_A.EXP	ATGE_142_A	ATGE_142_A.CHP	ATH1-121501.CDF	Nw-1
+AtGE_144_A	organism_part	Old-2	"seedling, aerial parts"	"Arabidopsis thaliana"	"N1429 "	P-TABM-Janne5		AtGE_144_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_144_A	total_RNA	P-TABM-Janne4	"AtGE_144_A biotin"	synthetic_DNA	biotin	ATGE_144_A	A-AFFY-2	ATGE_144_A	ATGE_144_A.CEL	ATGE_144_A.EXP	ATGE_144_A	ATGE_144_A.CHP	ATH1-121501.CDF	Old-2
+AtGE_145_A	organism_part	Ove-0	"seedling, aerial parts"	"Arabidopsis thaliana"	"N1435 "	P-TABM-Janne5		AtGE_145_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_145_A	total_RNA	P-TABM-Janne4	"AtGE_145_A biotin"	synthetic_DNA	biotin	ATGE_145_A	A-AFFY-2	ATGE_145_A	ATGE_145_A.CEL	ATGE_145_A.EXP	ATGE_145_A	ATGE_145_A.CHP	ATH1-121501.CDF	Ove-0
+AtGE_146_A	organism_part	Se-0	"seedling, aerial parts"	"Arabidopsis thaliana"	"N1503 "	P-TABM-Janne5		AtGE_146_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_146_A	total_RNA	P-TABM-Janne4	"AtGE_146_A biotin"	synthetic_DNA	biotin	ATGE_146_A	A-AFFY-2	ATGE_146_A	ATGE_146_A.CEL	ATGE_146_A.EXP	ATGE_146_A	ATGE_146_A.CHP	ATH1-121501.CDF	Se-0
+AtGE_147_A	organism_part	Sf-2e	"seedling, aerial parts"	"Arabidopsis thaliana"	"N1675 "	P-TABM-Janne5		AtGE_147_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_147_A	total_RNA	P-TABM-Janne4	"AtGE_147_A biotin"	synthetic_DNA	biotin	ATGE_147_A	A-AFFY-2	ATGE_147_A	ATGE_147_A.CEL	ATGE_147_A.EXP	ATGE_147_A	ATGE_147_A.CHP	ATH1-121501.CDF	Sf-2e
+AtGE_148_A	organism_part	Ta-0	"seedling, aerial parts"	"Arabidopsis thaliana"	"N1549 "	P-TABM-Janne5		AtGE_148_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_148_A	total_RNA	P-TABM-Janne4	"AtGE_148_A biotin"	synthetic_DNA	biotin	ATGE_148_A	A-AFFY-2	ATGE_148_A	ATGE_148_A.CEL	ATGE_148_A.EXP	ATGE_148_A	ATGE_148_A.CHP	ATH1-121501.CDF	Ta-0
+AtGE_149_A	organism_part	Uk-3	"seedling, aerial parts"	"Arabidopsis thaliana"	"N1577 "	P-TABM-Janne5		AtGE_149_A	organism_part	P-TABM-Janne2	P-TABM-Janne6	AtGE_149_A	total_RNA	P-TABM-Janne4	"AtGE_149_A biotin"	synthetic_DNA	biotin	ATGE_149_A	A-AFFY-2	ATGE_149_A	ATGE_149_A.CEL	ATGE_149_A.EXP	ATGE_149_A	ATGE_149_A.CHP	ATH1-121501.CDF	Uk-3
diff --git a/examples/magetab/real/E-TABM-22.idf b/examples/magetab/real/E-TABM-22.idf
new file mode 100644
index 0000000..0e6ee6f
--- /dev/null
+++ b/examples/magetab/real/E-TABM-22.idf
@@ -0,0 +1,24 @@
+"Investigation Title"	"Lung cancers"						
+"Experimental Design"	cell_type_comparison_design	stimulus_or_stress_design	compound_treatment_design	disease_state_design			
+"Experimental Factor Name"	COMPOUND	DISEASESTATE	DISEASESTAGING	CELLLINE	INDIVIDUAL		
+"Experimental Factor Type"	compound	disease_state	disease_staging	cell_line	individual		
+							
+"Person Last Name"	Yanaihara						
+"Person First Name"	Nozomu						
+"Person Mid Initial"							
+"Person Affiliation"	"Laboratory of Human Carcinogenesis, NCI, NIH"						
+"Person Roles"	submitter						
+							
+"Public Release Date"	"2006-07-01"						
+							
+Comment[ArrayExpressSubmissionDate]	"2005-06-20"						
+							
+"Experiment Description"	"Distinct miRNA expression significance in human lung cancers and lung cancer cell lines."						
+							
+"Protocol Name"	P-MEXP-9141	P-MEXP-9142	P-MEXP-9143	P-MEXP-9144	P-MEXP-9145	P-MEXP-9146	P-MEXP-9147
+"Protocol Type"	grow	compound_based_treatment	nucleic_acid_extraction	labeling	hybridization	image_acquisition	bioassay_data_transformation
+"Protocol Description"	"Human NSCLC cell lines were cultured with 10% FCS containing RPMI 1640 at 37C with 5% CO2."	"For the first 48h, cells were incubated with medium containing 1.0 uM 5-aza-dC, and then for another 24h with the addition of 1.0 uM TSA."	"Total RNA was isolated with TRIzol according to the manufacturer's protocol."	"Five micrograms of total RNA were separately added to reaction mix in a final volume of 12 ul, containing 1 ug of [3'-(N)8-(A)12-biotin-(A)12-biotin-5_] oli [...]
+"Protocol Parameters"	medium	compound		"RNA labelled"	"RNA hybridized; Hyb temperature"		
+"Protocol Software"							
+							
+"SDRF File"	E-TABM-22_sdrf.txt						
diff --git a/examples/magetab/real/E-TABM-22_sdrf.txt b/examples/magetab/real/E-TABM-22_sdrf.txt
new file mode 100644
index 0000000..2287653
--- /dev/null
+++ b/examples/magetab/real/E-TABM-22_sdrf.txt
@@ -0,0 +1,252 @@
+"Source Name"	"Material Type"	"Characteristics [Age]"	"Characteristics [BioSourceType]"	"Characteristics [CellLine]"	"Characteristics [DiseaseStaging]"	"Characteristics [DiseaseState]"	"Characteristics [Individual]"	"Characteristics [Organism]"	"Characteristics [Sex]"	"Characteristics [TimeUnit]"	"Protocol REF"	"Parameter Value [medium]"	"Protocol REF"	"Parameter Value [compound]"	"Sample Name"	"Material Type"	"Protocol REF"	"Extract Name"	"Material Type"	"Protocol REF"	"Parameter Value  [...]
+A549	cell		fresh_sample	A549		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"			A549	cell	P-MEXP-9143	A549	total_RNA	P-MEXP-9144	5	ug	"A549 biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1791_A549	A-MEXP-86	P-MEXP-9146	R1791_A549	R1791_A549.txt	A549			"Non-Small Cell Lung Carcinoma"	
+A-427	cell		fresh_sample	A-427		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"			A-427	cell	P-MEXP-9143	A-427	total_RNA	P-MEXP-9144	5	ug	"A-427 biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1790_A427	A-MEXP-86	P-MEXP-9146	R1790_A427	R1790_A427.txt	A-427			"Non-Small Cell Lung Carcinoma"	
+A2182	cell		fresh_sample	A2182		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"			A2182	cell	P-MEXP-9143	A2182	total_RNA	P-MEXP-9144	5	ug	"A2182 biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1792_A2182	A-MEXP-86	P-MEXP-9146	R1792_A2182	R1792_A2182.txt	A2182			"Non-Small Cell Lung Carcinoma"	
+Calu-1	cell		fresh_sample	Calu-1		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"			Calu-1	cell	P-MEXP-9143	Calu-1	total_RNA	P-MEXP-9144	5	ug	"Calu-1 biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1793_Calu1	A-MEXP-86	P-MEXP-9146	R1793_Calu1	R1793_Calu1.txt	Calu-1			"Non-Small Cell Lung Carcinoma"	
+Calu-6	cell		fresh_sample	Calu-6		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"			Calu-6	cell	P-MEXP-9143	Calu-6	total_RNA	P-MEXP-9144	5	ug	"Calu-6 biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1794_Calu6	A-MEXP-86	P-MEXP-9146	R1794_Calu6	R1794_Calu6.txt	Calu-6			"Non-Small Cell Lung Carcinoma"	
+DMS92	cell		fresh_sample	DMS92		"Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"			DMS92	cell	P-MEXP-9143	DMS92	total_RNA	P-MEXP-9144	5	ug	"DMS92 biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1795_DMS92	A-MEXP-86	P-MEXP-9146	R1795_DMS92	R1795_DMS92.txt	DMS92			"Small Cell Lung Carcinoma"	
+NCI-H82	cell		fresh_sample	NCI-H82		"Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"			NCI-H82	cell	P-MEXP-9143	NCI-H82	total_RNA	P-MEXP-9144	5	ug	"NCI-H82 biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1796_NCIH82	A-MEXP-86	P-MEXP-9146	R1796_NCIH82	R1796_NCIH82.txt	NCI-H82			"Small Cell Lung Carcinoma"	
+NCI-H146	cell		fresh_sample	NCI-H146		"Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"			NCI-H146	cell	P-MEXP-9143	NCI-H146	total_RNA	P-MEXP-9144	5	ug	"NCI-H146 biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1797_NCIH146	A-MEXP-86	P-MEXP-9146	R1797_NCIH146	R1797_NCIH146.txt	NCI-H146			"Small Cell Lung Carcinoma"	
+NCI-H157	cell		fresh_sample	NCI-H157		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"			NCI-H157	cell	P-MEXP-9143	NCI-H157	total_RNA	P-MEXP-9144	5	ug	"NCI-H157 biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1798_NCIH157	A-MEXP-86	P-MEXP-9146	R1798_NCIH157	R1798_NCIH157.txt	NCI-H157			"Non-Small Cell Lung Carcinoma"	
+NCI-H446	cell		fresh_sample	NCI-H446		"Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"			NCI-H446	cell	P-MEXP-9143	NCI-H446	total_RNA	P-MEXP-9144	5	ug	"NCI-H446 biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1805_NCIH446	A-MEXP-86	P-MEXP-9146	R1805_NCIH446	R1805_NCIH446.txt	NCI-H446			"Small Cell Lung Carcinoma"	
+NCI-H1155	cell		fresh_sample	NCI-H1155		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"			NCI-H1155	cell	P-MEXP-9143	NCI-H1155	total_RNA	P-MEXP-9144	5	ug	"NCI-H1155 biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1807_NCIH1155	A-MEXP-86	P-MEXP-9146	R1807_NCIH1155	R1807_NCIH1155.txt	NCI-H1155			"Non-Small Cell Lung Carcinoma"	
+NCI-N417	cell		fresh_sample	NCI-N417		"Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"			NCI-N417	cell	P-MEXP-9143	NCI-N417	total_RNA	P-MEXP-9144	5	ug	"NCI-N417 biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1808_NCIN417	A-MEXP-86	P-MEXP-9146	R1808_NCIN417	R1808_NCIN417.txt	NCI-N417			"Small Cell Lung Carcinoma"	
+NCI-H596	cell		fresh_sample	NCI-H596		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"			NCI-H596	cell	P-MEXP-9143	NCI-H596	total_RNA	P-MEXP-9144	5	ug	"NCI-H596 biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1806_NCIH596	A-MEXP-86	P-MEXP-9146	R1806_NCIH596	R1806_NCIH596.txt	NCI-H596			"Non-Small Cell Lung Carcinoma"	
+A549_ex1_nontreat	cell		fresh_sample	A549		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	none	A549_ex1_nontreat	cell	P-MEXP-9143	A549_ex1_nontreat	total_RNA	P-MEXP-9144	5	ug	"A549_ex1_nontreat biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1811_A5491-1	A-MEXP-86	P-MEXP-9146	R1811_A5491-1	R1811_A5491-1.txt	A549	null		"Non-Small Cell Lung Carcinoma"	
+A549_ex1_aza	cell		fresh_sample	A549		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	Aza	A549_ex1_aza	cell	P-MEXP-9143	A549_ex1_aza	total_RNA	P-MEXP-9144	5	ug	"A549_ex1_aza biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1812_A5491-2	A-MEXP-86	P-MEXP-9146	R1812_A5491-2	R1812_A5491-2.txt	A549	Aza		"Non-Small Cell Lung Carcinoma"	
+A549_ex1_tsa	cell		fresh_sample	A549		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	TSA	A549_ex1_tsa	cell	P-MEXP-9143	A549_ex1_tsa	total_RNA	P-MEXP-9144	5	ug	"A549_ex1_tsa biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1813_A5491-3	A-MEXP-86	P-MEXP-9146	R1813_A5491-3	R1813_A5491-3.txt	A549	TSA		"Non-Small Cell Lung Carcinoma"	
+A549_ex1_both	cell		fresh_sample	A549		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	Aza+TSA	A549_ex1_both	cell	P-MEXP-9143	A549_ex1_both	total_RNA	P-MEXP-9144	5	ug	"A549_ex1_both biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1814_A5491-4	A-MEXP-86	P-MEXP-9146	R1814_A5491-4	R1814_A5491-4.txt	A549	Aza		"Non-Small Cell Lung Carcinoma"	
+A549_ex1_both	cell		fresh_sample	A549		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	Aza+TSA	A549_ex1_both	cell	P-MEXP-9143	A549_ex1_both	total_RNA	P-MEXP-9144	5	ug	"A549_ex1_both biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1814_A5491-4	A-MEXP-86	P-MEXP-9146	R1814_A5491-4	R1814_A5491-4.txt	A549	TSA		"Non-Small Cell Lung Carcinoma"	
+A549_ex2_nontreat	cell		fresh_sample	A549		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	none	A549_ex2_nontreat	cell	P-MEXP-9143	A549_ex2_nontreat	total_RNA	P-MEXP-9144	5	ug	"A549_ex2_nontreat biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1815_A5492-1	A-MEXP-86	P-MEXP-9146	R1815_A5492-1	R1815_A5492-1.txt	A549	null		"Non-Small Cell Lung Carcinoma"	
+A549_ex2_aza	cell		fresh_sample	A549		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	Aza	A549_ex2_aza	cell	P-MEXP-9143	A549_ex2_aza	total_RNA	P-MEXP-9144	5	ug	"A549_ex2_aza biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1816_A5492-2	A-MEXP-86	P-MEXP-9146	R1816_A5492-2	R1816_A5492-2.txt	A549	Aza		"Non-Small Cell Lung Carcinoma"	
+A549_ex2_tsa	cell		fresh_sample	A549		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	TSA	A549_ex2_tsa	cell	P-MEXP-9143	A549_ex2_tsa	total_RNA	P-MEXP-9144	5	ug	"A549_ex2_tsa biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1817_A5492-3	A-MEXP-86	P-MEXP-9146	R1817_A5492-3	R1817_A5492-3.txt	A549	TSA		"Non-Small Cell Lung Carcinoma"	
+A549_ex2_both	cell		fresh_sample	A549		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	Aza+TSA	A549_ex2_both	cell	P-MEXP-9143	A549_ex2_both	total_RNA	P-MEXP-9144	5	ug	"A549_ex2_both biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1818_A5492-4	A-MEXP-86	P-MEXP-9146	R1818_A5492-4	R1818_A5492-4.txt	A549	Aza		"Non-Small Cell Lung Carcinoma"	
+A549_ex2_both	cell		fresh_sample	A549		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	Aza+TSA	A549_ex2_both	cell	P-MEXP-9143	A549_ex2_both	total_RNA	P-MEXP-9144	5	ug	"A549_ex2_both biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1818_A5492-4	A-MEXP-86	P-MEXP-9146	R1818_A5492-4	R1818_A5492-4.txt	A549	TSA		"Non-Small Cell Lung Carcinoma"	
+A549_ex3_nontreat	cell		fresh_sample	A549		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	none	A549_ex3_nontreat	cell	P-MEXP-9143	A549_ex3_nontreat	total_RNA	P-MEXP-9144	5	ug	"A549_ex3_nontreat biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1819_A5493-1	A-MEXP-86	P-MEXP-9146	R1819_A5493-1	R1819_A5493-1.txt	A549	null		"Non-Small Cell Lung Carcinoma"	
+A549_ex3_aza	cell		fresh_sample	A549		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	Aza	A549_ex3_aza	cell	P-MEXP-9143	A549_ex3_aza	total_RNA	P-MEXP-9144	5	ug	"A549_ex3_aza biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1820_A5493-2	A-MEXP-86	P-MEXP-9146	R1820_A5493-2	R1820_A5493-2.txt	A549	Aza		"Non-Small Cell Lung Carcinoma"	
+A549_ex3_tsa	cell		fresh_sample	A549		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	TSA	A549_ex3_tsa	cell	P-MEXP-9143	A549_ex3_tsa	total_RNA	P-MEXP-9144	5	ug	"A549_ex3_tsa biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1821_A5493-3	A-MEXP-86	P-MEXP-9146	R1821_A5493-3	R1821_A5493-3.txt	A549	TSA		"Non-Small Cell Lung Carcinoma"	
+A549_ex3_both	cell		fresh_sample	A549		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	Aza+TSA	A549_ex3_both	cell	P-MEXP-9143	A549_ex3_both	total_RNA	P-MEXP-9144	5	ug	"A549_ex3_both biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1822_A5493-4	A-MEXP-86	P-MEXP-9146	R1822_A5493-4	R1822_A5493-4.txt	A549	Aza		"Non-Small Cell Lung Carcinoma"	
+A549_ex3_both	cell		fresh_sample	A549		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	Aza+TSA	A549_ex3_both	cell	P-MEXP-9143	A549_ex3_both	total_RNA	P-MEXP-9144	5	ug	"A549_ex3_both biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1822_A5493-4	A-MEXP-86	P-MEXP-9146	R1822_A5493-4	R1822_A5493-4.txt	A549	TSA		"Non-Small Cell Lung Carcinoma"	
+NCI-H157_ex1_nontreat	cell		fresh_sample	NCI-H157		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	none	NCI-H157_ex1_nontreat	cell	P-MEXP-9143	NCI-H157_ex1_nontreat	total_RNA	P-MEXP-9144	5	ug	"NCI-H157_ex1_nontreat biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1835_NCIH1571-1	A-MEXP-86	P-MEXP-9146	R1835_NCIH1571-1	R1835_NCIH1571-1.txt	NCI-H157	null		"Non-Small Cell Lung Carcinoma"	
+NCI-H157_ex1_aza	cell		fresh_sample	NCI-H157		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	Aza	NCI-H157_ex1_aza	cell	P-MEXP-9143	NCI-H157_ex1_aza	total_RNA	P-MEXP-9144	5	ug	"NCI-H157_ex1_aza biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1836_NCIH1571-2	A-MEXP-86	P-MEXP-9146	R1836_NCIH1571-2	R1836_NCIH1571-2.txt	NCI-H157	Aza		"Non-Small Cell Lung Carcinoma"	
+NCI-H157_ex1_tsa	cell		fresh_sample	NCI-H157		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	TSA	NCI-H157_ex1_tsa	cell	P-MEXP-9143	NCI-H157_ex1_tsa	total_RNA	P-MEXP-9144	5	ug	"NCI-H157_ex1_tsa biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1837_NCIH1571-3	A-MEXP-86	P-MEXP-9146	R1837_NCIH1571-3	R1837_NCIH1571-3.txt	NCI-H157	TSA		"Non-Small Cell Lung Carcinoma"	
+NCI-H157_ex1_both	cell		fresh_sample	NCI-H157		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	Aza+TSA	NCI-H157_ex1_both	cell	P-MEXP-9143	NCI-H157_ex1_both	total_RNA	P-MEXP-9144	5	ug	"NCI-H157_ex1_both biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1838_NCIH1571-4	A-MEXP-86	P-MEXP-9146	R1838_NCIH1571-4	R1838_NCIH1571-4.txt	NCI-H157	Aza		"Non-Small Cell Lung Carcinoma"	
+NCI-H157_ex1_both	cell		fresh_sample	NCI-H157		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	Aza+TSA	NCI-H157_ex1_both	cell	P-MEXP-9143	NCI-H157_ex1_both	total_RNA	P-MEXP-9144	5	ug	"NCI-H157_ex1_both biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1838_NCIH1571-4	A-MEXP-86	P-MEXP-9146	R1838_NCIH1571-4	R1838_NCIH1571-4.txt	NCI-H157	TSA		"Non-Small Cell Lung Carcinoma"	
+NCI-H157_ex2_nontreat	cell		fresh_sample	NCI-H157		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	none	NCI-H157_ex2_nontreat	cell	P-MEXP-9143	NCI-H157_ex2_nontreat	total_RNA	P-MEXP-9144	5	ug	"NCI-H157_ex2_nontreat biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1839_NCIH1572-1	A-MEXP-86	P-MEXP-9146	R1839_NCIH1572-1	R1839_NCIH1572-1.txt	NCI-H157	null		"Non-Small Cell Lung Carcinoma"	
+NCI-H157_ex2_aza	cell		fresh_sample	NCI-H157		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	Aza	NCI-H157_ex2_aza	cell	P-MEXP-9143	NCI-H157_ex2_aza	total_RNA	P-MEXP-9144	5	ug	"NCI-H157_ex2_aza biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1840_NCIH1572-2	A-MEXP-86	P-MEXP-9146	R1840_NCIH1572-2	R1840_NCIH1572-2.txt	NCI-H157	Aza		"Non-Small Cell Lung Carcinoma"	
+NCI-H157_ex2_tsa	cell		fresh_sample	NCI-H157		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	TSA	NCI-H157_ex2_tsa	cell	P-MEXP-9143	NCI-H157_ex2_tsa	total_RNA	P-MEXP-9144	5	ug	"NCI-H157_ex2_tsa biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1841_NCIH1572-3	A-MEXP-86	P-MEXP-9146	R1841_NCIH1572-3	R1841_NCIH1572-3.txt	NCI-H157	TSA		"Non-Small Cell Lung Carcinoma"	
+NCI-H157_ex2_both	cell		fresh_sample	NCI-H157		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	Aza+TSA	NCI-H157_ex2_both	cell	P-MEXP-9143	NCI-H157_ex2_both	total_RNA	P-MEXP-9144	5	ug	"NCI-H157_ex2_both biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1842_NCIH1572-4	A-MEXP-86	P-MEXP-9146	R1842_NCIH1572-4	R1842_NCIH1572-4.txt	NCI-H157	Aza		"Non-Small Cell Lung Carcinoma"	
+NCI-H157_ex2_both	cell		fresh_sample	NCI-H157		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	Aza+TSA	NCI-H157_ex2_both	cell	P-MEXP-9143	NCI-H157_ex2_both	total_RNA	P-MEXP-9144	5	ug	"NCI-H157_ex2_both biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1842_NCIH1572-4	A-MEXP-86	P-MEXP-9146	R1842_NCIH1572-4	R1842_NCIH1572-4.txt	NCI-H157	TSA		"Non-Small Cell Lung Carcinoma"	
+NCI-H157_ex3_nontreat	cell		fresh_sample	NCI-H157		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	none	NCI-H157_ex3_nontreat	cell	P-MEXP-9143	NCI-H157_ex3_nontreat	total_RNA	P-MEXP-9144	5	ug	"NCI-H157_ex3_nontreat biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1843_NCIH1573-1	A-MEXP-86	P-MEXP-9146	R1843_NCIH1573-1	R1843_NCIH1573-1.txt	NCI-H157	null		"Non-Small Cell Lung Carcinoma"	
+NCI-H157_ex3_aza	cell		fresh_sample	NCI-H157		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	Aza	NCI-H157_ex3_aza	cell	P-MEXP-9143	NCI-H157_ex3_aza	total_RNA	P-MEXP-9144	5	ug	"NCI-H157_ex3_aza biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1844_NCIH1573-2	A-MEXP-86	P-MEXP-9146	R1844_NCIH1573-2	R1844_NCIH1573-2.txt	NCI-H157	Aza		"Non-Small Cell Lung Carcinoma"	
+NCI-H157_ex3_tsa	cell		fresh_sample	NCI-H157		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	TSA	NCI-H157_ex3_tsa	cell	P-MEXP-9143	NCI-H157_ex3_tsa	total_RNA	P-MEXP-9144	5	ug	"NCI-H157_ex3_tsa biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1845_NCIH1573-3	A-MEXP-86	P-MEXP-9146	R1845_NCIH1573-3	R1845_NCIH1573-3.txt	NCI-H157	TSA		"Non-Small Cell Lung Carcinoma"	
+NCI-H157_ex3_both	cell		fresh_sample	NCI-H157		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	Aza+TSA	NCI-H157_ex3_both	cell	P-MEXP-9143	NCI-H157_ex3_both	total_RNA	P-MEXP-9144	5	ug	"NCI-H157_ex3_both biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1846_NCIH1573-4	A-MEXP-86	P-MEXP-9146	R1846_NCIH1573-4	R1846_NCIH1573-4.txt	NCI-H157	Aza		"Non-Small Cell Lung Carcinoma"	
+NCI-H157_ex3_both	cell		fresh_sample	NCI-H157		"Non-Small Cell Lung Carcinoma"		"Homo sapiens"			P-MEXP-9141	"RPMI 1640"	P-MEXP-9142	Aza+TSA	NCI-H157_ex3_both	cell	P-MEXP-9143	NCI-H157_ex3_both	total_RNA	P-MEXP-9144	5	ug	"NCI-H157_ex3_both biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1846_NCIH1573-4	A-MEXP-86	P-MEXP-9146	R1846_NCIH1573-4	R1846_NCIH1573-4.txt	NCI-H157	TSA		"Non-Small Cell Lung Carcinoma"	
+12902T	organism_part	74	fresh_sample		"Stage II"	Adenocarcinoma	12902N	"Homo sapiens"	male	years					12902T	organism_part	P-MEXP-9143	12902T	total_RNA	P-MEXP-9144	5	ug	"12902T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1595_12902T	A-MEXP-86	P-MEXP-9146	R1595_12902T	R1595_12902T.txt			"Stage II"	Adenocarcinoma	12902N
+12902N	organism_part	74	fresh_sample			normal	12902N	"Homo sapiens"	male	years					12902N	organism_part	P-MEXP-9143	12902N	total_RNA	P-MEXP-9144	5	ug	"12902N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1594_12902N	A-MEXP-86	P-MEXP-9146	R1594_12902N	R1594_12902N.txt				normal	12902N
+12822T	organism_part	66	fresh_sample		"Stage I"	Adenocarcinoma	12822N	"Homo sapiens"	male	years					12822T	organism_part	P-MEXP-9143	12822T	total_RNA	P-MEXP-9144	5	ug	"12822T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1591_12822T	A-MEXP-86	P-MEXP-9146	R1591_12822T	R1591_12822T.txt			"Stage I"	Adenocarcinoma	12822N
+12822N	organism_part	66	fresh_sample			normal	12822N	"Homo sapiens"	male	years					12822N	organism_part	P-MEXP-9143	12822N	total_RNA	P-MEXP-9144	5	ug	"12822N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1590_12822N	A-MEXP-86	P-MEXP-9146	R1590_12822N	R1590_12822N.txt				normal	12822N
+12821T	organism_part	68	fresh_sample		"Stage II"	Adenocarcinoma	12821N	"Homo sapiens"	male	years					12821T	organism_part	P-MEXP-9143	12821T	total_RNA	P-MEXP-9144	5	ug	"12821T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1583_12821T	A-MEXP-86	P-MEXP-9146	R1583_12821T	R1583_12821T.txt			"Stage II"	Adenocarcinoma	12821N
+12821N	organism_part	68	fresh_sample			normal	12821N	"Homo sapiens"	male	years					12821N	organism_part	P-MEXP-9143	12821N	total_RNA	P-MEXP-9144	5	ug	"12821N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1582_12821N	A-MEXP-86	P-MEXP-9146	R1582_12821N	R1582_12821N.txt				normal	12821N
+12745T	organism_part	71	fresh_sample		"Stage I"	Adenocarcinoma	12745N	"Homo sapiens"	male	years					12745T	organism_part	P-MEXP-9143	12745T	total_RNA	P-MEXP-9144	5	ug	"12745T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1581_12745T	A-MEXP-86	P-MEXP-9146	R1581_12745T	R1581_12745T.txt			"Stage I"	Adenocarcinoma	12745N
+12745N	organism_part	71	fresh_sample			normal	12745N	"Homo sapiens"	male	years					12745N	organism_part	P-MEXP-9143	12745N	total_RNA	P-MEXP-9144	5	ug	"12745N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1580_12745N	A-MEXP-86	P-MEXP-9146	R1580_12745N	R1580_12745N.txt				normal	12745N
+12681T	organism_part	75	fresh_sample		"Stage I"	Adenocarcinoma	12681N	"Homo sapiens"	male	years					12681T	organism_part	P-MEXP-9143	12681T	total_RNA	P-MEXP-9144	5	ug	"12681T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1579_12681T	A-MEXP-86	P-MEXP-9146	R1579_12681T	R1579_12681T.txt			"Stage I"	Adenocarcinoma	12681N
+12681N	organism_part	75	fresh_sample			normal	12681N	"Homo sapiens"	male	years					12681N	organism_part	P-MEXP-9143	12681N	total_RNA	P-MEXP-9144	5	ug	"12681N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1578_12681N	A-MEXP-86	P-MEXP-9146	R1578_12681N	R1578_12681N.txt				normal	12681N
+12606T	organism_part	66	fresh_sample		"Stage I"	Adenocarcinoma	12606N	"Homo sapiens"	male	years					12606T	organism_part	P-MEXP-9143	12606T	total_RNA	P-MEXP-9144	5	ug	"12606T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1569_12606T	A-MEXP-86	P-MEXP-9146	R1569_12606T	R1569_12606T.txt			"Stage I"	Adenocarcinoma	12606N
+12606N	organism_part	66	fresh_sample			normal	12606N	"Homo sapiens"	male	years					12606N	organism_part	P-MEXP-9143	12606N	total_RNA	P-MEXP-9144	5	ug	"12606N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1568_12606N	A-MEXP-86	P-MEXP-9146	R1568_12606N	R1568_12606N.txt				normal	12606N
+12577T	organism_part	71	fresh_sample		"Stage I"	Adenocarcinoma	12577N	"Homo sapiens"	male	years					12577T	organism_part	P-MEXP-9143	12577T	total_RNA	P-MEXP-9144	5	ug	"12577T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1567_12577T	A-MEXP-86	P-MEXP-9146	R1567_12577T	R1567_12577T.txt			"Stage I"	Adenocarcinoma	12577N
+12577N	organism_part	71	fresh_sample			normal	12577N	"Homo sapiens"	male	years					12577N	organism_part	P-MEXP-9143	12577N	total_RNA	P-MEXP-9144	5	ug	"12577N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1566_12577N	A-MEXP-86	P-MEXP-9146	R1566_12577N	R1566_12577N.txt				normal	12577N
+12520T	organism_part	49	fresh_sample		"Stage I"	Adenocarcinoma	12520N	"Homo sapiens"	male	years					12520T	organism_part	P-MEXP-9143	12520T	total_RNA	P-MEXP-9144	5	ug	"12520T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1563_12520T	A-MEXP-86	P-MEXP-9146	R1563_12520T	R1563_12520T.txt			"Stage I"	Adenocarcinoma	12520N
+12520N	organism_part	49	fresh_sample			normal	12520N	"Homo sapiens"	male	years					12520N	organism_part	P-MEXP-9143	12520N	total_RNA	P-MEXP-9144	5	ug	"12520N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1562_12520N	A-MEXP-86	P-MEXP-9146	R1562_12520N	R1562_12520N.txt				normal	12520N
+12503T	organism_part	75	fresh_sample		"Stage I"	Adenocarcinoma	12503N	"Homo sapiens"	male	years					12503T	organism_part	P-MEXP-9143	12503T	total_RNA	P-MEXP-9144	5	ug	"12503T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1559_12503T	A-MEXP-86	P-MEXP-9146	R1559_12503T	R1559_12503T.txt			"Stage I"	Adenocarcinoma	12503N
+12503N	organism_part	75	fresh_sample			normal	12503N	"Homo sapiens"	male	years					12503N	organism_part	P-MEXP-9143	12503N	total_RNA	P-MEXP-9144	5	ug	"12503N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1558_12503N	A-MEXP-86	P-MEXP-9146	R1558_12503N	R1558_12503N.txt				normal	12503N
+12490T	organism_part	57	fresh_sample		"Stage III or IV"	Adenocarcinoma	12490N	"Homo sapiens"	male	years					12490T	organism_part	P-MEXP-9143	12490T	total_RNA	P-MEXP-9144	5	ug	"12490T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1547_12490T	A-MEXP-86	P-MEXP-9146	R1547_12490T	R1547_12490T.txt			"Stage III or IV"	Adenocarcinoma	12490N
+12490N	organism_part	57	fresh_sample			normal	12490N	"Homo sapiens"	male	years					12490N	organism_part	P-MEXP-9143	12490N	total_RNA	P-MEXP-9144	5	ug	"12490N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1546_12490N	A-MEXP-86	P-MEXP-9146	R1546_12490N	R1546_12490N.txt				normal	12490N
+12394T	organism_part	51	fresh_sample		"Stage I"	Adenocarcinoma	12394N	"Homo sapiens"	female	years					12394T	organism_part	P-MEXP-9143	12394T	total_RNA	P-MEXP-9144	5	ug	"12394T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1539_12394T	A-MEXP-86	P-MEXP-9146	R1539_12394T	R1539_12394T.txt			"Stage I"	Adenocarcinoma	12394N
+12394N	organism_part	51	fresh_sample			normal	12394N	"Homo sapiens"	female	years					12394N	organism_part	P-MEXP-9143	12394N	total_RNA	P-MEXP-9144	5	ug	"12394N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1538_12394N	A-MEXP-86	P-MEXP-9146	R1538_12394N	R1538_12394N.txt				normal	12394N
+12374T	organism_part	68	fresh_sample		"Stage I"	Adenocarcinoma	12374N	"Homo sapiens"	male	years					12374T	organism_part	P-MEXP-9143	12374T	total_RNA	P-MEXP-9144	5	ug	"12374T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1537_12374T	A-MEXP-86	P-MEXP-9146	R1537_12374T	R1537_12374T.txt			"Stage I"	Adenocarcinoma	12374N
+12374N	organism_part	68	fresh_sample			normal	12374N	"Homo sapiens"	male	years					12374N	organism_part	P-MEXP-9143	12374N	total_RNA	P-MEXP-9144	5	ug	"12374N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1536_12374N	A-MEXP-86	P-MEXP-9146	R1536_12374N	R1536_12374N.txt				normal	12374N
+12251T	organism_part	63	fresh_sample		"Stage I"	Adenocarcinoma	12251N	"Homo sapiens"	female	years					12251T	organism_part	P-MEXP-9143	12251T	total_RNA	P-MEXP-9144	5	ug	"12251T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1527_12251T	A-MEXP-86	P-MEXP-9146	R1527_12251T	R1527_12251T.txt			"Stage I"	Adenocarcinoma	12251N
+12251N	organism_part	63	fresh_sample			normal	12251N	"Homo sapiens"	female	years					12251N	organism_part	P-MEXP-9143	12251N	total_RNA	P-MEXP-9144	5	ug	"12251N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1526_12251N	A-MEXP-86	P-MEXP-9146	R1526_12251N	R1526_12251N.txt				normal	12251N
+12189T	organism_part	61	fresh_sample		"Stage I"	Adenocarcinoma	12189N	"Homo sapiens"	male	years					12189T	organism_part	P-MEXP-9143	12189T	total_RNA	P-MEXP-9144	5	ug	"12189T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1525_12189T	A-MEXP-86	P-MEXP-9146	R1525_12189T	R1525_12189T.txt			"Stage I"	Adenocarcinoma	12189N
+12189N	organism_part	61	fresh_sample			normal	12189N	"Homo sapiens"	male	years					12189N	organism_part	P-MEXP-9143	12189N	total_RNA	P-MEXP-9144	5	ug	"12189N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1524_12189N	A-MEXP-86	P-MEXP-9146	R1524_12189N	R1524_12189N.txt				normal	12189N
+12178T	organism_part	63	fresh_sample		"Stage II"	Adenocarcinoma	12178N	"Homo sapiens"	male	years					12178T	organism_part	P-MEXP-9143	12178T	total_RNA	P-MEXP-9144	5	ug	"12178T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1521_12178T	A-MEXP-86	P-MEXP-9146	R1521_12178T	R1521_12178T.txt			"Stage II"	Adenocarcinoma	12178N
+12178N	organism_part	63	fresh_sample			normal	12178N	"Homo sapiens"	male	years					12178N	organism_part	P-MEXP-9143	12178N	total_RNA	P-MEXP-9144	5	ug	"12178N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1520_12178N	A-MEXP-86	P-MEXP-9146	R1520_12178N	R1520_12178N.txt				normal	12178N
+12170T	organism_part	50	fresh_sample		"Stage I"	Adenocarcinoma	12170N	"Homo sapiens"	female	years					12170T	organism_part	P-MEXP-9143	12170T	total_RNA	P-MEXP-9144	5	ug	"12170T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1519_12170T	A-MEXP-86	P-MEXP-9146	R1519_12170T	R1519_12170T.txt			"Stage I"	Adenocarcinoma	12170N
+12170N	organism_part	50	fresh_sample			normal	12170N	"Homo sapiens"	female	years					12170N	organism_part	P-MEXP-9143	12170N	total_RNA	P-MEXP-9144	5	ug	"12170N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1518_12170N	A-MEXP-86	P-MEXP-9146	R1518_12170N	R1518_12170N.txt				normal	12170N
+12167T	organism_part	71	fresh_sample		"Stage I"	Adenocarcinoma	12167N	"Homo sapiens"	male	years					12167T	organism_part	P-MEXP-9143	12167T	total_RNA	P-MEXP-9144	5	ug	"12167T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1517_12167T	A-MEXP-86	P-MEXP-9146	R1517_12167T	R1517_12167T.txt			"Stage I"	Adenocarcinoma	12167N
+12167N	organism_part	71	fresh_sample			normal	12167N	"Homo sapiens"	male	years					12167N	organism_part	P-MEXP-9143	12167N	total_RNA	P-MEXP-9144	5	ug	"12167N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1516_12167N	A-MEXP-86	P-MEXP-9146	R1516_12167N	R1516_12167N.txt				normal	12167N
+12165T	organism_part	38	fresh_sample		"Stage III or IV"	Adenocarcinoma	12165N	"Homo sapiens"	female	years					12165T	organism_part	P-MEXP-9143	12165T	total_RNA	P-MEXP-9144	5	ug	"12165T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1778_12165T	A-MEXP-86	P-MEXP-9146	R1778_12165T	R1778_12165T.txt			"Stage III or IV"	Adenocarcinoma	12165N
+12165N	organism_part	38	fresh_sample			normal	12165N	"Homo sapiens"	female	years					12165N	organism_part	P-MEXP-9143	12165N	total_RNA	P-MEXP-9144	5	ug	"12165N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1777_12165N	A-MEXP-86	P-MEXP-9146	R1777_12165N	R1777_12165N.txt				normal	12165N
+12118T	organism_part	57	fresh_sample		"Stage I"	Adenocarcinoma	12118N	"Homo sapiens"	male	years					12118T	organism_part	P-MEXP-9143	12118T	total_RNA	P-MEXP-9144	5	ug	"12118T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1513_12118T	A-MEXP-86	P-MEXP-9146	R1513_12118T	R1513_12118T.txt			"Stage I"	Adenocarcinoma	12118N
+12118N	organism_part	57	fresh_sample			normal	12118N	"Homo sapiens"	male	years					12118N	organism_part	P-MEXP-9143	12118N	total_RNA	P-MEXP-9144	5	ug	"12118N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1512_12118N	A-MEXP-86	P-MEXP-9146	R1512_12118N	R1512_12118N.txt				normal	12118N
+12084T	organism_part	75	fresh_sample		"Stage I"	Adenocarcinoma	12084N	"Homo sapiens"	male	years					12084T	organism_part	P-MEXP-9143	12084T	total_RNA	P-MEXP-9144	5	ug	"12084T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1507_12084T	A-MEXP-86	P-MEXP-9146	R1507_12084T	R1507_12084T.txt			"Stage I"	Adenocarcinoma	12084N
+12084N	organism_part	75	fresh_sample			normal	12084N	"Homo sapiens"	male	years					12084N	organism_part	P-MEXP-9143	12084N	total_RNA	P-MEXP-9144	5	ug	"12084N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1500_12084N	A-MEXP-86	P-MEXP-9146	R1500_12084N	R1500_12084N.txt				normal	12084N
+12012T	organism_part	61	fresh_sample		"Stage I"	Adenocarcinoma	12012N	"Homo sapiens"	male	years					12012T	organism_part	P-MEXP-9143	12012T	total_RNA	P-MEXP-9144	5	ug	"12012T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1099_12012T	A-MEXP-86	P-MEXP-9146	R1099_12012T	R1099_12012T.txt			"Stage I"	Adenocarcinoma	12012N
+12012N	organism_part	61	fresh_sample			normal	12012N	"Homo sapiens"	male	years					12012N	organism_part	P-MEXP-9143	12012N	total_RNA	P-MEXP-9144	5	ug	"12012N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1098_12012N	A-MEXP-86	P-MEXP-9146	R1098_12012N	R1098_12012N.txt				normal	12012N
+11922T	organism_part	80	fresh_sample		"Stage III or IV"	Adenocarcinoma	11922N	"Homo sapiens"	male	years					11922T	organism_part	P-MEXP-9143	11922T	total_RNA	P-MEXP-9144	5	ug	"11922T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1091_11922T	A-MEXP-86	P-MEXP-9146	R1091_11922T	R1091_11922T.txt			"Stage III or IV"	Adenocarcinoma	11922N
+11922N	organism_part	80	fresh_sample			normal	11922N	"Homo sapiens"	male	years					11922N	organism_part	P-MEXP-9143	11922N	total_RNA	P-MEXP-9144	5	ug	"11922N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1090_11922N	A-MEXP-86	P-MEXP-9146	R1090_11922N	R1090_11922N.txt				normal	11922N
+11908T	organism_part	57	fresh_sample		"Stage I"	Adenocarcinoma	11908N	"Homo sapiens"	female	years					11908T	organism_part	P-MEXP-9143	11908T	total_RNA	P-MEXP-9144	5	ug	"11908T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1776_11908T	A-MEXP-86	P-MEXP-9146	R1776_11908T	R1776_11908T.txt			"Stage I"	Adenocarcinoma	11908N
+11908N	organism_part	57	fresh_sample			normal	11908N	"Homo sapiens"	female	years					11908N	organism_part	P-MEXP-9143	11908N	total_RNA	P-MEXP-9144	5	ug	"11908N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1775_11908N	A-MEXP-86	P-MEXP-9146	R1775_11908N	R1775_11908N.txt				normal	11908N
+11898T	organism_part	84	fresh_sample		"Stage III or IV"	Adenocarcinoma	11898N	"Homo sapiens"	female	years					11898T	organism_part	P-MEXP-9143	11898T	total_RNA	P-MEXP-9144	5	ug	"11898T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1089_11898T	A-MEXP-86	P-MEXP-9146	R1089_11898T	R1089_11898T.txt			"Stage III or IV"	Adenocarcinoma	11898N
+11898N	organism_part	84	fresh_sample			normal	11898N	"Homo sapiens"	female	years					11898N	organism_part	P-MEXP-9143	11898N	total_RNA	P-MEXP-9144	5	ug	"11898N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1088_11898N	A-MEXP-86	P-MEXP-9146	R1088_11898N	R1088_11898N.txt				normal	11898N
+11891T	organism_part	69	fresh_sample		"Stage III or IV"	Adenocarcinoma	11891N	"Homo sapiens"	female	years					11891T	organism_part	P-MEXP-9143	11891T	total_RNA	P-MEXP-9144	5	ug	"11891T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1087_11891T	A-MEXP-86	P-MEXP-9146	R1087_11891T	R1087_11891T.txt			"Stage III or IV"	Adenocarcinoma	11891N
+11891N	organism_part	69	fresh_sample			normal	11891N	"Homo sapiens"	female	years					11891N	organism_part	P-MEXP-9143	11891N	total_RNA	P-MEXP-9144	5	ug	"11891N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1085_11891N	A-MEXP-86	P-MEXP-9146	R1085_11891N	R1085_11891N.txt				normal	11891N
+11885T	organism_part	65	fresh_sample		"Stage I"	Adenocarcinoma	11885N	"Homo sapiens"	male	years					11885T	organism_part	P-MEXP-9143	11885T	total_RNA	P-MEXP-9144	5	ug	"11885T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1084_11885T	A-MEXP-86	P-MEXP-9146	R1084_11885T	R1084_11885T.txt			"Stage I"	Adenocarcinoma	11885N
+11885N	organism_part	65	fresh_sample			normal	11885N	"Homo sapiens"	male	years					11885N	organism_part	P-MEXP-9143	11885N	total_RNA	P-MEXP-9144	5	ug	"11885N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1083_11885N	A-MEXP-86	P-MEXP-9146	R1083_11885N	R1083_11885N.txt				normal	11885N
+11823T	organism_part	67	fresh_sample		"Stage I"	Adenocarcinoma	11823N	"Homo sapiens"	male	years					11823T	organism_part	P-MEXP-9143	11823T	total_RNA	P-MEXP-9144	5	ug	"11823T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R896_11823T	A-MEXP-86	P-MEXP-9146	R896_11823T	R896_11823T.txt			"Stage I"	Adenocarcinoma	11823N
+11823N	organism_part	67	fresh_sample			normal	11823N	"Homo sapiens"	male	years					11823N	organism_part	P-MEXP-9143	11823N	total_RNA	P-MEXP-9144	5	ug	"11823N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R895_11823N	A-MEXP-86	P-MEXP-9146	R895_11823N	R895_11823N.txt				normal	11823N
+11812T	organism_part	67	fresh_sample		"Stage III or IV"	Adenocarcinoma	11812N	"Homo sapiens"	male	years					11812T	organism_part	P-MEXP-9143	11812T	total_RNA	P-MEXP-9144	5	ug	"11812T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R894_11812T	A-MEXP-86	P-MEXP-9146	R894_11812T	R894_11812T.txt			"Stage III or IV"	Adenocarcinoma	11812N
+11812N	organism_part	67	fresh_sample			normal	11812N	"Homo sapiens"	male	years					11812N	organism_part	P-MEXP-9143	11812N	total_RNA	P-MEXP-9144	5	ug	"11812N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R893_11812N	A-MEXP-86	P-MEXP-9146	R893_11812N	R893_11812N.txt				normal	11812N
+11807T	organism_part	73	fresh_sample		"Stage I"	Adenocarcinoma	11807N	"Homo sapiens"	female	years					11807T	organism_part	P-MEXP-9143	11807T	total_RNA	P-MEXP-9144	5	ug	"11807T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R890_11807T	A-MEXP-86	P-MEXP-9146	R890_11807T	R890_11807T.txt			"Stage I"	Adenocarcinoma	11807N
+11807N	organism_part	73	fresh_sample			normal	11807N	"Homo sapiens"	female	years					11807N	organism_part	P-MEXP-9143	11807N	total_RNA	P-MEXP-9144	5	ug	"11807N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R889_11807N	A-MEXP-86	P-MEXP-9146	R889_11807N	R889_11807N.txt				normal	11807N
+11777T	organism_part	72	fresh_sample		"Stage I"	Adenocarcinoma	11777N	"Homo sapiens"	female	years					11777T	organism_part	P-MEXP-9143	11777T	total_RNA	P-MEXP-9144	5	ug	"11777T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R888_11777T	A-MEXP-86	P-MEXP-9146	R888_11777T	R888_11777T.txt			"Stage I"	Adenocarcinoma	11777N
+11777N	organism_part	72	fresh_sample			normal	11777N	"Homo sapiens"	female	years					11777N	organism_part	P-MEXP-9143	11777N	total_RNA	P-MEXP-9144	5	ug	"11777N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R887_11777N	A-MEXP-86	P-MEXP-9146	R887_11777N	R887_11777N.txt				normal	11777N
+11671T	organism_part	53	fresh_sample		"Stage I"	Adenocarcinoma	11671N	"Homo sapiens"	male	years					11671T	organism_part	P-MEXP-9143	11671T	total_RNA	P-MEXP-9144	5	ug	"11671T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R882_11671T	A-MEXP-86	P-MEXP-9146	R882_11671T	R882_11671T.txt			"Stage I"	Adenocarcinoma	11671N
+11671N	organism_part	53	fresh_sample			normal	11671N	"Homo sapiens"	male	years					11671N	organism_part	P-MEXP-9143	11671N	total_RNA	P-MEXP-9144	5	ug	"11671N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R881_11671N	A-MEXP-86	P-MEXP-9146	R881_11671N	R881_11671N.txt				normal	11671N
+11669T	organism_part	67	fresh_sample		"Stage I"	Adenocarcinoma	11669N	"Homo sapiens"	female	years					11669T	organism_part	P-MEXP-9143	11669T	total_RNA	P-MEXP-9144	5	ug	"11669T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R880_11669T	A-MEXP-86	P-MEXP-9146	R880_11669T	R880_11669T.txt			"Stage I"	Adenocarcinoma	11669N
+11669N	organism_part	67	fresh_sample			normal	11669N	"Homo sapiens"	female	years					11669N	organism_part	P-MEXP-9143	11669N	total_RNA	P-MEXP-9144	5	ug	"11669N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R879_11669N	A-MEXP-86	P-MEXP-9146	R879_11669N	R879_11669N.txt				normal	11669N
+11616T	organism_part	65	fresh_sample		"Stage III or IV"	Adenocarcinoma	11616N	"Homo sapiens"	male	years					11616T	organism_part	P-MEXP-9143	11616T	total_RNA	P-MEXP-9144	5	ug	"11616T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R876_11616T	A-MEXP-86	P-MEXP-9146	R876_11616T	R876_11616T.txt			"Stage III or IV"	Adenocarcinoma	11616N
+11616N	organism_part	65	fresh_sample			normal	11616N	"Homo sapiens"	male	years					11616N	organism_part	P-MEXP-9143	11616N	total_RNA	P-MEXP-9144	5	ug	"11616N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R875_11616N	A-MEXP-86	P-MEXP-9146	R875_11616N	R875_11616N.txt				normal	11616N
+11547T	organism_part	74	fresh_sample		"Stage III or IV"	Adenocarcinoma	11547N	"Homo sapiens"	female	years					11547T	organism_part	P-MEXP-9143	11547T	total_RNA	P-MEXP-9144	5	ug	"11547T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1074_11547T	A-MEXP-86	P-MEXP-9146	R1074_11547T	R1074_11547T.txt			"Stage III or IV"	Adenocarcinoma	11547N
+11547N	organism_part	74	fresh_sample			normal	11547N	"Homo sapiens"	female	years					11547N	organism_part	P-MEXP-9143	11547N	total_RNA	P-MEXP-9144	5	ug	"11547N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1073_11547N	A-MEXP-86	P-MEXP-9146	R1073_11547N	R1073_11547N.txt				normal	11547N
+11541T	organism_part	70	fresh_sample		"Stage I"	Adenocarcinoma	11541N	"Homo sapiens"	male	years					11541T	organism_part	P-MEXP-9143	11541T	total_RNA	P-MEXP-9144	5	ug	"11541T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1072_11541T	A-MEXP-86	P-MEXP-9146	R1072_11541T	R1072_11541T.txt			"Stage I"	Adenocarcinoma	11541N
+11541N	organism_part	70	fresh_sample			normal	11541N	"Homo sapiens"	male	years					11541N	organism_part	P-MEXP-9143	11541N	total_RNA	P-MEXP-9144	5	ug	"11541N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1071_11541N	A-MEXP-86	P-MEXP-9146	R1071_11541N	R1071_11541N.txt				normal	11541N
+11502T	organism_part	61	fresh_sample		"Stage III or IV"	Adenocarcinoma	11502N	"Homo sapiens"	female	years					11502T	organism_part	P-MEXP-9143	11502T	total_RNA	P-MEXP-9144	5	ug	"11502T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1070_11502T	A-MEXP-86	P-MEXP-9146	R1070_11502T	R1070_11502T.txt			"Stage III or IV"	Adenocarcinoma	11502N
+11502N	organism_part	61	fresh_sample			normal	11502N	"Homo sapiens"	female	years					11502N	organism_part	P-MEXP-9143	11502N	total_RNA	P-MEXP-9144	5	ug	"11502N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1069_11502N	A-MEXP-86	P-MEXP-9146	R1069_11502N	R1069_11502N.txt				normal	11502N
+11447T	organism_part	52	fresh_sample		"Stage I"	Adenocarcinoma	11447N	"Homo sapiens"	female	years					11447T	organism_part	P-MEXP-9143	11447T	total_RNA	P-MEXP-9144	5	ug	"11447T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1068_11447T	A-MEXP-86	P-MEXP-9146	R1068_11447T	R1068_11447T.txt			"Stage I"	Adenocarcinoma	11447N
+11447N	organism_part	52	fresh_sample			normal	11447N	"Homo sapiens"	female	years					11447N	organism_part	P-MEXP-9143	11447N	total_RNA	P-MEXP-9144	5	ug	"11447N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1067_11447N	A-MEXP-86	P-MEXP-9146	R1067_11447N	R1067_11447N.txt				normal	11447N
+11404T	organism_part	38	fresh_sample		"Stage I"	Adenocarcinoma	11404N	"Homo sapiens"	female	years					11404T	organism_part	P-MEXP-9143	11404T	total_RNA	P-MEXP-9144	5	ug	"11404T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R900_11404T	A-MEXP-86	P-MEXP-9146	R900_11404T	R900_11404T.txt			"Stage I"	Adenocarcinoma	11404N
+11404N	organism_part	38	fresh_sample			normal	11404N	"Homo sapiens"	female	years					11404N	organism_part	P-MEXP-9143	11404N	total_RNA	P-MEXP-9144	5	ug	"11404N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R899_11404N	A-MEXP-86	P-MEXP-9146	R899_11404N	R899_11404N.txt				normal	11404N
+11343T	organism_part	68	fresh_sample		"Stage II"	Adenocarcinoma	11343N	"Homo sapiens"	male	years					11343T	organism_part	P-MEXP-9143	11343T	total_RNA	P-MEXP-9144	5	ug	"11343T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1050_11343T	A-MEXP-86	P-MEXP-9146	R1050_11343T	R1050_11343T.txt			"Stage II"	Adenocarcinoma	11343N
+11343N	organism_part	68	fresh_sample			normal	11343N	"Homo sapiens"	male	years					11343N	organism_part	P-MEXP-9143	11343N	total_RNA	P-MEXP-9144	5	ug	"11343N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1049_11343N	A-MEXP-86	P-MEXP-9146	R1049_11343N	R1049_11343N.txt				normal	11343N
+11231T	organism_part	56	fresh_sample		"Stage III or IV"	Adenocarcinoma	11231N	"Homo sapiens"	male	years					11231T	organism_part	P-MEXP-9143	11231T	total_RNA	P-MEXP-9144	5	ug	"11231T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1048_11231T	A-MEXP-86	P-MEXP-9146	R1048_11231T	R1048_11231T.txt			"Stage III or IV"	Adenocarcinoma	11231N
+11231N	organism_part	56	fresh_sample			normal	11231N	"Homo sapiens"	male	years					11231N	organism_part	P-MEXP-9143	11231N	total_RNA	P-MEXP-9144	5	ug	"11231N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1047_11231N	A-MEXP-86	P-MEXP-9146	R1047_11231N	R1047_11231N.txt				normal	11231N
+11191T	organism_part	47	fresh_sample		"Stage I"	Adenocarcinoma	11191N	"Homo sapiens"	male	years					11191T	organism_part	P-MEXP-9143	11191T	total_RNA	P-MEXP-9144	5	ug	"11191T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1042_11191T	A-MEXP-86	P-MEXP-9146	R1042_11191T	R1042_11191T.txt			"Stage I"	Adenocarcinoma	11191N
+11191N	organism_part	47	fresh_sample			normal	11191N	"Homo sapiens"	male	years					11191N	organism_part	P-MEXP-9143	11191N	total_RNA	P-MEXP-9144	5	ug	"11191N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1041_11191N	A-MEXP-86	P-MEXP-9146	R1041_11191N	R1041_11191N.txt				normal	11191N
+11184T	organism_part	53	fresh_sample		"Stage I"	Adenocarcinoma	11184N	"Homo sapiens"	female	years					11184T	organism_part	P-MEXP-9143	11184T	total_RNA	P-MEXP-9144	5	ug	"11184T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1040_11184T	A-MEXP-86	P-MEXP-9146	R1040_11184T	R1040_11184T.txt			"Stage I"	Adenocarcinoma	11184N
+11184N	organism_part	53	fresh_sample			normal	11184N	"Homo sapiens"	female	years					11184N	organism_part	P-MEXP-9143	11184N	total_RNA	P-MEXP-9144	5	ug	"11184N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1039_11184N	A-MEXP-86	P-MEXP-9146	R1039_11184N	R1039_11184N.txt				normal	11184N
+11139T	organism_part	49	fresh_sample		"Stage I"	Adenocarcinoma	11139N	"Homo sapiens"	male	years					11139T	organism_part	P-MEXP-9143	11139T	total_RNA	P-MEXP-9144	5	ug	"11139T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1036_11139T	A-MEXP-86	P-MEXP-9146	R1036_11139T	R1036_11139T.txt			"Stage I"	Adenocarcinoma	11139N
+11139N	organism_part	49	fresh_sample			normal	11139N	"Homo sapiens"	male	years					11139N	organism_part	P-MEXP-9143	11139N	total_RNA	P-MEXP-9144	5	ug	"11139N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1035_11139N	A-MEXP-86	P-MEXP-9146	R1035_11139N	R1035_11139N.txt				normal	11139N
+11019T	organism_part	59	fresh_sample		"Stage III or IV"	Adenocarcinoma	11019N	"Homo sapiens"	male	years					11019T	organism_part	P-MEXP-9143	11019T	total_RNA	P-MEXP-9144	5	ug	"11019T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1026_11019T	A-MEXP-86	P-MEXP-9146	R1026_11019T	R1026_11019T.txt			"Stage III or IV"	Adenocarcinoma	11019N
+11019N	organism_part	59	fresh_sample			normal	11019N	"Homo sapiens"	male	years					11019N	organism_part	P-MEXP-9143	11019N	total_RNA	P-MEXP-9144	5	ug	"11019N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1025_11019N	A-MEXP-86	P-MEXP-9146	R1025_11019N	R1025_11019N.txt				normal	11019N
+11005T	organism_part	76	fresh_sample		"Stage I"	Adenocarcinoma	11005N	"Homo sapiens"	female	years					11005T	organism_part	P-MEXP-9143	11005T	total_RNA	P-MEXP-9144	5	ug	"11005T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1020_11005T	A-MEXP-86	P-MEXP-9146	R1020_11005T	R1020_11005T.txt			"Stage I"	Adenocarcinoma	11005N
+11005N	organism_part	76	fresh_sample			normal	11005N	"Homo sapiens"	female	years					11005N	organism_part	P-MEXP-9143	11005N	total_RNA	P-MEXP-9144	5	ug	"11005N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1019_11005N	A-MEXP-86	P-MEXP-9146	R1019_11005N	R1019_11005N.txt				normal	11005N
+10622T	organism_part	57	fresh_sample		"Stage I"	Adenocarcinoma	10622N	"Homo sapiens"	male	years					10622T	organism_part	P-MEXP-9143	10622T	total_RNA	P-MEXP-9144	5	ug	"10622T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R991_10622T	A-MEXP-86	P-MEXP-9146	R991_10622T	R991_10622T.txt			"Stage I"	Adenocarcinoma	10622N
+10622N	organism_part	57	fresh_sample			normal	10622N	"Homo sapiens"	male	years					10622N	organism_part	P-MEXP-9143	10622N	total_RNA	P-MEXP-9144	5	ug	"10622N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R990_10622N	A-MEXP-86	P-MEXP-9146	R990_10622N	R990_10622N.txt				normal	10622N
+10605T	organism_part	62	fresh_sample		"Stage I"	Adenocarcinoma	10605N	"Homo sapiens"	male	years					10605T	organism_part	P-MEXP-9143	10605T	total_RNA	P-MEXP-9144	5	ug	"10605T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R989_10605T	A-MEXP-86	P-MEXP-9146	R989_10605T	R989_10605T.txt			"Stage I"	Adenocarcinoma	10605N
+10605N	organism_part	62	fresh_sample			normal	10605N	"Homo sapiens"	male	years					10605N	organism_part	P-MEXP-9143	10605N	total_RNA	P-MEXP-9144	5	ug	"10605N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R988_10605N	A-MEXP-86	P-MEXP-9146	R988_10605N	R988_10605N.txt				normal	10605N
+10455T	organism_part	66	fresh_sample		"Stage III or IV"	Adenocarcinoma	10455N	"Homo sapiens"	female	years					10455T	organism_part	P-MEXP-9143	10455T	total_RNA	P-MEXP-9144	5	ug	"10455T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R983_10455T	A-MEXP-86	P-MEXP-9146	R983_10455T	R983_10455T.txt			"Stage III or IV"	Adenocarcinoma	10455N
+10455N	organism_part	66	fresh_sample			normal	10455N	"Homo sapiens"	female	years					10455N	organism_part	P-MEXP-9143	10455N	total_RNA	P-MEXP-9144	5	ug	"10455N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R982_10455N	A-MEXP-86	P-MEXP-9146	R982_10455N	R982_10455N.txt				normal	10455N
+10419T	organism_part	72	fresh_sample		"Stage I"	Adenocarcinoma	10419N	"Homo sapiens"	female	years					10419T	organism_part	P-MEXP-9143	10419T	total_RNA	P-MEXP-9144	5	ug	"10419T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R977_10419T	A-MEXP-86	P-MEXP-9146	R977_10419T	R977_10419T.txt			"Stage I"	Adenocarcinoma	10419N
+10419N	organism_part	72	fresh_sample			normal	10419N	"Homo sapiens"	female	years					10419N	organism_part	P-MEXP-9143	10419N	total_RNA	P-MEXP-9144	5	ug	"10419N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R976_10419N	A-MEXP-86	P-MEXP-9146	R976_10419N	R976_10419N.txt				normal	10419N
+10213T	organism_part	72	fresh_sample		"Stage III or IV"	Adenocarcinoma	10213N	"Homo sapiens"	male	years					10213T	organism_part	P-MEXP-9143	10213T	total_RNA	P-MEXP-9144	5	ug	"10213T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R868_10213T	A-MEXP-86	P-MEXP-9146	R868_10213T	R868_10213T.txt			"Stage III or IV"	Adenocarcinoma	10213N
+10213N	organism_part	72	fresh_sample			normal	10213N	"Homo sapiens"	male	years					10213N	organism_part	P-MEXP-9143	10213N	total_RNA	P-MEXP-9144	5	ug	"10213N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R867_10213N	A-MEXP-86	P-MEXP-9146	R867_10213N	R867_10213N.txt				normal	10213N
+10210T	organism_part	79	fresh_sample		"Stage I"	Adenocarcinoma	10210N	"Homo sapiens"	male	years					10210T	organism_part	P-MEXP-9143	10210T	total_RNA	P-MEXP-9144	5	ug	"10210T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R866_10210T	A-MEXP-86	P-MEXP-9146	R866_10210T	R866_10210T.txt			"Stage I"	Adenocarcinoma	10210N
+10210N	organism_part	79	fresh_sample			normal	10210N	"Homo sapiens"	male	years					10210N	organism_part	P-MEXP-9143	10210N	total_RNA	P-MEXP-9144	5	ug	"10210N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R865_10210N	A-MEXP-86	P-MEXP-9146	R865_10210N	R865_10210N.txt				normal	10210N
+10018T	organism_part	63	fresh_sample		"Stage III or IV"	Adenocarcinoma	10018N	"Homo sapiens"	female	years					10018T	organism_part	P-MEXP-9143	10018T	total_RNA	P-MEXP-9144	5	ug	"10018T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1749_10018T	A-MEXP-86	P-MEXP-9146	R1749_10018T	R1749_10018T.txt			"Stage III or IV"	Adenocarcinoma	10018N
+10018N	organism_part	63	fresh_sample			normal	10018N	"Homo sapiens"	female	years					10018N	organism_part	P-MEXP-9143	10018N	total_RNA	P-MEXP-9144	5	ug	"10018N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1748_10018N	A-MEXP-86	P-MEXP-9146	R1748_10018N	R1748_10018N.txt				normal	10018N
+1822T	organism_part	61	fresh_sample		"Stage III or IV"	Adenocarcinoma	1822N	"Homo sapiens"	male	years					1822T	organism_part	P-MEXP-9143	1822T	total_RNA	P-MEXP-9144	5	ug	"1822T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1747_1822T	A-MEXP-86	P-MEXP-9146	R1747_1822T	R1747_1822T.txt			"Stage III or IV"	Adenocarcinoma	1822N
+1822N	organism_part	61	fresh_sample			normal	1822N	"Homo sapiens"	male	years					1822N	organism_part	P-MEXP-9143	1822N	total_RNA	P-MEXP-9144	5	ug	"1822N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1746_1822N	A-MEXP-86	P-MEXP-9146	R1746_1822N	R1746_1822N.txt				normal	1822N
+1818T	organism_part	55	fresh_sample		"Stage I"	Adenocarcinoma	1818N	"Homo sapiens"	male	years					1818T	organism_part	P-MEXP-9143	1818T	total_RNA	P-MEXP-9144	5	ug	"1818T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1743_1818T	A-MEXP-86	P-MEXP-9146	R1743_1818T	R1743_1818T.txt			"Stage I"	Adenocarcinoma	1818N
+1818N	organism_part	55	fresh_sample			normal	1818N	"Homo sapiens"	male	years					1818N	organism_part	P-MEXP-9143	1818N	total_RNA	P-MEXP-9144	5	ug	"1818N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1742_1818N	A-MEXP-86	P-MEXP-9146	R1742_1818N	R1742_1818N.txt				normal	1818N
+1729T	organism_part	73	fresh_sample		"Stage I"	Adenocarcinoma	1729N	"Homo sapiens"	female	years					1729T	organism_part	P-MEXP-9143	1729T	total_RNA	P-MEXP-9144	5	ug	"1729T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1731_1729T	A-MEXP-86	P-MEXP-9146	R1731_1729T	R1731_1729T.txt			"Stage I"	Adenocarcinoma	1729N
+1729N	organism_part	73	fresh_sample			normal	1729N	"Homo sapiens"	female	years					1729N	organism_part	P-MEXP-9143	1729N	total_RNA	P-MEXP-9144	5	ug	"1729N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1730_1729N	A-MEXP-86	P-MEXP-9146	R1730_1729N	R1730_1729N.txt				normal	1729N
+1718T	organism_part	58	fresh_sample		"Stage II"	Adenocarcinoma	1718N	"Homo sapiens"	female	years					1718T	organism_part	P-MEXP-9143	1718T	total_RNA	P-MEXP-9144	5	ug	"1718T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1729_1718T	A-MEXP-86	P-MEXP-9146	R1729_1718T	R1729_1718T.txt			"Stage II"	Adenocarcinoma	1718N
+1718N	organism_part	58	fresh_sample			normal	1718N	"Homo sapiens"	female	years					1718N	organism_part	P-MEXP-9143	1718N	total_RNA	P-MEXP-9144	5	ug	"1718N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1728_1718N	A-MEXP-86	P-MEXP-9146	R1728_1718N	R1728_1718N.txt				normal	1718N
+1610T	organism_part	69	fresh_sample		"Stage II"	Adenocarcinoma	1610N	"Homo sapiens"	female	years					1610T	organism_part	P-MEXP-9143	1610T	total_RNA	P-MEXP-9144	5	ug	"1610T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1723_1610T	A-MEXP-86	P-MEXP-9146	R1723_1610T	R1723_1610T.txt			"Stage II"	Adenocarcinoma	1610N
+1610N	organism_part	69	fresh_sample			normal	1610N	"Homo sapiens"	female	years					1610N	organism_part	P-MEXP-9143	1610N	total_RNA	P-MEXP-9144	5	ug	"1610N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1722_1610N	A-MEXP-86	P-MEXP-9146	R1722_1610N	R1722_1610N.txt				normal	1610N
+1604T	organism_part	59	fresh_sample		"Stage I"	Adenocarcinoma	1604N	"Homo sapiens"	female	years					1604T	organism_part	P-MEXP-9143	1604T	total_RNA	P-MEXP-9144	5	ug	"1604T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1721_1604T	A-MEXP-86	P-MEXP-9146	R1721_1604T	R1721_1604T.txt			"Stage I"	Adenocarcinoma	1604N
+1604N	organism_part	59	fresh_sample			normal	1604N	"Homo sapiens"	female	years					1604N	organism_part	P-MEXP-9143	1604N	total_RNA	P-MEXP-9144	5	ug	"1604N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1720_1604N	A-MEXP-86	P-MEXP-9146	R1720_1604N	R1720_1604N.txt				normal	1604N
+1570T	organism_part	70	fresh_sample		"Stage III or IV"	Adenocarcinoma	1570N	"Homo sapiens"	male	years					1570T	organism_part	P-MEXP-9143	1570T	total_RNA	P-MEXP-9144	5	ug	"1570T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1719_1570T	A-MEXP-86	P-MEXP-9146	R1719_1570T	R1719_1570T.txt			"Stage III or IV"	Adenocarcinoma	1570N
+1570N	organism_part	70	fresh_sample			normal	1570N	"Homo sapiens"	male	years					1570N	organism_part	P-MEXP-9143	1570N	total_RNA	P-MEXP-9144	5	ug	"1570N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1718_1570N	A-MEXP-86	P-MEXP-9146	R1718_1570N	R1718_1570N.txt				normal	1570N
+1545T	organism_part	69	fresh_sample		"Stage I"	Adenocarcinoma	1545N	"Homo sapiens"	female	years					1545T	organism_part	P-MEXP-9143	1545T	total_RNA	P-MEXP-9144	5	ug	"1545T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1715_1545T	A-MEXP-86	P-MEXP-9146	R1715_1545T	R1715_1545T.txt			"Stage I"	Adenocarcinoma	1545N
+1545N	organism_part	69	fresh_sample			normal	1545N	"Homo sapiens"	female	years					1545N	organism_part	P-MEXP-9143	1545N	total_RNA	P-MEXP-9144	5	ug	"1545N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1714_1545N	A-MEXP-86	P-MEXP-9146	R1714_1545N	R1714_1545N.txt				normal	1545N
+1532T	organism_part	62	fresh_sample		"Stage II"	Adenocarcinoma	1532N	"Homo sapiens"	male	years					1532T	organism_part	P-MEXP-9143	1532T	total_RNA	P-MEXP-9144	5	ug	"1532T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1711_1532T	A-MEXP-86	P-MEXP-9146	R1711_1532T	R1711_1532T.txt			"Stage II"	Adenocarcinoma	1532N
+1532N	organism_part	62	fresh_sample			normal	1532N	"Homo sapiens"	male	years					1532N	organism_part	P-MEXP-9143	1532N	total_RNA	P-MEXP-9144	5	ug	"1532N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1710_1532N	A-MEXP-86	P-MEXP-9146	R1710_1532N	R1710_1532N.txt				normal	1532N
+1498T	organism_part	67	fresh_sample		"Stage I"	Adenocarcinoma	1498N	"Homo sapiens"	female	years					1498T	organism_part	P-MEXP-9143	1498T	total_RNA	P-MEXP-9144	5	ug	"1498T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1707_1498T	A-MEXP-86	P-MEXP-9146	R1707_1498T	R1707_1498T.txt			"Stage I"	Adenocarcinoma	1498N
+1498N	organism_part	67	fresh_sample			normal	1498N	"Homo sapiens"	female	years					1498N	organism_part	P-MEXP-9143	1498N	total_RNA	P-MEXP-9144	5	ug	"1498N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1600_1498N	A-MEXP-86	P-MEXP-9146	R1600_1498N	R1600_1498N.txt				normal	1498N
+1449T	organism_part	79	fresh_sample		"Stage II"	Adenocarcinoma	1449N	"Homo sapiens"	female	years					1449T	organism_part	P-MEXP-9143	1449T	total_RNA	P-MEXP-9144	5	ug	"1449T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1599_1449T	A-MEXP-86	P-MEXP-9146	R1599_1449T	R1599_1449T.txt			"Stage II"	Adenocarcinoma	1449N
+1449N	organism_part	79	fresh_sample			normal	1449N	"Homo sapiens"	female	years					1449N	organism_part	P-MEXP-9143	1449N	total_RNA	P-MEXP-9144	5	ug	"1449N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1598_1449N	A-MEXP-86	P-MEXP-9146	R1598_1449N	R1598_1449N.txt				normal	1449N
+1249T	organism_part	54	fresh_sample		"Stage I"	Adenocarcinoma	1249N	"Homo sapiens"	male	years					1249T	organism_part	P-MEXP-9143	1249T	total_RNA	P-MEXP-9144	5	ug	"1249T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1557_1249T	A-MEXP-86	P-MEXP-9146	R1557_1249T	R1557_1249T.txt			"Stage I"	Adenocarcinoma	1249N
+1249N	organism_part	54	fresh_sample			normal	1249N	"Homo sapiens"	male	years					1249N	organism_part	P-MEXP-9143	1249N	total_RNA	P-MEXP-9144	5	ug	"1249N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1550_1249N	A-MEXP-86	P-MEXP-9146	R1550_1249N	R1550_1249N.txt				normal	1249N
+1180T	organism_part	76	fresh_sample		"Stage I"	Adenocarcinoma	1180N	"Homo sapiens"	male	years					1180T	organism_part	P-MEXP-9143	1180T	total_RNA	P-MEXP-9144	5	ug	"1180T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R892_1180T	A-MEXP-86	P-MEXP-9146	R892_1180T	R892_1180T.txt			"Stage I"	Adenocarcinoma	1180N
+1180N	organism_part	76	fresh_sample			normal	1180N	"Homo sapiens"	male	years					1180N	organism_part	P-MEXP-9143	1180N	total_RNA	P-MEXP-9144	5	ug	"1180N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R891_1180N	A-MEXP-86	P-MEXP-9146	R891_1180N	R891_1180N.txt				normal	1180N
+12979T	organism_part	52	fresh_sample		"Stage I"	"Squamous Cell Carcinoma"	12979N	"Homo sapiens"	male	years					12979T	organism_part	P-MEXP-9143	12979T	total_RNA	P-MEXP-9144	5	ug	"12979T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1597_12979T	A-MEXP-86	P-MEXP-9146	R1597_12979T	R1597_12979T.txt			"Stage I"	"Squamous Cell Carcinoma"	12979N
+12979N	organism_part	52	fresh_sample			normal	12979N	"Homo sapiens"	male	years					12979N	organism_part	P-MEXP-9143	12979N	total_RNA	P-MEXP-9144	5	ug	"12979N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1596_12979N	A-MEXP-86	P-MEXP-9146	R1596_12979N	R1596_12979N.txt				normal	12979N
+12281T	organism_part	59	fresh_sample		"Stage I"	"Squamous Cell Carcinoma"	12281N	"Homo sapiens"	female	years					12281T	organism_part	P-MEXP-9143	12281T	total_RNA	P-MEXP-9144	5	ug	"12281T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1531_12281T	A-MEXP-86	P-MEXP-9146	R1531_12281T	R1531_12281T.txt			"Stage I"	"Squamous Cell Carcinoma"	12281N
+12281N	organism_part	59	fresh_sample			normal	12281N	"Homo sapiens"	female	years					12281N	organism_part	P-MEXP-9143	12281N	total_RNA	P-MEXP-9144	5	ug	"12281N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1530_12281N	A-MEXP-86	P-MEXP-9146	R1530_12281N	R1530_12281N.txt				normal	12281N
+12187T	organism_part	72	fresh_sample		"Stage I"	"Squamous Cell Carcinoma"	12187N	"Homo sapiens"	female	years					12187T	organism_part	P-MEXP-9143	12187T	total_RNA	P-MEXP-9144	5	ug	"12187T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1523_12187T	A-MEXP-86	P-MEXP-9146	R1523_12187T	R1523_12187T.txt			"Stage I"	"Squamous Cell Carcinoma"	12187N
+12187N	organism_part	72	fresh_sample			normal	12187N	"Homo sapiens"	female	years					12187N	organism_part	P-MEXP-9143	12187N	total_RNA	P-MEXP-9144	5	ug	"12187N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1522_12187N	A-MEXP-86	P-MEXP-9146	R1522_12187N	R1522_12187N.txt				normal	12187N
+12147T	organism_part	56	fresh_sample		"Stage I"	"Squamous Cell Carcinoma"	12147N	"Homo sapiens"	male	years					12147T	organism_part	P-MEXP-9143	12147T	total_RNA	P-MEXP-9144	5	ug	"12147T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1515_12147T	A-MEXP-86	P-MEXP-9146	R1515_12147T	R1515_12147T.txt			"Stage I"	"Squamous Cell Carcinoma"	12147N
+12147N	organism_part	56	fresh_sample			normal	12147N	"Homo sapiens"	male	years					12147N	organism_part	P-MEXP-9143	12147N	total_RNA	P-MEXP-9144	5	ug	"12147N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1514_12147N	A-MEXP-86	P-MEXP-9146	R1514_12147N	R1514_12147N.txt				normal	12147N
+12104T	organism_part	55	fresh_sample		"Stage II"	"Squamous Cell Carcinoma"	12104N	"Homo sapiens"	female	years					12104T	organism_part	P-MEXP-9143	12104T	total_RNA	P-MEXP-9144	5	ug	"12104T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1511_12104T	A-MEXP-86	P-MEXP-9146	R1511_12104T	R1511_12104T.txt			"Stage II"	"Squamous Cell Carcinoma"	12104N
+12104N	organism_part	55	fresh_sample			normal	12104N	"Homo sapiens"	female	years					12104N	organism_part	P-MEXP-9143	12104N	total_RNA	P-MEXP-9144	5	ug	"12104N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1510_12104N	A-MEXP-86	P-MEXP-9146	R1510_12104N	R1510_12104N.txt				normal	12104N
+12027T	organism_part	73	fresh_sample		"Stage I"	"Squamous Cell Carcinoma"	12027N	"Homo sapiens"	female	years					12027T	organism_part	P-MEXP-9143	12027T	total_RNA	P-MEXP-9144	5	ug	"12027T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1499_12027T	A-MEXP-86	P-MEXP-9146	R1499_12027T	R1499_12027T.txt			"Stage I"	"Squamous Cell Carcinoma"	12027N
+12027N	organism_part	73	fresh_sample			normal	12027N	"Homo sapiens"	female	years					12027N	organism_part	P-MEXP-9143	12027N	total_RNA	P-MEXP-9144	5	ug	"12027N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1498_12027N	A-MEXP-86	P-MEXP-9146	R1498_12027N	R1498_12027N.txt				normal	12027N
+12025T	organism_part	49	fresh_sample		"Stage II"	"Squamous Cell Carcinoma"	12025N	"Homo sapiens"	male	years					12025T	organism_part	P-MEXP-9143	12025T	total_RNA	P-MEXP-9144	5	ug	"12025T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1497_12025T	A-MEXP-86	P-MEXP-9146	R1497_12025T	R1497_12025T.txt			"Stage II"	"Squamous Cell Carcinoma"	12025N
+12025N	organism_part	49	fresh_sample			normal	12025N	"Homo sapiens"	male	years					12025N	organism_part	P-MEXP-9143	12025N	total_RNA	P-MEXP-9144	5	ug	"12025N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1100_12025N	A-MEXP-86	P-MEXP-9146	R1100_12025N	R1100_12025N.txt				normal	12025N
+12017T	organism_part	67	fresh_sample		"Stage I"	"Squamous Cell Carcinoma"	12017N	"Homo sapiens"	female	years					12017T	organism_part	P-MEXP-9143	12017T	total_RNA	P-MEXP-9144	5	ug	"12017T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1765_12017T	A-MEXP-86	P-MEXP-9146	R1765_12017T	R1765_12017T.txt			"Stage I"	"Squamous Cell Carcinoma"	12017N
+12017N	organism_part	67	fresh_sample			normal	12017N	"Homo sapiens"	female	years					12017N	organism_part	P-MEXP-9143	12017N	total_RNA	P-MEXP-9144	5	ug	"12017N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1764_12017N	A-MEXP-86	P-MEXP-9146	R1764_12017N	R1764_12017N.txt				normal	12017N
+12003T	organism_part	66	fresh_sample		"Stage I"	"Squamous Cell Carcinoma"	12003N	"Homo sapiens"	male	years					12003T	organism_part	P-MEXP-9143	12003T	total_RNA	P-MEXP-9144	5	ug	"12003T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1496_12003T	A-MEXP-86	P-MEXP-9146	R1496_12003T	R1496_12003T.txt			"Stage I"	"Squamous Cell Carcinoma"	12003N
+12003N	organism_part	66	fresh_sample			normal	12003N	"Homo sapiens"	male	years					12003N	organism_part	P-MEXP-9143	12003N	total_RNA	P-MEXP-9144	5	ug	"12003N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1495_12003N	A-MEXP-86	P-MEXP-9146	R1495_12003N	R1495_12003N.txt				normal	12003N
+11950T	organism_part	66	fresh_sample		"Stage II"	"Squamous Cell Carcinoma"	11950N	"Homo sapiens"	male	years					11950T	organism_part	P-MEXP-9143	11950T	total_RNA	P-MEXP-9144	5	ug	"11950T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1097_11950T	A-MEXP-86	P-MEXP-9146	R1097_11950T	R1097_11950T.txt			"Stage II"	"Squamous Cell Carcinoma"	11950N
+11950N	organism_part	66	fresh_sample			normal	11950N	"Homo sapiens"	male	years					11950N	organism_part	P-MEXP-9143	11950N	total_RNA	P-MEXP-9144	5	ug	"11950N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1096_11950N	A-MEXP-86	P-MEXP-9146	R1096_11950N	R1096_11950N.txt				normal	11950N
+11923T	organism_part	72	fresh_sample		"Stage I"	"Squamous Cell Carcinoma"	11923N	"Homo sapiens"	female	years					11923T	organism_part	P-MEXP-9143	11923T	total_RNA	P-MEXP-9144	5	ug	"11923T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1093_11923T	A-MEXP-86	P-MEXP-9146	R1093_11923T	R1093_11923T.txt			"Stage I"	"Squamous Cell Carcinoma"	11923N
+11923N	organism_part	72	fresh_sample			normal	11923N	"Homo sapiens"	female	years					11923N	organism_part	P-MEXP-9143	11923N	total_RNA	P-MEXP-9144	5	ug	"11923N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1092_11923N	A-MEXP-86	P-MEXP-9146	R1092_11923N	R1092_11923N.txt				normal	11923N
+11764T	organism_part	68	fresh_sample		"Stage I"	"Squamous Cell Carcinoma"	11764N	"Homo sapiens"	male	years					11764T	organism_part	P-MEXP-9143	11764T	total_RNA	P-MEXP-9144	5	ug	"11764T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1763_11764T	A-MEXP-86	P-MEXP-9146	R1763_11764T	R1763_11764T.txt			"Stage I"	"Squamous Cell Carcinoma"	11764N
+11764N	organism_part	68	fresh_sample			normal	11764N	"Homo sapiens"	male	years					11764N	organism_part	P-MEXP-9143	11764N	total_RNA	P-MEXP-9144	5	ug	"11764N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1762_11764N	A-MEXP-86	P-MEXP-9146	R1762_11764N	R1762_11764N.txt				normal	11764N
+11759T	organism_part	67	fresh_sample		"Stage I"	"Squamous Cell Carcinoma"	11759N	"Homo sapiens"	male	years					11759T	organism_part	P-MEXP-9143	11759T	total_RNA	P-MEXP-9144	5	ug	"11759T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R886_11759T	A-MEXP-86	P-MEXP-9146	R886_11759T	R886_11759T.txt			"Stage I"	"Squamous Cell Carcinoma"	11759N
+11759N	organism_part	67	fresh_sample			normal	11759N	"Homo sapiens"	male	years					11759N	organism_part	P-MEXP-9143	11759N	total_RNA	P-MEXP-9144	5	ug	"11759N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R885_11759N	A-MEXP-86	P-MEXP-9146	R885_11759N	R885_11759N.txt				normal	11759N
+11728T	organism_part	53	fresh_sample		"Stage II"	"Squamous Cell Carcinoma"	11728N	"Homo sapiens"	male	years					11728T	organism_part	P-MEXP-9143	11728T	total_RNA	P-MEXP-9144	5	ug	"11728T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R884_11728T	A-MEXP-86	P-MEXP-9146	R884_11728T	R884_11728T.txt			"Stage II"	"Squamous Cell Carcinoma"	11728N
+11728N	organism_part	53	fresh_sample			normal	11728N	"Homo sapiens"	male	years					11728N	organism_part	P-MEXP-9143	11728N	total_RNA	P-MEXP-9144	5	ug	"11728N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R883_11728N	A-MEXP-86	P-MEXP-9146	R883_11728N	R883_11728N.txt				normal	11728N
+11588T	organism_part	69	fresh_sample		"Stage I"	"Squamous Cell Carcinoma"	11588N	"Homo sapiens"	female	years					11588T	organism_part	P-MEXP-9143	11588T	total_RNA	P-MEXP-9144	5	ug	"11588T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1082_11588T	A-MEXP-86	P-MEXP-9146	R1082_11588T	R1082_11588T.txt			"Stage I"	"Squamous Cell Carcinoma"	11588N
+11588N	organism_part	69	fresh_sample			normal	11588N	"Homo sapiens"	female	years					11588N	organism_part	P-MEXP-9143	11588N	total_RNA	P-MEXP-9144	5	ug	"11588N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1081_11588N	A-MEXP-86	P-MEXP-9146	R1081_11588N	R1081_11588N.txt				normal	11588N
+11574T	organism_part	63	fresh_sample		"Stage III or IV"	"Squamous Cell Carcinoma"	11574N	"Homo sapiens"	male	years					11574T	organism_part	P-MEXP-9143	11574T	total_RNA	P-MEXP-9144	5	ug	"11574T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1080_11574T	A-MEXP-86	P-MEXP-9146	R1080_11574T	R1080_11574T.txt			"Stage III or IV"	"Squamous Cell Carcinoma"	11574N
+11574N	organism_part	63	fresh_sample			normal	11574N	"Homo sapiens"	male	years					11574N	organism_part	P-MEXP-9143	11574N	total_RNA	P-MEXP-9144	5	ug	"11574N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1079_11574N	A-MEXP-86	P-MEXP-9146	R1079_11574N	R1079_11574N.txt				normal	11574N
+11238T	organism_part	69	fresh_sample		"Stage I"	"Squamous Cell Carcinoma"	11238N	"Homo sapiens"	male	years					11238T	organism_part	P-MEXP-9143	11238T	total_RNA	P-MEXP-9144	5	ug	"11238T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1058_11238T	A-MEXP-86	P-MEXP-9146	R1058_11238T	R1058_11238T.txt			"Stage I"	"Squamous Cell Carcinoma"	11238N
+11238N	organism_part	69	fresh_sample			normal	11238N	"Homo sapiens"	male	years					11238N	organism_part	P-MEXP-9143	11238N	total_RNA	P-MEXP-9144	5	ug	"11238N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1057_11238N	A-MEXP-86	P-MEXP-9146	R1057_11238N	R1057_11238N.txt				normal	11238N
+11047T	organism_part	45	fresh_sample		"Stage II"	"Squamous Cell Carcinoma"	11047N	"Homo sapiens"	female	years					11047T	organism_part	P-MEXP-9143	11047T	total_RNA	P-MEXP-9144	5	ug	"11047T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1030_11047T	A-MEXP-86	P-MEXP-9146	R1030_11047T	R1030_11047T.txt			"Stage II"	"Squamous Cell Carcinoma"	11047N
+11047N	organism_part	45	fresh_sample			normal	11047N	"Homo sapiens"	female	years					11047N	organism_part	P-MEXP-9143	11047N	total_RNA	P-MEXP-9144	5	ug	"11047N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1029_11047N	A-MEXP-86	P-MEXP-9146	R1029_11047N	R1029_11047N.txt				normal	11047N
+11045T	organism_part	59	fresh_sample		"Stage III or IV"	"Squamous Cell Carcinoma"	11045N	"Homo sapiens"	male	years					11045T	organism_part	P-MEXP-9143	11045T	total_RNA	P-MEXP-9144	5	ug	"11045T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1028_11045T	A-MEXP-86	P-MEXP-9146	R1028_11045T	R1028_11045T.txt			"Stage III or IV"	"Squamous Cell Carcinoma"	11045N
+11045N	organism_part	59	fresh_sample			normal	11045N	"Homo sapiens"	male	years					11045N	organism_part	P-MEXP-9143	11045N	total_RNA	P-MEXP-9144	5	ug	"11045N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1027_11045N	A-MEXP-86	P-MEXP-9146	R1027_11045N	R1027_11045N.txt				normal	11045N
+11018T	organism_part	70	fresh_sample		"Stage II"	"Squamous Cell Carcinoma"	11018N	"Homo sapiens"	male	years					11018T	organism_part	P-MEXP-9143	11018T	total_RNA	P-MEXP-9144	5	ug	"11018T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1024_11018T	A-MEXP-86	P-MEXP-9146	R1024_11018T	R1024_11018T.txt			"Stage II"	"Squamous Cell Carcinoma"	11018N
+11018N	organism_part	70	fresh_sample			normal	11018N	"Homo sapiens"	male	years					11018N	organism_part	P-MEXP-9143	11018N	total_RNA	P-MEXP-9144	5	ug	"11018N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1023_11018N	A-MEXP-86	P-MEXP-9146	R1023_11018N	R1023_11018N.txt				normal	11018N
+11008T	organism_part	60	fresh_sample		"Stage I"	"Squamous Cell Carcinoma"	11008N	"Homo sapiens"	female	years					11008T	organism_part	P-MEXP-9143	11008T	total_RNA	P-MEXP-9144	5	ug	"11008T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1022_11008T	A-MEXP-86	P-MEXP-9146	R1022_11008T	R1022_11008T.txt			"Stage I"	"Squamous Cell Carcinoma"	11008N
+11008N	organism_part	60	fresh_sample			normal	11008N	"Homo sapiens"	female	years					11008N	organism_part	P-MEXP-9143	11008N	total_RNA	P-MEXP-9144	5	ug	"11008N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1021_11008N	A-MEXP-86	P-MEXP-9146	R1021_11008N	R1021_11008N.txt				normal	11008N
+11002T	organism_part	74	fresh_sample		"Stage I"	"Squamous Cell Carcinoma"	11002N	"Homo sapiens"	male	years					11002T	organism_part	P-MEXP-9143	11002T	total_RNA	P-MEXP-9144	5	ug	"11002T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1018_11002T	A-MEXP-86	P-MEXP-9146	R1018_11002T	R1018_11002T.txt			"Stage I"	"Squamous Cell Carcinoma"	11002N
+11002N	organism_part	74	fresh_sample			normal	11002N	"Homo sapiens"	male	years					11002N	organism_part	P-MEXP-9143	11002N	total_RNA	P-MEXP-9144	5	ug	"11002N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1017_11002N	A-MEXP-86	P-MEXP-9146	R1017_11002N	R1017_11002N.txt				normal	11002N
+10941T	organism_part	68	fresh_sample		"Stage I"	"Squamous Cell Carcinoma"	10941N	"Homo sapiens"	male	years					10941T	organism_part	P-MEXP-9143	10941T	total_RNA	P-MEXP-9144	5	ug	"10941T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1014_10941T	A-MEXP-86	P-MEXP-9146	R1014_10941T	R1014_10941T.txt			"Stage I"	"Squamous Cell Carcinoma"	10941N
+10941N	organism_part	68	fresh_sample			normal	10941N	"Homo sapiens"	male	years					10941N	organism_part	P-MEXP-9143	10941N	total_RNA	P-MEXP-9144	5	ug	"10941N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1013_10941N	A-MEXP-86	P-MEXP-9146	R1013_10941N	R1013_10941N.txt				normal	10941N
+10788T	organism_part	54	fresh_sample		"Stage III or IV"	"Squamous Cell Carcinoma"	10788N	"Homo sapiens"	female	years					10788T	organism_part	P-MEXP-9143	10788T	total_RNA	P-MEXP-9144	5	ug	"10788T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1007_10788T	A-MEXP-86	P-MEXP-9146	R1007_10788T	R1007_10788T.txt			"Stage III or IV"	"Squamous Cell Carcinoma"	10788N
+10788N	organism_part	54	fresh_sample			normal	10788N	"Homo sapiens"	female	years					10788N	organism_part	P-MEXP-9143	10788N	total_RNA	P-MEXP-9144	5	ug	"10788N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1000_10788N	A-MEXP-86	P-MEXP-9146	R1000_10788N	R1000_10788N.txt				normal	10788N
+10780T	organism_part	75	fresh_sample		"Stage III or IV"	"Squamous Cell Carcinoma"	10780N	"Homo sapiens"	female	years					10780T	organism_part	P-MEXP-9143	10780T	total_RNA	P-MEXP-9144	5	ug	"10780T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R999_10780T	A-MEXP-86	P-MEXP-9146	R999_10780T	R999_10780T.txt			"Stage III or IV"	"Squamous Cell Carcinoma"	10780N
+10780N	organism_part	75	fresh_sample			normal	10780N	"Homo sapiens"	female	years					10780N	organism_part	P-MEXP-9143	10780N	total_RNA	P-MEXP-9144	5	ug	"10780N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R998_10780N	A-MEXP-86	P-MEXP-9146	R998_10780N	R998_10780N.txt				normal	10780N
+10759T	organism_part	72	fresh_sample		"Stage III or IV"	"Squamous Cell Carcinoma"	10759N	"Homo sapiens"	male	years					10759T	organism_part	P-MEXP-9143	10759T	total_RNA	P-MEXP-9144	5	ug	"10759T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R997_10759T	A-MEXP-86	P-MEXP-9146	R997_10759T	R997_10759T.txt			"Stage III or IV"	"Squamous Cell Carcinoma"	10759N
+10759N	organism_part	72	fresh_sample			normal	10759N	"Homo sapiens"	male	years					10759N	organism_part	P-MEXP-9143	10759N	total_RNA	P-MEXP-9144	5	ug	"10759N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R996_10759N	A-MEXP-86	P-MEXP-9146	R996_10759N	R996_10759N.txt				normal	10759N
+10743T	organism_part	54	fresh_sample		"Stage I"	"Squamous Cell Carcinoma"	10743N	"Homo sapiens"	male	years					10743T	organism_part	P-MEXP-9143	10743T	total_RNA	P-MEXP-9144	5	ug	"10743T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R995_10743T	A-MEXP-86	P-MEXP-9146	R995_10743T	R995_10743T.txt			"Stage I"	"Squamous Cell Carcinoma"	10743N
+10743N	organism_part	54	fresh_sample			normal	10743N	"Homo sapiens"	male	years					10743N	organism_part	P-MEXP-9143	10743N	total_RNA	P-MEXP-9144	5	ug	"10743N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R994_10743N	A-MEXP-86	P-MEXP-9146	R994_10743N	R994_10743N.txt				normal	10743N
+10417T	organism_part	74	fresh_sample		"Stage II"	"Squamous Cell Carcinoma"	10417N	"Homo sapiens"	male	years					10417T	organism_part	P-MEXP-9143	10417T	total_RNA	P-MEXP-9144	5	ug	"10417T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R975_10417T	A-MEXP-86	P-MEXP-9146	R975_10417T	R975_10417T.txt			"Stage II"	"Squamous Cell Carcinoma"	10417N
+10417N	organism_part	74	fresh_sample			normal	10417N	"Homo sapiens"	male	years					10417N	organism_part	P-MEXP-9143	10417N	total_RNA	P-MEXP-9144	5	ug	"10417N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R874_10417N	A-MEXP-86	P-MEXP-9146	R874_10417N	R874_10417N.txt				normal	10417N
+10404T	organism_part	78	fresh_sample		"Stage II"	"Squamous Cell Carcinoma"	10404N	"Homo sapiens"	male	years					10404T	organism_part	P-MEXP-9143	10404T	total_RNA	P-MEXP-9144	5	ug	"10404T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R873_10404T	A-MEXP-86	P-MEXP-9146	R873_10404T	R873_10404T.txt			"Stage II"	"Squamous Cell Carcinoma"	10404N
+10404N	organism_part	78	fresh_sample			normal	10404N	"Homo sapiens"	male	years					10404N	organism_part	P-MEXP-9143	10404N	total_RNA	P-MEXP-9144	5	ug	"10404N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R871_10404N	A-MEXP-86	P-MEXP-9146	R871_10404N	R871_10404N.txt				normal	10404N
+10296T	organism_part	73	fresh_sample		"Stage I"	"Squamous Cell Carcinoma"	10296N	"Homo sapiens"	male	years					10296T	organism_part	P-MEXP-9143	10296T	total_RNA	P-MEXP-9144	5	ug	"10296T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R870_10296T	A-MEXP-86	P-MEXP-9146	R870_10296T	R870_10296T.txt			"Stage I"	"Squamous Cell Carcinoma"	10296N
+10296N	organism_part	73	fresh_sample			normal	10296N	"Homo sapiens"	male	years					10296N	organism_part	P-MEXP-9143	10296N	total_RNA	P-MEXP-9144	5	ug	"10296N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R869_10296N	A-MEXP-86	P-MEXP-9146	R869_10296N	R869_10296N.txt				normal	10296N
+10286T	organism_part	65	fresh_sample		"Stage I"	"Squamous Cell Carcinoma"	10286N	"Homo sapiens"	male	years					10286T	organism_part	P-MEXP-9143	10286T	total_RNA	P-MEXP-9144	5	ug	"10286T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1759_10286T	A-MEXP-86	P-MEXP-9146	R1759_10286T	R1759_10286T.txt			"Stage I"	"Squamous Cell Carcinoma"	10286N
+10286N	organism_part	65	fresh_sample			normal	10286N	"Homo sapiens"	male	years					10286N	organism_part	P-MEXP-9143	10286N	total_RNA	P-MEXP-9144	5	ug	"10286N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1758_10286N	A-MEXP-86	P-MEXP-9146	R1758_10286N	R1758_10286N.txt				normal	10286N
+10070T	organism_part	75	fresh_sample		"Stage I"	"Squamous Cell Carcinoma"	10070N	"Homo sapiens"	male	years					10070T	organism_part	P-MEXP-9143	10070T	total_RNA	P-MEXP-9144	5	ug	"10070T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1757_10070T	A-MEXP-86	P-MEXP-9146	R1757_10070T	R1757_10070T.txt			"Stage I"	"Squamous Cell Carcinoma"	10070N
+10070N	organism_part	75	fresh_sample			normal	10070N	"Homo sapiens"	male	years					10070N	organism_part	P-MEXP-9143	10070N	total_RNA	P-MEXP-9144	5	ug	"10070N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1750_10070N	A-MEXP-86	P-MEXP-9146	R1750_10070N	R1750_10070N.txt				normal	10070N
+1820T	organism_part	51	fresh_sample		"Stage II"	"Squamous Cell Carcinoma"	1820N	"Homo sapiens"	male	years					1820T	organism_part	P-MEXP-9143	1820T	total_RNA	P-MEXP-9144	5	ug	"1820T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1745_1820T	A-MEXP-86	P-MEXP-9146	R1745_1820T	R1745_1820T.txt			"Stage II"	"Squamous Cell Carcinoma"	1820N
+1820N	organism_part	51	fresh_sample			normal	1820N	"Homo sapiens"	male	years					1820N	organism_part	P-MEXP-9143	1820N	total_RNA	P-MEXP-9144	5	ug	"1820N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1744_1820N	A-MEXP-86	P-MEXP-9146	R1744_1820N	R1744_1820N.txt				normal	1820N
+1800T	organism_part	74	fresh_sample		"Stage I"	"Squamous Cell Carcinoma"	1800N	"Homo sapiens"	male	years					1800T	organism_part	P-MEXP-9143	1800T	total_RNA	P-MEXP-9144	5	ug	"1800T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1741_1800T	A-MEXP-86	P-MEXP-9146	R1741_1800T	R1741_1800T.txt			"Stage I"	"Squamous Cell Carcinoma"	1800N
+1800N	organism_part	74	fresh_sample			normal	1800N	"Homo sapiens"	male	years					1800N	organism_part	P-MEXP-9143	1800N	total_RNA	P-MEXP-9144	5	ug	"1800N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1740_1800N	A-MEXP-86	P-MEXP-9146	R1740_1800N	R1740_1800N.txt				normal	1800N
+1799T	organism_part	66	fresh_sample		"Stage I"	"Squamous Cell Carcinoma"	1799N	"Homo sapiens"	male	years					1799T	organism_part	P-MEXP-9143	1799T	total_RNA	P-MEXP-9144	5	ug	"1799T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1739_1799T	A-MEXP-86	P-MEXP-9146	R1739_1799T	R1739_1799T.txt			"Stage I"	"Squamous Cell Carcinoma"	1799N
+1799N	organism_part	66	fresh_sample			normal	1799N	"Homo sapiens"	male	years					1799N	organism_part	P-MEXP-9143	1799N	total_RNA	P-MEXP-9144	5	ug	"1799N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1738_1799N	A-MEXP-86	P-MEXP-9146	R1738_1799N	R1738_1799N.txt				normal	1799N
+1797T	organism_part	73	fresh_sample		"Stage I"	"Squamous Cell Carcinoma"	1797N	"Homo sapiens"	female	years					1797T	organism_part	P-MEXP-9143	1797T	total_RNA	P-MEXP-9144	5	ug	"1797T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1735_1797T	A-MEXP-86	P-MEXP-9146	R1735_1797T	R1735_1797T.txt			"Stage I"	"Squamous Cell Carcinoma"	1797N
+1797N	organism_part	73	fresh_sample			normal	1797N	"Homo sapiens"	female	years					1797N	organism_part	P-MEXP-9143	1797N	total_RNA	P-MEXP-9144	5	ug	"1797N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1734_1797N	A-MEXP-86	P-MEXP-9146	R1734_1797N	R1734_1797N.txt				normal	1797N
+1746T	organism_part	72	fresh_sample		"Stage I"	"Squamous Cell Carcinoma"	1746N	"Homo sapiens"	female	years					1746T	organism_part	P-MEXP-9143	1746T	total_RNA	P-MEXP-9144	5	ug	"1746T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1733_1746T	A-MEXP-86	P-MEXP-9146	R1733_1746T	R1733_1746T.txt			"Stage I"	"Squamous Cell Carcinoma"	1746N
+1746N	organism_part	72	fresh_sample			normal	1746N	"Homo sapiens"	female	years					1746N	organism_part	P-MEXP-9143	1746N	total_RNA	P-MEXP-9144	5	ug	"1746N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1732_1746N	A-MEXP-86	P-MEXP-9146	R1732_1746N	R1732_1746N.txt				normal	1746N
+1614T	organism_part	63	fresh_sample		"Stage III or IV"	"Squamous Cell Carcinoma"	1614N	"Homo sapiens"	male	years					1614T	organism_part	P-MEXP-9143	1614T	total_RNA	P-MEXP-9144	5	ug	"1614T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1725_1614T	A-MEXP-86	P-MEXP-9146	R1725_1614T	R1725_1614T.txt			"Stage III or IV"	"Squamous Cell Carcinoma"	1614N
+1614N	organism_part	63	fresh_sample			normal	1614N	"Homo sapiens"	male	years					1614N	organism_part	P-MEXP-9143	1614N	total_RNA	P-MEXP-9144	5	ug	"1614N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1724_1614N	A-MEXP-86	P-MEXP-9146	R1724_1614N	R1724_1614N.txt				normal	1614N
+1535T	organism_part	71	fresh_sample		"Stage I"	"Squamous Cell Carcinoma"	1535N	"Homo sapiens"	male	years					1535T	organism_part	P-MEXP-9143	1535T	total_RNA	P-MEXP-9144	5	ug	"1535T biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1713_1535T	A-MEXP-86	P-MEXP-9146	R1713_1535T	R1713_1535T.txt			"Stage I"	"Squamous Cell Carcinoma"	1535N
+1535N	organism_part	71	fresh_sample			normal	1535N	"Homo sapiens"	male	years					1535N	organism_part	P-MEXP-9143	1535N	total_RNA	P-MEXP-9144	5	ug	"1535N biotin"	synthetic_DNA	biotin	P-MEXP-9145	25	degree_C	5	ug	R1712_1535N	A-MEXP-86	P-MEXP-9146	R1712_1535N	R1712_1535N.txt				normal	1535N
diff --git a/examples/magetab/real/E-TABM-33.idf b/examples/magetab/real/E-TABM-33.idf
new file mode 100644
index 0000000..7d5882f
--- /dev/null
+++ b/examples/magetab/real/E-TABM-33.idf
@@ -0,0 +1,27 @@
+"Investigation Title"	"Micorarray analysis of gene expression in zebrafish development"		
+"Experimental Design"	development_or_differentiation_design	time_series_design	
+"Experimental Factor Name"	TIME	DEVELOPMENTALSTAGE	
+"Experimental Factor Type"	time	developmental_stage	
+			
+"Person Last Name"	Saric	Konantz	
+"Person First Name"	Marc	Martina	
+"Person Mid Initial"			
+"Person Affiliation"	"Max-Planck-Institute for Developmental Biology, T�bingen, Germany"		
+"Person Roles"	submitter	submitter	
+			
+"Quality Control Types"	biological_replicate	spike_quality_control	real_time_PCR_quality_control
+"Public Release Date"	"2005-08-18"		
+			
+Comment[ArrayExpressSubmissionDate]	"2005-08-18"		
+			
+"Publication Author List"	"Martina Konantz; Georg-Wilhelm Otto; Christian Weiler; Marc Saric; Robert Geisler"		
+"Publication Title"	"Micorarray analysis of gene expression in zebrafish development"		
+"Experiment Description"	"We are performing microarray experiments for expression profiling of zebrafish embryogenesis, both as a baseline for future analysis of mutant and other conditions and to validate our microarray technology.  For our purpose we used the Affymetrix zebrafish array which contains approximately 15,000 genes. This represents about 50 % of the estimated number of zebrafish genes.  Total RNA was collected from embryos at 16 different stages (zygote, shield stage, 75 %  [...]
+			
+"Protocol Name"	P-TABM-55	P-TABM-56	P-AFFY-2
+"Protocol Type"	"grow, sacrifice, store"	"nucleic_acid_extraction, store"	labeling
+"Protocol Description"	"T�bingen wildtype-fish were maintained as described in N�sslein-Volhard and Dahm: Zebrafish, PAS Oxford Univ. Press ISBN 019963808 X. Staging was done by hours postfertilization (Kimmel et al) in liquid nitrogen and embryos were stored at  - 80 �C. "	"Briefly, total RNAs were prepared from every stages using TRIzol reagent (Invitrogen GmbH, Germany) with an added chlorophorm extraction before precipitation. Quality of embryonic RNA was assesed using the NanoDrop N [...]
+"Protocol Parameters"			
+"Protocol Software"			
+			
+"SDRF File"	E-TABM-33_sdrf.txt		
diff --git a/examples/magetab/real/E-TABM-33_sdrf.txt b/examples/magetab/real/E-TABM-33_sdrf.txt
new file mode 100644
index 0000000..e749a60
--- /dev/null
+++ b/examples/magetab/real/E-TABM-33_sdrf.txt
@@ -0,0 +1,31 @@
+"Source Name"	"Material Type"	"Characteristics [Age]"	"Characteristics [BioSourceType]"	"Characteristics [DevelopmentalStage]"	"Characteristics [Genotype]"	"Characteristics [InitialTimePoint]"	"Characteristics [Organism]"	"Characteristics [StrainOrLine]"	"Characteristics [TimeUnit]"	"Protocol REF"	"Sample Name"	"Material Type"	"Protocol REF"	"Extract Name"	"Material Type"	"Protocol REF"	"Labeled Extract Name"	"Material Type"	Label	"Hybridization Name"	"Array Design REF"	"Scan Name"	"Arra [...]
+"Danio rerio Tuebingen wildtype_10h-1"	whole_organism	10	frozen_sample	Gastrula:Bud	wild_type	fertilization	"Danio rerio"	Tuebingen	hours	P-TABM-55	"Danio rerio Tuebingen wildtype_10h-1"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_10h-1"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_10h-1 biotin"	synthetic_DNA	biotin	Tue-10h-1	A-AFFY-38	Tue-10h-1	Tue-10h-1.CEL	Tue-10h-1.EXP	Tue-10h-1	Tue-10h-1.CHP	Zebrafish.cdf	Gastrula:Bud	10	h
+"Danio rerio Tuebingen wildtype_10h-2"	whole_organism	10	frozen_sample	Gastrula:Bud	wild_type	fertilization	"Danio rerio"	Tuebingen	hours	P-TABM-55	"Danio rerio Tuebingen wildtype_10h-2"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_10h-2"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_10h-2 biotin"	synthetic_DNA	biotin	Tue-10h-2	A-AFFY-38	Tue-10h-2	Tue-10h-2.CEL	Tue-10h-2.EXP	Tue-10h-2	Tue-10h-2.CHP	Zebrafish.cdf	Gastrula:Bud	10	h
+"Danio rerio Tuebingen wildtype_11.7h-1"	whole_organism	11.7	frozen_sample	"Segmentation:5-9 somites"	wild_type	fertilization	"Danio rerio"	Tuebingen	hours	P-TABM-55	"Danio rerio Tuebingen wildtype_11.7h-1"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_11.7h-1"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_11.7h-1 biotin"	synthetic_DNA	biotin	Tue-11.7h-1	A-AFFY-38	Tue-11.7h-1	Tue-11.7h-1.CEL	Tue-11.7h-1.EXP	Tue-11.7h-1	Tue-11.7h-1.CHP	Zebrafish.cdf	"Segmentation:5-9 somite [...]
+"Danio rerio Tuebingen wildtype_11.7h-2"	whole_organism	11.7	frozen_sample	"Segmentation:5-9 somites"	wild_type	fertilization	"Danio rerio"	Tuebingen	hours	P-TABM-55	"Danio rerio Tuebingen wildtype_11.7h-2"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_11.7h-2"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_11.7h-2 biotin"	synthetic_DNA	biotin	Tue-11.7h-2	A-AFFY-38	Tue-11.7h-2	Tue-11.7h-2.CEL	Tue-11.7h-2.EXP	Tue-11.7h-2	Tue-11.7h-2.CHP	Zebrafish.cdf	"Segmentation:5-9 somite [...]
+"Danio rerio Tuebingen wildtype_14d-1"	whole_organism	14	frozen_sample	"Larval:Days 14-20"	wild_type	fertilization	"Danio rerio"	Tuebingen	days	P-TABM-55	"Danio rerio Tuebingen wildtype_14d-1"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_14d-1"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_14d-1 biotin"	synthetic_DNA	biotin	Tue-14d-1	A-AFFY-38	Tue-14d-1	Tue-14d-1.CEL	Tue-14d-1.EXP	Tue-14d-1	Tue-14d-1.CHP	Zebrafish.cdf	"Larval:Days 14-20"	14	d
+"Danio rerio Tuebingen wildtype_14d_2"	whole_organism	14	frozen_sample	"Larval:Days 14-20"	wild_type	fertilization	"Danio rerio"	Tuebingen	days	P-TABM-55	"Danio rerio Tuebingen wildtype_14d_2"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_14d_2"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_14d_2 biotin"	synthetic_DNA	biotin	Tue-14d-2	A-AFFY-38	Tue-14d-2	Tue-14d-2.CEL	Tue-14d-2.EXP	Tue-14d-2	Tue-14d-2.CHP	Zebrafish.cdf	"Larval:Days 14-20"	14	d
+"Danio rerio Tuebingen wildtype_16h-1"	whole_organism	16	frozen_sample	"Segmentation:14-19 somites"	wild_type	fertilization	"Danio rerio"	Tuebingen	hours	P-TABM-55	"Danio rerio Tuebingen wildtype_16h-1"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_16h-1"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_16h-1 biotin"	synthetic_DNA	biotin	Tue-16h-1	A-AFFY-38	Tue-16h-1	Tue-16h-1.CEL	Tue-16h-1.EXP	Tue-16h-1	Tue-16h-1.CHP	Zebrafish.cdf	"Segmentation:14-19 somites"	16	h
+"Danio rerio Tuebingen wildtype_16h-2"	whole_organism	16	frozen_sample	"Segmentation:14-19 somites"	wild_type	fertilization	"Danio rerio"	Tuebingen	hours	P-TABM-55	"Danio rerio Tuebingen wildtype_16h-2"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_16h-2"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_16h-2 biotin"	synthetic_DNA	biotin	Tue-16h-2	A-AFFY-38	Tue-16h-2	Tue-16h-2.CEL	Tue-16h-2.EXP	Tue-16h-2	Tue-16h-2.CHP	Zebrafish.cdf	"Segmentation:14-19 somites"	16	h
+"Danio rerio Tuebingen wildtype_1c-1"	whole_organism	0.25	frozen_sample	Zygote:1-cell	wild_type	fertilization	"Danio rerio"	Tuebingen	hours	P-TABM-55	"Danio rerio Tuebingen wildtype_1c-1"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_1c-1"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_1c-1 biotin"	synthetic_DNA	biotin	Tue-1c-1	A-AFFY-38	Tue-1c-1	Tue-1c-1.CEL	Tue-1c-1.EXP	Tue-1c-1	Tue-1c-1.CHP	Zebrafish.cdf	Zygote:1-cell	0.25	h
+"Danio rerio Tuebingen wildtype_1c-2"	whole_organism	0.25	frozen_sample	Zygote:1-cell	wild_type	fertilization	"Danio rerio"	Tuebingen	hours	P-TABM-55	"Danio rerio Tuebingen wildtype_1c-2"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_1c-2"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_1c-2 biotin"	synthetic_DNA	biotin	Tue-1c-2	A-AFFY-38	Tue-1c-2	Tue-1c-2.CEL	Tue-1c-2.EXP	Tue-1c-2	Tue-1c-2.CHP	Zebrafish.cdf	Zygote:1-cell	0.25	h
+"Danio rerio Tuebingen wildtype_24h-1"	whole_organism	24	frozen_sample	Pharyngula:Prim-5	wild_type	fertilization	"Danio rerio"	Tuebingen	hours	P-TABM-55	"Danio rerio Tuebingen wildtype_24h-1"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_24h-1"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_24h-1 biotin"	synthetic_DNA	biotin	Tue-24h-1	A-AFFY-38	Tue-24h-1	Tue-24h-1.CEL	Tue-24h-1.EXP	Tue-24h-1	Tue-24h-1.CHP	Zebrafish.cdf	Pharyngula:Prim-5	24	h
+"Danio rerio Tuebingen wildtype_24h-2"	whole_organism	24	frozen_sample	Pharyngula:Prim-5	wild_type	fertilization	"Danio rerio"	Tuebingen	hours	P-TABM-55	"Danio rerio Tuebingen wildtype_24h-2"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_24h-2"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_24h-2 biotin"	synthetic_DNA	biotin	Tue-24h-2	A-AFFY-38	Tue-24h-2	Tue-24h-2.CEL	Tue-24h-2.EXP	Tue-24h-2	Tue-24h-2.CHP	Zebrafish.cdf	Pharyngula:Prim-5	24	h
+"Danio rerio Tuebingen wildtype_30d-1"	whole_organism	30	frozen_sample	"Juvenile:Days 30-44"	wild_type	fertilization	"Danio rerio"	Tuebingen	days	P-TABM-55	"Danio rerio Tuebingen wildtype_30d-1"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_30d-1"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_30d-1 biotin"	synthetic_DNA	biotin	Tue-30d-1	A-AFFY-38	Tue-30d-1	Tue-30d-1.CEL	Tue-30d-1.EXP	Tue-30d-1	Tue-30d-1.CHP	Zebrafish.cdf	"Juvenile:Days 30-44"	30	d
+"Danio rerio Tuebingen wildtype_30d-2"	whole_organism	30	frozen_sample	"Juvenile:Days 30-44"	wild_type	fertilization	"Danio rerio"	Tuebingen	days	P-TABM-55	"Danio rerio Tuebingen wildtype_30d-2"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_30d-2"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_30d-2 biotin"	synthetic_DNA	biotin	Tue-30d-2	A-AFFY-38	Tue-30d-2	Tue-30d-2.CEL	Tue-30d-2.EXP	Tue-30d-2	Tue-30d-2.CHP	Zebrafish.cdf	"Juvenile:Days 30-44"	30	d
+"Danio rerio Tuebingen wildtype_32h-1"	whole_organism	32	frozen_sample	Pharyngula:Prim-15	wild_type	fertilization	"Danio rerio"	Tuebingen	hours	P-TABM-55	"Danio rerio Tuebingen wildtype_32h-1"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_32h-1"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_32h-1 biotin"	synthetic_DNA	biotin	Tue-32h-1	A-AFFY-38	Tue-32h-1	Tue-32h-1.CEL	Tue-32h-1.EXP	Tue-32h-1	Tue-32h-1.CHP	Zebrafish.cdf	Pharyngula:Prim-15	32	h
+"Danio rerio Tuebingen wildtype_32h-2"	whole_organism	32	frozen_sample	Pharyngula:Prim-15	wild_type	fertilization	"Danio rerio"	Tuebingen	hours	P-TABM-55	"Danio rerio Tuebingen wildtype_32h-2"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_32h-2"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_32h-2 biotin"	synthetic_DNA	biotin	Tue-32h-2	A-AFFY-38	Tue-32h-2	Tue-32h-2.CEL	Tue-32h-2.EXP	Tue-32h-2	Tue-32h-2.CHP	Zebrafish.cdf	Pharyngula:Prim-15	32	h
+"Danio rerio Tuebingen wildtype_48h-1"	whole_organism	48	frozen_sample	Hatching:Long-pec	wild_type	fertilization	"Danio rerio"	Tuebingen	hours	P-TABM-55	"Danio rerio Tuebingen wildtype_48h-1"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_48h-1"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_48h-1 biotin"	synthetic_DNA	biotin	Tue-48h-1	A-AFFY-38	Tue-48h-1	Tue-48h-1.CEL	Tue-48h-1.EXP	Tue-48h-1	Tue-48h-1.CHP	Zebrafish.cdf	Hatching:Long-pec	48	h
+"Danio rerio Tuebingen wildtype_48h-2"	whole_organism	48	frozen_sample	Hatching:Long-pec	wild_type	fertilization	"Danio rerio"	Tuebingen	hours	P-TABM-55	"Danio rerio Tuebingen wildtype_48h-2"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_48h-2"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_48h-2 biotin"	synthetic_DNA	biotin	Tue-48h-2	A-AFFY-38	Tue-48h-2	Tue-48h-2.CEL	Tue-48h-2.EXP	Tue-48h-2	Tue-48h-2.CHP	Zebrafish.cdf	Hatching:Long-pec	48	h
+"Danio rerio Tuebingen wildtype_4d-1"	whole_organism	4	frozen_sample	"Larval:Day 4"	wild_type	fertilization	"Danio rerio"	Tuebingen	days	P-TABM-55	"Danio rerio Tuebingen wildtype_4d-1"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_4d-1"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_4d-1 biotin"	synthetic_DNA	biotin	Tue-4d-1	A-AFFY-38	Tue-4d-1	Tue-4d-1.CEL	Tue-4d-1.EXP	Tue-4d-1	Tue-4d-1.CHP	Zebrafish.cdf	"Larval:Day 4"	4	d
+"Danio rerio Tuebingen wildtype_4d-2"	whole_organism	4	frozen_sample	"Larval:Day 4"	wild_type	fertilization	"Danio rerio"	Tuebingen	days	P-TABM-55	"Danio rerio Tuebingen wildtype_4d-2"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_4d-2"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_4d-2 biotin"	synthetic_DNA	biotin	Tue-4d-2	A-AFFY-38	Tue-4d-2	Tue-4d-2.CEL	Tue-4d-2.EXP	Tue-4d-2	Tue-4d-2.CHP	Zebrafish.cdf	"Larval:Day 4"	4	d
+"Danio rerio Tuebingen wildtype_5d-1"	whole_organism	5	frozen_sample	"Larval:Day 5"	wild_type	fertilization	"Danio rerio"	Tuebingen	days	P-TABM-55	"Danio rerio Tuebingen wildtype_5d-1"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_5d-1"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_5d-1 biotin"	synthetic_DNA	biotin	Tue-5d-1	A-AFFY-38	Tue-5d-1	Tue-5d-1.CEL	Tue-5d-1.EXP	Tue-5d-1	Tue-5d-1.CHP	Zebrafish.cdf	"Larval:Day 5"	5	d
+"Danio rerio Tuebingen wildtype_5d-2"	whole_organism	5	frozen_sample	"Larval:Day 5"	wild_type	fertilization	"Danio rerio"	Tuebingen	days	P-TABM-55	"Danio rerio Tuebingen wildtype_5d-2"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_5d-2"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_5d-2 biotin"	synthetic_DNA	biotin	Tue-5d-2	A-AFFY-38	Tue-5d-2	Tue-5d-2.CEL	Tue-5d-2.EXP	Tue-5d-2	Tue-5d-2.CHP	Zebrafish.cdf	"Larval:Day 5"	5	d
+"Danio rerio Tuebingen wildtype_6h-1"	whole_organism	6	frozen_sample	Gastrula:Shield	wild_type	fertilization	"Danio rerio"	Tuebingen	hours	P-TABM-55	"Danio rerio Tuebingen wildtype_6h-1"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_6h-1"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_6h-1 biotin"	synthetic_DNA	biotin	Tue-6h-1	A-AFFY-38	Tue-6h-1	Tue-6h-1.CEL	Tue-6h-1.EXP	Tue-6h-1	Tue-6h-1.CHP	Zebrafish.cdf	Gastrula:Shield	6	h
+"Danio rerio Tuebingen wildtype_6h-2"	whole_organism	6	frozen_sample	Gastrula:Shield	wild_type	fertilization	"Danio rerio"	Tuebingen	hours	P-TABM-55	"Danio rerio Tuebingen wildtype_6h-2"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_6h-2"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_6h-2 biotin"	synthetic_DNA	biotin	Tue-6h-2	A-AFFY-38	Tue-6h-2	Tue-6h-2.CEL	Tue-6h-2.EXP	Tue-6h-2	Tue-6h-2.CHP	Zebrafish.cdf	Gastrula:Shield	6	h
+"Danio rerio Tuebingen wildtype_8h-1"	whole_organism	8	frozen_sample	Gastrula:75%-epiboly	wild_type	fertilization	"Danio rerio"	Tuebingen	hours	P-TABM-55	"Danio rerio Tuebingen wildtype_8h-1"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_8h-1"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_8h-1 biotin"	synthetic_DNA	biotin	Tue-8h-1	A-AFFY-38	Tue-8h-1	Tue-8h-1.CEL	Tue-8h-1.EXP	Tue-8h-1	Tue-8h-1.CHP	Zebrafish.cdf	Gastrula:75%-epiboly	8	h
+"Danio rerio Tuebingen wildtype_8h-2"	whole_organism	8	frozen_sample	Gastrula:75%-epiboly	wild_type	fertilization	"Danio rerio"	Tuebingen	hours	P-TABM-55	"Danio rerio Tuebingen wildtype_8h-2"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_8h-2"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_8h-2 biotin"	synthetic_DNA	biotin	Tue-8h-2	A-AFFY-38	Tue-8h-2	Tue-8h-2.CEL	Tue-8h-2.EXP	Tue-8h-2	Tue-8h-2.CHP	Zebrafish.cdf	Gastrula:75%-epiboly	8	h
+"Danio rerio Tuebingen wildtype_90d-1"	whole_organism	90	frozen_sample	Adult	wild_type	fertilization	"Danio rerio"	Tuebingen	days	P-TABM-55	"Danio rerio Tuebingen wildtype_90d-1"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_90d-1"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_90d-1 biotin"	synthetic_DNA	biotin	Tue-90d-1	A-AFFY-38	Tue-90d-1	Tue-90d-1.CEL	Tue-90d-1.EXP	Tue-90d-1	Tue-90d-1.CHP	Zebrafish.cdf	Adult	90	d
+"Danio rerio Tuebingen wildtype_90d-2"	whole_organism	90	frozen_sample	Adult	wild_type	fertilization	"Danio rerio"	Tuebingen	days	P-TABM-55	"Danio rerio Tuebingen wildtype_90d-2"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_90d-2"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_90d-2 biotin"	synthetic_DNA	biotin	Tue-90d-2	A-AFFY-38	Tue-90d-2	Tue-90d-2.CEL	Tue-90d-2.EXP	Tue-90d-2	Tue-90d-2.CHP	Zebrafish.cdf	Adult	90	d
+"Danio rerio Tuebingen wildtype_9h-1"	whole_organism	9	frozen_sample	Gastrula:90%-epiboly	wild_type	fertilization	"Danio rerio"	Tuebingen	hours	P-TABM-55	"Danio rerio Tuebingen wildtype_9h-1"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_9h-1"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_9h-1 biotin"	synthetic_DNA	biotin	Tue-9h-1	A-AFFY-38	Tue-9h-1	Tue-9h-1.CEL	Tue-9h-1.EXP	Tue-9h-1	Tue-9h-1.CHP	Zebrafish.cdf	Gastrula:90%-epiboly	9	h
+"Danio rerio Tuebingen wildtype_9h-2"	whole_organism	9	frozen_sample	Gastrula:90%-epiboly	wild_type	fertilization	"Danio rerio"	Tuebingen	hours	P-TABM-55	"Danio rerio Tuebingen wildtype_9h-2"	organism_part	P-TABM-56	"Danio rerio Tuebingen wildtype_9h-2"	total_RNA	P-AFFY-2	"Danio rerio Tuebingen wildtype_9h-2 biotin"	synthetic_DNA	biotin	Tue-9h-2	A-AFFY-38	Tue-9h-2	Tue-9h-2.CEL	Tue-9h-2.EXP	Tue-9h-2	Tue-9h-2.CHP	Zebrafish.cdf	Gastrula:90%-epiboly	9	h
diff --git a/examples/magetab/real/E-TABM-35.idf b/examples/magetab/real/E-TABM-35.idf
new file mode 100644
index 0000000..a3a5ba5
--- /dev/null
+++ b/examples/magetab/real/E-TABM-35.idf
@@ -0,0 +1,26 @@
+"Investigation Title"	"RML C. burnetii CGH analysis"								
+"Experimental Design"	strain_or_line_design	comparative_genome_hybridization_design							
+"Experimental Factor Name"	STRAINORLINE								
+"Experimental Factor Type"	strain_or_line								
+									
+"Person Last Name"	Beare								
+"Person First Name"	Paul								
+"Person Mid Initial"									
+"Person Affiliation"	"Rocky Mountain Laboratories, Laboratory of Intracellular Parasites, National Institute of Allergy and Infectious Diseases, National Institute of Health"								
+"Person Roles"	submitter								
+									
+"Public Release Date"	"2006-06-21"								
+									
+Comment[ArrayExpressSubmissionDate]									
+									
+"Publication Author List"	"P. A. Beare; J. E. Samuel; D. Howe; K. Virtaneva; S. F. Porcella; R. A. Heinzen"								
+"Publication Title"	"Genetic Diversity of the Q Fever agent Coxiella burnetii Assessed by Microarray-based Whole Genome Comparisons "								
+"Experiment Description"	"Analysis of genetic diversity of 25 Coxiella burnetii isolates with relative to the Nine Mile (RSA493) reference isolate."								
+									
+"Protocol Name"	P-TABM-61	P-TABM-62	P-TABM-63	P-TABM-64	P-TABM-65	P-TABM-66	P-TABM-67	P-TABM-68	P-TABM-69
+"Protocol Type"	grow	grow	nucleic_acid_extraction	nucleic_acid_extraction	nucleic_acid_extraction	nucleic_acid_extraction	nucleic_acid_extraction	labeling	bioassay_data_transformation
+"Protocol Description"	"C. burnetii isolates were propagated in embryonated henメs eggs for 8 days."	"C. burnetii isolates were propagated in African green monkey kidney (Vero) fibroblasts (CCL-81; American Type Culture Collection) grown in RPMI medium (Invitrogen) supplemented with 2% fetal bovine serum for 14 days."	"Organisms were purified from infected cells by Renografin or sucrose density gradient centrifugation. Total DNA was extracted using the UltraClean microbial DNA isolation k [...]
+"Protocol Parameters"									
+"Protocol Software"									
+									
+"SDRF File"	E-TABM-35_sdrf.txt								
diff --git a/examples/magetab/real/E-TABM-35_sdrf.txt b/examples/magetab/real/E-TABM-35_sdrf.txt
new file mode 100644
index 0000000..7e02eb6
--- /dev/null
+++ b/examples/magetab/real/E-TABM-35_sdrf.txt
@@ -0,0 +1,62 @@
+"Source Name"	"Material Type"	"Characteristics [Organism]"	"Characteristics [StrainOrLine]"	"Protocol REF"	"Sample Name"	"Material Type"	"Protocol REF"	"Extract Name"	"Material Type"	"Protocol REF"	"Labeled Extract Name"	"Material Type"	Label	"Hybridization Name"	"Array Design REF"	"Scan Name"	"Array Data File"	Comment[EXP]	"Protocol REF"	"Normalization Name"	"Derived Array Data File"	Comment[CDF]	"Factor Value [STRAINORLINE]"
+"Dugway 5G61-63"	whole_organism	"Coxiella burnetii"	"Dugway 5G61-63"	P-TABM-62	"Dugway 5G61-63 A"	organism_part	P-TABM-63	"Dugway 5G61-63 A"	genomic_DNA	P-TABM-68	"Dugway 5G61-63 A biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-Dugway 5G61-63 A"	A-AFFY-48	"PAB-1-Cburnetti-Dugway 5G61-63 A"	"PAB-1-Cburnetti-Dugway 5G61-63 A.CEL"	"PAB-1-Cburnetti-Dugway 5G61-63 A.EXP"	P-TABM-69	"PAB-1-Cburnetti-Dugway 5G61-63 A"	"PAB-1-Cburnetti-Dugway 5G61-63 A.CHP"	RML-chipa510998.CDF	"Dugway 5G61-63"
+"Dugway 5G61-63"	whole_organism	"Coxiella burnetii"	"Dugway 5G61-63"	P-TABM-62	"Dugway 5G61-63 B"	organism_part	P-TABM-63	"Dugway 5G61-63 B"	genomic_DNA	P-TABM-68	"Dugway 5G61-63 B biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-Dugway 5G61-63 B"	A-AFFY-48	"PAB-1-Cburnetti-Dugway 5G61-63 B"	"PAB-1-Cburnetti-Dugway 5G61-63 B.CEL"	"PAB-1-Cburnetti-Dugway 5G61-63 B.EXP"	P-TABM-69	"PAB-1-Cburnetti-Dugway 5G61-63 B"	"PAB-1-Cburnetti-Dugway 5G61-63 B.CHP"	RML-chipa510998.CDF	"Dugway 5G61-63"
+"Dugway 7E9-12"	whole_organism	"Coxiella burnetii"	"Dugway 7E9-12"	P-TABM-61	"Dugway 7E9-12 A"	organism_part	P-TABM-64	"Dugway 7E9-12 A"	genomic_DNA	P-TABM-68	"Dugway 7E9-12 A biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-Dugway 7E9-12 A"	A-AFFY-48	"PAB-1-Cburnetti-Dugway 7E9-12 A"	"PAB-1-Cburnetti-Dugway 7E9-12 A.CEL"	"PAB-1-Cburnetti-Dugway 7E9-12 A.EXP"	P-TABM-69	"PAB-1-Cburnetti-Dugway 7E9-12 A"	"PAB-1-Cburnetti-Dugway 7E9-12 A.CHP"	RML-chipa510998.CDF	"Dugway 7E9-12"
+"Dugway 7E9-12"	whole_organism	"Coxiella burnetii"	"Dugway 7E9-12"	P-TABM-61	"Dugway 7E9-12 B"	organism_part	P-TABM-64	"Dugway 7E9-12 B"	genomic_DNA	P-TABM-68	"Dugway 7E9-12 B biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-Dugway 7E9-12 B"	A-AFFY-48	"PAB-1-Cburnetti-Dugway 7E9-12 B"	"PAB-1-Cburnetti-Dugway 7E9-12 B.CEL"	"PAB-1-Cburnetti-Dugway 7E9-12 B.EXP"	P-TABM-69	"PAB-1-Cburnetti-Dugway 7E9-12 B"	"PAB-1-Cburnetti-Dugway 7E9-12 B.CHP"	RML-chipa510998.CDF	"Dugway 7E9-12"
+African	whole_organism	"Coxiella burnetii"	African	P-TABM-61	"African A"	organism_part	P-TABM-64	"African A"	genomic_DNA	P-TABM-68	"African A biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-African A"	A-AFFY-48	"PAB-1-Cburnetti-African A"	"PAB-1-Cburnetti-African A.CEL"	"PAB-1-Cburnetti-African A.EXP"	P-TABM-69	"PAB-1-Cburnetti-African A"	"PAB-1-Cburnetti-African A.CHP"	RML-chipa510998.CDF	African
+African	whole_organism	"Coxiella burnetii"	African	P-TABM-61	"African B"	organism_part	P-TABM-64	"African B"	genomic_DNA	P-TABM-68	"African B biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-African B"	A-AFFY-48	"PAB-1-Cburnetti-African B"	"PAB-1-Cburnetti-African B.CEL"	"PAB-1-Cburnetti-African B.EXP"	P-TABM-69	"PAB-1-Cburnetti-African B"	"PAB-1-Cburnetti-African B.CHP"	RML-chipa510998.CDF	African
+African	whole_organism	"Coxiella burnetii"	African	P-TABM-61	"African C"	organism_part	P-TABM-64	"African C"	genomic_DNA	P-TABM-68	"African C biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-African C"	A-AFFY-48	"PAB-1-Cburnetti-African C"	"PAB-1-Cburnetti-African C.CEL"	"PAB-1-Cburnetti-African C.EXP"	P-TABM-69	"PAB-1-Cburnetti-African C"	"PAB-1-Cburnetti-African C.CHP"	RML-chipa510998.CDF	African
+Australia	whole_organism	"Coxiella burnetii"	Australian	P-TABM-62	"Australian A"	organism_part	P-TABM-63	"Australian A"	genomic_DNA	P-TABM-68	"Australian A biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-Australian A"	A-AFFY-48	"PAB-1-Cburnetti-Australian A"	"PAB-1-Cburnetti-Australian A.CEL"	"PAB-1-Cburnetti-Australian A.EXP"	P-TABM-69	"PAB-1-Cburnetti-Australian A"	"PAB-1-Cburnetti-Australian A.CHP"	RML-chipa510998.CDF	"Australian "
+Australia	whole_organism	"Coxiella burnetii"	Australian	P-TABM-62	"Australian B"	organism_part	P-TABM-63	"Australian B"	genomic_DNA	P-TABM-68	"Australian B biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-Australian B"	A-AFFY-48	"PAB-1-Cburnetti-Australian B"	"PAB-1-Cburnetti-Australian B.CEL"	"PAB-1-Cburnetti-Australian B.EXP"	P-TABM-69	"PAB-1-Cburnetti-Australian B"	"PAB-1-Cburnetti-Australian B.CHP"	RML-chipa510998.CDF	"Australian "
+BDT	whole_organism	"Coxiella burnetii"	BDT	P-TABM-62	"BDT A"	organism_part	P-TABM-66	"BDT A"	genomic_DNA	P-TABM-68	"BDT A biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-BDT A"	A-AFFY-48	"PAB-1-Cburnetti-BDT A"	"PAB-1-Cburnetti-BDT A.CEL"	"PAB-1-Cburnetti-BDT A.EXP"	P-TABM-69	"PAB-1-Cburnetti-BDT A"	"PAB-1-Cburnetti-BDT A.CHP"	RML-chipa510998.CDF	BDT
+BDT	whole_organism	"Coxiella burnetii"	BDT	P-TABM-62	"BDT B"	organism_part	P-TABM-66	"BDT B"	genomic_DNA	P-TABM-68	"BDT B biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-BDT B"	A-AFFY-48	"PAB-1-Cburnetti-BDT B"	"PAB-1-Cburnetti-BDT B.CEL"	"PAB-1-Cburnetti-BDT B.EXP"	P-TABM-69	"PAB-1-Cburnetti-BDT B"	"PAB-1-Cburnetti-BDT B.CHP"	RML-chipa510998.CDF	BDT
+"California 16"	whole_organism	"Coxiella burnetii"	"California 16"	P-TABM-61	"California 16"	organism_part	P-TABM-64	"California 16"	genomic_DNA	P-TABM-68	"California 16 biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-California 16"	A-AFFY-48	"PAB-1-Cburnetti-California 16"	"PAB-1-Cburnetti-California 16.CEL"	"PAB-1-Cburnetti-California 16.EXP"	P-TABM-69	"PAB-1-Cburnetti-California 16"	"PAB-1-Cburnetti-California 16.CHP"	RML-chipa510998.CDF	"California 16"
+"California 33"	whole_organism	"Coxiella burnetii"	"California 33"	P-TABM-61	"California 33"	organism_part	P-TABM-64	"California 33"	genomic_DNA	P-TABM-68	"California 33 biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-California 33"	A-AFFY-48	"PAB-1-Cburnetti-California 33"	"PAB-1-Cburnetti-California 33.CEL"	"PAB-1-Cburnetti-California 33.EXP"	P-TABM-69	"PAB-1-Cburnetti-California 33"	"PAB-1-Cburnetti-California 33.CHP"	RML-chipa510998.CDF	"California 33"
+Crazy	whole_organism	"Coxiella burnetii"	Crazy	P-TABM-62	"Crazy A"	organism_part	P-TABM-63	"Crazy A"	genomic_DNA	P-TABM-68	"Crazy A biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-Crazy A"	A-AFFY-48	"PAB-1-Cburnetti-Crazy A"	"PAB-1-Cburnetti-Crazy A.CEL"	"PAB-1-Cburnetti-Crazy A.EXP"	P-TABM-69	"PAB-1-Cburnetti-Crazy A"	"PAB-1-Cburnetti-Crazy A.CHP"	RML-chipa510998.CDF	Crazy
+Crazy	whole_organism	"Coxiella burnetii"	Crazy	P-TABM-62	"Crazy B"	organism_part	P-TABM-63	"Crazy B"	genomic_DNA	P-TABM-68	"Crazy B biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-Crazy B"	A-AFFY-48	"PAB-1-Cburnetti-Crazy B"	"PAB-1-Cburnetti-Crazy B.CEL"	"PAB-1-Cburnetti-Crazy B.EXP"	P-TABM-69	"PAB-1-Cburnetti-Crazy B"	"PAB-1-Cburnetti-Crazy B.CHP"	RML-chipa510998.CDF	Crazy
+D	whole_organism	"Coxiella burnetii"	D	P-TABM-61	"D A"	organism_part	P-TABM-64	"D A"	genomic_DNA	P-TABM-68	"D A biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-D A"	A-AFFY-48	"PAB-1-Cburnetti-D A"	"PAB-1-Cburnetti-D A.CEL"	"PAB-1-Cburnetti-D A.EXP"	P-TABM-69	"PAB-1-Cburnetti-D A"	"PAB-1-Cburnetti-D A.CHP"	RML-chipa510998.CDF	D
+D	whole_organism	"Coxiella burnetii"	D	P-TABM-61	"D B"	organism_part	P-TABM-64	"D B"	genomic_DNA	P-TABM-68	"D B biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-D B"	A-AFFY-48	"PAB-1-Cburnetti-D B"	"PAB-1-Cburnetti-D B.CEL"	"PAB-1-Cburnetti-D B.EXP"	P-TABM-69	"PAB-1-Cburnetti-D B"	"PAB-1-Cburnetti-D B.CHP"	RML-chipa510998.CDF	D
+"El Tayeb"	whole_organism	"Coxiella burnetii"	"El Tayeb"	P-TABM-61	"El Tayeb A"	organism_part	P-TABM-64	"El Tayeb A"	genomic_DNA	P-TABM-68	"El Tayeb A biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-El Tayeb A"	A-AFFY-48	"PAB-1-Cburnetti-El Tayeb A"	"PAB-1-Cburnetti-El Tayeb A.CEL"	"PAB-1-Cburnetti-El Tayeb A.EXP"	P-TABM-69	"PAB-1-Cburnetti-El Tayeb A"	"PAB-1-Cburnetti-El Tayeb A.CHP"	RML-chipa510998.CDF	"El Tayeb"
+"El Tayeb"	whole_organism	"Coxiella burnetii"	"El Tayeb"	P-TABM-61	"El Tayeb B"	organism_part	P-TABM-64	"El Tayeb B"	genomic_DNA	P-TABM-68	"El Tayeb B biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-El Tayeb B"	A-AFFY-48	"PAB-1-Cburnetti-El Tayeb B"	"PAB-1-Cburnetti-El Tayeb B.CEL"	"PAB-1-Cburnetti-El Tayeb B.EXP"	P-TABM-69	"PAB-1-Cburnetti-El Tayeb B"	"PAB-1-Cburnetti-El Tayeb B.CHP"	RML-chipa510998.CDF	"El Tayeb"
+G	whole_organism	"Coxiella burnetii"	G	P-TABM-62	"G A"	organism_part	P-TABM-63	"G A"	genomic_DNA	P-TABM-68	"G A biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-G A"	A-AFFY-48	"PAB-1-Cburnetti-G A"	"PAB-1-Cburnetti-G A.CEL"	"PAB-1-Cburnetti-G A.EXP"	P-TABM-69	"PAB-1-Cburnetti-G A"	"PAB-1-Cburnetti-G A.CHP"	RML-chipa510998.CDF	G
+G	whole_organism	"Coxiella burnetii"	G	P-TABM-62	"G B"	organism_part	P-TABM-63	"G B"	genomic_DNA	P-TABM-68	"G B biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-G B"	A-AFFY-48	"PAB-1-Cburnetti-G B"	"PAB-1-Cburnetti-G B.CEL"	"PAB-1-Cburnetti-G B.EXP"	P-TABM-69	"PAB-1-Cburnetti-G B"	"PAB-1-Cburnetti-G B.CHP"	RML-chipa510998.CDF	G
+G	whole_organism	"Coxiella burnetii"	G	P-TABM-61	"G C"	organism_part	P-TABM-65	"G C"	genomic_DNA	P-TABM-68	"G C biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-G C"	A-AFFY-48	"PAB-1-Cburnetti-G C"	"PAB-1-Cburnetti-G C.CEL"	"PAB-1-Cburnetti-G C.EXP"	P-TABM-69	"PAB-1-Cburnetti-G C"	"PAB-1-Cburnetti-G C.CHP"	RML-chipa510998.CDF	G
+G	whole_organism	"Coxiella burnetii"	G	P-TABM-61	"G D"	organism_part	P-TABM-66	"G D"	genomic_DNA	P-TABM-68	"G D biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-G D"	A-AFFY-48	"PAB-1-Cburnetti-G D"	"PAB-1-Cburnetti-G D.CEL"	"PAB-1-Cburnetti-G D.EXP"	P-TABM-69	"PAB-1-Cburnetti-G D"	"PAB-1-Cburnetti-G D.CHP"	RML-chipa510998.CDF	G
+Giroud	whole_organism	"Coxiella burnetii"	Giroud	P-TABM-61	"Giroud A"	organism_part	P-TABM-64	"Giroud A"	genomic_DNA	P-TABM-68	"Giroud A biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-Giroud A"	A-AFFY-48	"PAB-1-Cburnetti-Giroud A"	"PAB-1-Cburnetti-Giroud A.CEL"	"PAB-1-Cburnetti-Giroud A.EXP"	P-TABM-69	"PAB-1-Cburnetti-Giroud A"	"PAB-1-Cburnetti-Giroud A.CHP"	RML-chipa510998.CDF	Giroud
+Giroud	whole_organism	"Coxiella burnetii"	Giroud	P-TABM-61	"Giroud B"	organism_part	P-TABM-64	"Giroud B"	genomic_DNA	P-TABM-68	"Giroud B biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-Giroud B"	A-AFFY-48	"PAB-1-Cburnetti-Giroud B"	"PAB-1-Cburnetti-Giroud B.CEL"	"PAB-1-Cburnetti-Giroud B.EXP"	P-TABM-69	"PAB-1-Cburnetti-Giroud B"	"PAB-1-Cburnetti-Giroud B.CHP"	RML-chipa510998.CDF	Giroud
+He	whole_organism	"Coxiella burnetii"	He	P-TABM-61	"He A"	organism_part	P-TABM-64	"He A"	genomic_DNA	P-TABM-68	"He A biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-He A"	A-AFFY-48	"PAB-1-Cburnetti-He A"	"PAB-1-Cburnetti-He A.CEL"	"PAB-1-Cburnetti-He A.EXP"	P-TABM-69	"PAB-1-Cburnetti-He A"	"PAB-1-Cburnetti-He A.CHP"	RML-chipa510998.CDF	"He "
+He	whole_organism	"Coxiella burnetii"	He	P-TABM-61	"He B"	organism_part	P-TABM-64	"He B"	genomic_DNA	P-TABM-68	"He B biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-He B"	A-AFFY-48	"PAB-1-Cburnetti-He B"	"PAB-1-Cburnetti-He B.CEL"	"PAB-1-Cburnetti-He B.EXP"	P-TABM-69	"PAB-1-Cburnetti-He B"	"PAB-1-Cburnetti-He B.CHP"	RML-chipa510998.CDF	"He "
+Idaho	whole_organism	"Coxiella burnetii"	Idaho	P-TABM-62	"Idaho A"	organism_part	P-TABM-63	"Idaho A"	genomic_DNA	P-TABM-68	"Idaho A biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-Idaho A"	A-AFFY-48	"PAB-1-Cburnetti-Idaho A"	"PAB-1-Cburnetti-Idaho A.CEL"	"PAB-1-Cburnetti-Idaho A.EXP"	P-TABM-69	"PAB-1-Cburnetti-Idaho A"	"PAB-1-Cburnetti-Idaho A.CHP"	RML-chipa510998.CDF	Idaho
+Idaho	whole_organism	"Coxiella burnetii"	Idaho	P-TABM-62	"Idaho B"	organism_part	P-TABM-63	"Idaho B"	genomic_DNA	P-TABM-68	"Idaho B biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-Idaho B"	A-AFFY-48	"PAB-1-Cburnetti-Idaho B"	"PAB-1-Cburnetti-Idaho B.CEL"	"PAB-1-Cburnetti-Idaho B.EXP"	P-TABM-69	"PAB-1-Cburnetti-Idaho B"	"PAB-1-Cburnetti-Idaho B.CHP"	RML-chipa510998.CDF	Idaho
+K	whole_organism	"Coxiella burnetii"	K	P-TABM-61	"K A"	organism_part	P-TABM-64	"K A"	genomic_DNA	P-TABM-68	"K A biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-K A"	A-AFFY-48	"PAB-1-Cburnetti-K A"	"PAB-1-Cburnetti-K A.CEL"	"PAB-1-Cburnetti-K A.EXP"	P-TABM-69	"PAB-1-Cburnetti-K A"	"PAB-1-Cburnetti-K A.CHP"	RML-chipa510998.CDF	K
+K	whole_organism	"Coxiella burnetii"	K	P-TABM-61	"K B"	organism_part	P-TABM-64	"K B"	genomic_DNA	P-TABM-68	"K B biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-K B"	A-AFFY-48	"PAB-1-Cburnetti-K B"	"PAB-1-Cburnetti-K B.CEL"	"PAB-1-Cburnetti-K B.EXP"	P-TABM-69	"PAB-1-Cburnetti-K B"	"PAB-1-Cburnetti-K B.CHP"	RML-chipa510998.CDF	K
+K	whole_organism	"Coxiella burnetii"	K	P-TABM-61	"K C"	organism_part	P-TABM-64	"K C"	genomic_DNA	P-TABM-68	"K C biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-K C"	A-AFFY-48	"PAB-1-Cburnetti-K C"	"PAB-1-Cburnetti-K C.CEL"	"PAB-1-Cburnetti-K C.EXP"	P-TABM-69	"PAB-1-Cburnetti-K C"	"PAB-1-Cburnetti-K C.CHP"	RML-chipa510998.CDF	K
+KO	whole_organism	"Coxiella burnetii"	KO	P-TABM-62	"KO A"	organism_part	P-TABM-63	"KO A"	genomic_DNA	P-TABM-68	"KO A biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-KO A"	A-AFFY-48	"PAB-1-Cburnetti-KO A"	"PAB-1-Cburnetti-KO A.CEL"	"PAB-1-Cburnetti-KO A.EXP"	P-TABM-69	"PAB-1-Cburnetti-KO A"	"PAB-1-Cburnetti-KO A.CHP"	RML-chipa510998.CDF	KO
+KO	whole_organism	"Coxiella burnetii"	KO	P-TABM-62	"KO B"	organism_part	P-TABM-63	"KO B"	genomic_DNA	P-TABM-68	"KO B biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-KO B"	A-AFFY-48	"PAB-1-Cburnetti-KO B"	"PAB-1-Cburnetti-KO B.CEL"	"PAB-1-Cburnetti-KO B.EXP"	P-TABM-69	"PAB-1-Cburnetti-KO B"	"PAB-1-Cburnetti-KO B.CHP"	RML-chipa510998.CDF	KO
+KO	whole_organism	"Coxiella burnetii"	KO	P-TABM-62	"KO C"	organism_part	P-TABM-63	"KO C"	genomic_DNA	P-TABM-68	"KO C biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-KO C"	A-AFFY-48	"PAB-1-Cburnetti-KO C"	"PAB-1-Cburnetti-KO C.CEL"	"PAB-1-Cburnetti-KO C.EXP"	P-TABM-69	"PAB-1-Cburnetti-KO C"	"PAB-1-Cburnetti-KO C.CHP"	RML-chipa510998.CDF	KO
+"Le B"	whole_organism	"Coxiella burnetii"	"Le B"	P-TABM-62	"Le B A"	organism_part	P-TABM-63	"Le B A"	genomic_DNA	P-TABM-68	"Le B A biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-LeB A"	A-AFFY-48	"PAB-1-Cburnetti-LeB A"	"PAB-1-Cburnetti-LeB A.CEL"	"PAB-1-Cburnetti-LeB A.EXP"	P-TABM-69	"PAB-1-Cburnetti-LeB A"	"PAB-1-Cburnetti-LeB A.CHP"	RML-chipa510998.CDF	"Le B"
+"Le B"	whole_organism	"Coxiella burnetii"	"Le B"	P-TABM-62	"Le B B"	organism_part	P-TABM-63	"Le B B"	genomic_DNA	P-TABM-68	"Le B B biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-LeB B"	A-AFFY-48	"PAB-1-Cburnetti-LeB B"	"PAB-1-Cburnetti-LeB B.CEL"	"PAB-1-Cburnetti-LeB B.EXP"	P-TABM-69	"PAB-1-Cburnetti-LeB B"	"PAB-1-Cburnetti-LeB B.CHP"	RML-chipa510998.CDF	"Le B"
+MSU	whole_organism	"Coxiella burnetii"	MSU	P-TABM-62	"MSU A"	organism_part	P-TABM-63	"MSU A"	genomic_DNA	P-TABM-68	"MSU A biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-MSU A"	A-AFFY-48	"PAB-1-Cburnetti-MSU A"	"PAB-1-Cburnetti-MSU A.CEL"	"PAB-1-Cburnetti-MSU A.EXP"	P-TABM-69	"PAB-1-Cburnetti-MSU A"	"PAB-1-Cburnetti-MSU A.CHP"	RML-chipa510998.CDF	MSU
+MSU	whole_organism	"Coxiella burnetii"	MSU	P-TABM-62	"MSU B"	organism_part	P-TABM-63	"MSU B"	genomic_DNA	P-TABM-68	"MSU B biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-MSU B"	A-AFFY-48	"PAB-1-Cburnetti-MSU B"	"PAB-1-Cburnetti-MSU B.CEL"	"PAB-1-Cburnetti-MSU B.EXP"	P-TABM-69	"PAB-1-Cburnetti-MSU B"	"PAB-1-Cburnetti-MSU B.CHP"	RML-chipa510998.CDF	MSU
+MSU	whole_organism	"Coxiella burnetii"	MSU	P-TABM-62	"MSU C"	organism_part	P-TABM-63	"MSU C"	genomic_DNA	P-TABM-68	"MSU C biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-MSU C"	A-AFFY-48	"PAB-1-Cburnetti-MSU C"	"PAB-1-Cburnetti-MSU C.CEL"	"PAB-1-Cburnetti-MSU C.EXP"	P-TABM-69	"PAB-1-Cburnetti-MSU C"	"PAB-1-Cburnetti-MSU C.CHP"	RML-chipa510998.CDF	MSU
+NMI	whole_organism	"Coxiella burnetii"	NMI	P-TABM-62	"NMI A"	organism_part	P-TABM-63	"NMI A"	genomic_DNA	P-TABM-68	"NMI A biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-NMI A"	A-AFFY-48	"PAB-1-Cburnetti-NMI A"	"PAB-1-Cburnetti-NMI A.CEL"	"PAB-1-Cburnetti-NMI A.EXP"	P-TABM-69	"PAB-1-Cburnetti-NMI A"	"PAB-1-Cburnetti-NMI A.CHP"	RML-chipa510998.CDF	NMI
+NMI	whole_organism	"Coxiella burnetii"	NMI	P-TABM-62	"NMI B"	organism_part	P-TABM-63	"NMI B"	genomic_DNA	P-TABM-68	"NMI B biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-NMI B"	A-AFFY-48	"PAB-1-Cburnetti-NMI B"	"PAB-1-Cburnetti-NMI B.CEL"	"PAB-1-Cburnetti-NMI B.EXP"	P-TABM-69	"PAB-1-Cburnetti-NMI B"	"PAB-1-Cburnetti-NMI B.CHP"	RML-chipa510998.CDF	NMI
+NMI	whole_organism	"Coxiella burnetii"	NMI	P-TABM-62	"NMI C"	organism_part	P-TABM-63	"NMI C"	genomic_DNA	P-TABM-68	"NMI C biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-NMI C"	A-AFFY-48	"PAB-1-Cburnetti-NMI C"	"PAB-1-Cburnetti-NMI C.CEL"	"PAB-1-Cburnetti-NMI C.EXP"	P-TABM-69	"PAB-1-Cburnetti-NMI C"	"PAB-1-Cburnetti-NMI C.CHP"	RML-chipa510998.CDF	NMI
+NMI	whole_organism	"Coxiella burnetii"	NMI	P-TABM-62	"NMI D"	organism_part	P-TABM-63	"NMI D"	genomic_DNA	P-TABM-68	"NMI D biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-NMI D"	A-AFFY-48	"PAB-1-Cburnetti-NMI D"	"PAB-1-Cburnetti-NMI D.CEL"	"PAB-1-Cburnetti-NMI D.EXP"	P-TABM-69	"PAB-1-Cburnetti-NMI D"	"PAB-1-Cburnetti-NMI D.CHP"	RML-chipa510998.CDF	NMI
+NMII	whole_organism	"Coxiella burnetii"	NMII	P-TABM-62	"NMII A"	organism_part	P-TABM-63	"NMII A"	genomic_DNA	P-TABM-68	"NMII A biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-NMII A"	A-AFFY-48	"PAB-1-Cburnetti-NMII A"	"PAB-1-Cburnetti-NMII A.CEL"	"PAB-1-Cburnetti-NMII A.EXP"	P-TABM-69	"PAB-1-Cburnetti-NMII A"	"PAB-1-Cburnetti-NMII A.CHP"	RML-chipa510998.CDF	NMII
+NMII	whole_organism	"Coxiella burnetii"	NMII	P-TABM-62	"NMII B"	organism_part	P-TABM-67	"NMII B"	genomic_DNA	P-TABM-68	"NMII B biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-NMII B"	A-AFFY-48	"PAB-1-Cburnetti-NMII B"	"PAB-1-Cburnetti-NMII B.CEL"	"PAB-1-Cburnetti-NMII B.EXP"	P-TABM-69	"PAB-1-Cburnetti-NMII B"	"PAB-1-Cburnetti-NMII B.CHP"	RML-chipa510998.CDF	NMII
+Ohio	whole_organism	"Coxiella burnetii"	Ohio	P-TABM-61	"Ohio A"	organism_part	P-TABM-64	"Ohio A"	genomic_DNA	P-TABM-68	"Ohio A biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-Ohio A"	A-AFFY-48	"PAB-1-Cburnetti-Ohio A"	"PAB-1-Cburnetti-Ohio A.CEL"	"PAB-1-Cburnetti-Ohio A.EXP"	P-TABM-69	"PAB-1-Cburnetti-Ohio A"	"PAB-1-Cburnetti-Ohio A.CHP"	RML-chipa510998.CDF	Ohio
+Ohio	whole_organism	"Coxiella burnetii"	Ohio	P-TABM-61	"Ohio B"	organism_part	P-TABM-64	"Ohio B"	genomic_DNA	P-TABM-68	"Ohio B biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-Ohio B"	A-AFFY-48	"PAB-1-Cburnetti-Ohio B"	"PAB-1-Cburnetti-Ohio B.CEL"	"PAB-1-Cburnetti-Ohio B.EXP"	P-TABM-69	"PAB-1-Cburnetti-Ohio B"	"PAB-1-Cburnetti-Ohio B.CHP"	RML-chipa510998.CDF	Ohio
+P	whole_organism	"Coxiella burnetii"	P	P-TABM-61	"P A"	organism_part	P-TABM-64	"P A"	genomic_DNA	P-TABM-68	"P A biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-P A"	A-AFFY-48	"PAB-1-Cburnetti-P A"	"PAB-1-Cburnetti-P A.CEL"	"PAB-1-Cburnetti-P A.EXP"	P-TABM-69	"PAB-1-Cburnetti-P A"	"PAB-1-Cburnetti-P A.CHP"	RML-chipa510998.CDF	P
+P	whole_organism	"Coxiella burnetii"	P	P-TABM-61	"P B"	organism_part	P-TABM-64	"P B"	genomic_DNA	P-TABM-68	"P B biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-P B"	A-AFFY-48	"PAB-1-Cburnetti-P B"	"PAB-1-Cburnetti-P B.CEL"	"PAB-1-Cburnetti-P B.EXP"	P-TABM-69	"PAB-1-Cburnetti-P B"	"PAB-1-Cburnetti-P B.CHP"	RML-chipa510998.CDF	P
+P	whole_organism	"Coxiella burnetii"	P	P-TABM-61	"P C"	organism_part	P-TABM-64	"P C"	genomic_DNA	P-TABM-68	"P C biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-P C"	A-AFFY-48	"PAB-1-Cburnetti-P C"	"PAB-1-Cburnetti-P C.CEL"	"PAB-1-Cburnetti-P C.EXP"	P-TABM-69	"PAB-1-Cburnetti-P C"	"PAB-1-Cburnetti-P C.CHP"	RML-chipa510998.CDF	P
+Panama	whole_organism	"Coxiella burnetii"	Panama	P-TABM-61	"Panama A"	organism_part	P-TABM-64	"Panama A"	genomic_DNA	P-TABM-68	"Panama A biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-Panama A"	A-AFFY-48	"PAB-1-Cburnetti-Panama A"	"PAB-1-Cburnetti-Panama A.CEL"	"PAB-1-Cburnetti-Panama A.EXP"	P-TABM-69	"PAB-1-Cburnetti-Panama A"	"PAB-1-Cburnetti-Panama A.CHP"	RML-chipa510998.CDF	Panama
+Panama	whole_organism	"Coxiella burnetii"	Panama	P-TABM-61	"Panama B"	organism_part	P-TABM-64	"Panama B"	genomic_DNA	P-TABM-68	"Panama B biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-Panama B"	A-AFFY-48	"PAB-1-Cburnetti-Panama B"	"PAB-1-Cburnetti-Panama B.CEL"	"PAB-1-Cburnetti-Panama B.EXP"	P-TABM-69	"PAB-1-Cburnetti-Panama B"	"PAB-1-Cburnetti-Panama B.CHP"	RML-chipa510998.CDF	Panama
+Q321	whole_organism	"Coxiella burnetii"	Q321	P-TABM-62	"Q321 A"	organism_part	P-TABM-63	"Q321 A"	genomic_DNA	P-TABM-68	"Q321 A biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-Q321 A"	A-AFFY-48	"PAB-1-Cburnetti-Q321 A"	"PAB-1-Cburnetti-Q321 A.CEL"	"PAB-1-Cburnetti-Q321 A.EXP"	P-TABM-69	"PAB-1-Cburnetti-Q321 A"	"PAB-1-Cburnetti-Q321 A.CHP"	RML-chipa510998.CDF	Q321
+Q321	whole_organism	"Coxiella burnetii"	Q321	P-TABM-62	"Q321 B"	organism_part	P-TABM-63	"Q321 B"	genomic_DNA	P-TABM-68	"Q321 B biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-Q321 B"	A-AFFY-48	"PAB-1-Cburnetti-Q321 B"	"PAB-1-Cburnetti-Q321 B.CEL"	"PAB-1-Cburnetti-Q321 B.EXP"	P-TABM-69	"PAB-1-Cburnetti-Q321 B"	"PAB-1-Cburnetti-Q321 B.CHP"	RML-chipa510998.CDF	Q321
+Q321	whole_organism	"Coxiella burnetii"	Q321	P-TABM-62	"Q321 C"	organism_part	P-TABM-63	"Q321 C"	genomic_DNA	P-TABM-68	"Q321 C biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-Q321 C"	A-AFFY-48	"PAB-1-Cburnetti-Q321 C"	"PAB-1-Cburnetti-Q321 C.CEL"	"PAB-1-Cburnetti-Q321 C.EXP"	P-TABM-69	"PAB-1-Cburnetti-Q321 C"	"PAB-1-Cburnetti-Q321 C.CHP"	RML-chipa510998.CDF	Q321
+S	whole_organism	"Coxiella burnetii"	S	P-TABM-62	"S A"	organism_part	P-TABM-63	"S A"	genomic_DNA	P-TABM-68	"S A biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-S A"	A-AFFY-48	"PAB-1-Cburnetti-S A"	"PAB-1-Cburnetti-S A.CEL"	"PAB-1-Cburnetti-S A.EXP"	P-TABM-69	"PAB-1-Cburnetti-S A"	"PAB-1-Cburnetti-S A.CHP"	RML-chipa510998.CDF	S
+S	whole_organism	"Coxiella burnetii"	S	P-TABM-62	"S B"	organism_part	P-TABM-63	"S B"	genomic_DNA	P-TABM-68	"S B biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-S B"	A-AFFY-48	"PAB-1-Cburnetti-S B"	"PAB-1-Cburnetti-S B.CEL"	"PAB-1-Cburnetti-S B.EXP"	P-TABM-69	"PAB-1-Cburnetti-S B"	"PAB-1-Cburnetti-S B.CHP"	RML-chipa510998.CDF	S
+S	whole_organism	"Coxiella burnetii"	S	P-TABM-62	"S C"	organism_part	P-TABM-63	"S C"	genomic_DNA	P-TABM-68	"S C biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-S C"	A-AFFY-48	"PAB-1-Cburnetti-S C"	"PAB-1-Cburnetti-S C.CEL"	"PAB-1-Cburnetti-S C.EXP"	P-TABM-69	"PAB-1-Cburnetti-S C"	"PAB-1-Cburnetti-S C.CHP"	RML-chipa510998.CDF	S
+Turkey	whole_organism	"Coxiella burnetii"	Turkey	P-TABM-61	"Turkey A"	organism_part	P-TABM-64	"Turkey A"	genomic_DNA	P-TABM-68	"Turkey A biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-Turkey A"	A-AFFY-48	"PAB-1-Cburnetti-Turkey A"	"PAB-1-Cburnetti-Turkey A.CEL"	"PAB-1-Cburnetti-Turkey A.EXP"	P-TABM-69	"PAB-1-Cburnetti-Turkey A"	"PAB-1-Cburnetti-Turkey A.CHP"	RML-chipa510998.CDF	Turkey
+Turkey	whole_organism	"Coxiella burnetii"	Turkey	P-TABM-61	"Turkey B"	organism_part	P-TABM-64	"Turkey B"	genomic_DNA	P-TABM-68	"Turkey B biotin"	synthetic_DNA	biotin	"PAB-1-Cburnetti-Turkey B"	A-AFFY-48	"PAB-1-Cburnetti-Turkey B"	"PAB-1-Cburnetti-Turkey B.CEL"	"PAB-1-Cburnetti-Turkey B.EXP"	P-TABM-69	"PAB-1-Cburnetti-Turkey B"	"PAB-1-Cburnetti-Turkey B.CHP"	RML-chipa510998.CDF	Turkey
diff --git a/examples/magetab/real/E-TABM-54.idf b/examples/magetab/real/E-TABM-54.idf
new file mode 100644
index 0000000..bdb664d
--- /dev/null
+++ b/examples/magetab/real/E-TABM-54.idf
@@ -0,0 +1,21 @@
+"Investigation Title"	"Bordetella pertussis comparative genome hybridization"	
+"Experimental Design"	strain_or_line_design	comparative_genome_hybridization
+"Experimental Factor Name"	STRAINORLINE	CLINICAL_INFORMATION
+"Experimental Factor Type"	strain_or_line	clinical_information
+		
+"Person Last Name"	Cummings	
+"Person First Name"	Craig	
+"Person Mid Initial"		
+"Person Email"	cummings at pmgm2.stanford.edu	
+"Person Roles"	submitter	
+		
+"Quality Control Types"	technical_replicate	biological_replicate
+"Public Release Date"	"2005-12-31"	
+		
+		
+"Experiment Description"	"Comparative genome hybridization (CGH) of sample of 137 Bordetella pertussis strains"	
+		
+"SDRF File"	E-TABM-54_sdrf.txt	
+		
+"Term Source Name"	ArrayExpress	
+"Term Source File"	http://www.ebi.ac.uk/arrayexpress/	
diff --git a/examples/magetab/real/E-TABM-54_sdrf.txt b/examples/magetab/real/E-TABM-54_sdrf.txt
new file mode 100644
index 0000000..66e4575
--- /dev/null
+++ b/examples/magetab/real/E-TABM-54_sdrf.txt
@@ -0,0 +1,275 @@
+"Source Name"	"Material Type"	"Characteristics [Organism]"	"Characteristics [StrainOrLine]"	"Protocol REF"	"Term Source REF"	"Sample Name"	"Material Type"	"Protocol REF"	"Term Source REF"	"Protocol REF"	"Term Source REF"	"Extract Name"	"Material Type"	"Protocol REF"	"Term Source REF"	"Labeled Extract Name"	"Material Type"	Label	"Protocol REF"	"Term Source REF"	"Hybridization Name"	"Array Design REF"	"Protocol REF"	"Term Source REF"	"Scan Name"	"Array Data File"	"Protocol REF"	"Term Sourc [...]
+Bpe60	whole_organism	"Bordetella pertussis"	Bpe60	P-MEXP-9739	ArrayExpress	Bpe60	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe60	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe60 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b23n061	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n061	b23n061.txt	P-MEXP-9965	ArrayExpress	b23n061	b23n061_norm.txt	Bpe60	"Tohama I laboratory strain"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n061	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n061	b23n061.txt	P-MEXP-9965	ArrayExpress	b23n061	b23n061_norm.txt	Bpe60	"Tohama I laborator [...]
+Bpe317	whole_organism	"Bordetella pertussis"	Bpe317	P-MEXP-9739	ArrayExpress	Bpe317	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe317	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe317 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b29n119	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n119	b29n119.txt	P-MEXP-9965	ArrayExpress	b29n119	b29n119_norm.txt	Bpe317	"GMT-1 recent lab strain"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n119	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n119	b29n119.txt	P-MEXP-9965	ArrayExpress	b29n119	b29n119_norm.txt	Bpe317	"GMT-1 recent lab strain"
+Bpe55	whole_organism	"Bordetella pertussis"	Bpe55	P-MEXP-9739	ArrayExpress	Bpe55	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe55	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe55 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b25n048	A-MEXP-234	P-MEXP-9747	ArrayExpress	b25n048	b25n048.txt	P-MEXP-9965	ArrayExpress	b25n048	b25n048_norm.txt	Bpe55	"Tohama II laboratory strain"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b25n048	A-MEXP-234	P-MEXP-9747	ArrayExpress	b25n048	b25n048.txt	P-MEXP-9965	ArrayExpress	b25n048	b25n048_norm.txt	Bpe55	"Tohama II laborato [...]
+Bpe243	whole_organism	"Bordetella pertussis"	Bpe243	P-MEXP-9739	ArrayExpress	Bpe243	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe243	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe243 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b29n112	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n112	b29n112.txt	P-MEXP-9965	ArrayExpress	b29n112	b29n112_norm.txt	Bpe243	"US pre-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n112	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n112	b29n112.txt	P-MEXP-9965	ArrayExpress	b29n112	b29n112_norm.txt	Bpe243	"US pre-vaccine"
+Bpe244	whole_organism	"Bordetella pertussis"	Bpe244	P-MEXP-9739	ArrayExpress	Bpe244	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe244	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe244 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b29n113	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n113	b29n113.txt	P-MEXP-9965	ArrayExpress	b29n113	b29n113_norm.txt	Bpe244	"US pre-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n113	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n113	b29n113.txt	P-MEXP-9965	ArrayExpress	b29n113	b29n113_norm.txt	Bpe244	"US pre-vaccine"
+Bpe245	whole_organism	"Bordetella pertussis"	Bpe245	P-MEXP-9739	ArrayExpress	Bpe245	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe245	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe245 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b29n114	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n114	b29n114.txt	P-MEXP-9965	ArrayExpress	b29n114	b29n114_norm.txt	Bpe245	"US pre-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n114	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n114	b29n114.txt	P-MEXP-9965	ArrayExpress	b29n114	b29n114_norm.txt	Bpe245	"US pre-vaccine"
+Bpe247	whole_organism	"Bordetella pertussis"	Bpe247	P-MEXP-9739	ArrayExpress	Bpe247	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe247	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe247 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b29n116	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n116	b29n116.txt	P-MEXP-9965	ArrayExpress	b29n116	b29n116_norm.txt	Bpe247	"US pre-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n116	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n116	b29n116.txt	P-MEXP-9965	ArrayExpress	b29n116	b29n116_norm.txt	Bpe247	"US pre-vaccine"
+Bpe41	whole_organism	"Bordetella pertussis"	Bpe41	P-MEXP-9739	ArrayExpress	Bpe41	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe41	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe41 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b23n124	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n124	b23n124.txt	P-MEXP-9965	ArrayExpress	b23n124	b23n124_norm.txt	Bpe41	"US Cincinnati outbreak"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n124	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n124	b23n124.txt	P-MEXP-9965	ArrayExpress	b23n124	b23n124_norm.txt	Bpe41	"US Cincinnati outbreak"
+Bpe51	whole_organism	"Bordetella pertussis"	Bpe51	P-MEXP-9739	ArrayExpress	Bpe51	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe51	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe51 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b24n122	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n122	b24n122.txt	P-MEXP-9965	ArrayExpress	b24n122	b24n122_norm.txt	Bpe51	"US Cincinnati outbreak"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n122	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n122	b24n122.txt	P-MEXP-9965	ArrayExpress	b24n122	b24n122_norm.txt	Bpe51	"US Cincinnati outbreak"
+Bpe43	whole_organism	"Bordetella pertussis"	Bpe43	P-MEXP-9739	ArrayExpress	Bpe43	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe43	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe43 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b24n016	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n016	b24n016.txt	P-MEXP-9965	ArrayExpress	b24n016	b24n016_norm.txt	Bpe43	"US Cincinnati outbreak"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n016	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n016	b24n016.txt	P-MEXP-9965	ArrayExpress	b24n016	b24n016_norm.txt	Bpe43	"US Cincinnati outbreak"
+Bpe44	whole_organism	"Bordetella pertussis"	Bpe44	P-MEXP-9739	ArrayExpress	Bpe44	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe44	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe44 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b24n017	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n017	b24n017.txt	P-MEXP-9965	ArrayExpress	b24n017	b24n017_norm.txt	Bpe44	"US Cincinnati outbreak"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n017	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n017	b24n017.txt	P-MEXP-9965	ArrayExpress	b24n017	b24n017_norm.txt	Bpe44	"US Cincinnati outbreak"
+Bpe48	whole_organism	"Bordetella pertussis"	Bpe48	P-MEXP-9739	ArrayExpress	Bpe48	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe48	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe48 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b24n020	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n020	b24n020.txt	P-MEXP-9965	ArrayExpress	b24n020	b24n020_norm.txt	Bpe48	"US Cincinnati outbreak"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n020	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n020	b24n020.txt	P-MEXP-9965	ArrayExpress	b24n020	b24n020_norm.txt	Bpe48	"US Cincinnati outbreak"
+Bpe50	whole_organism	"Bordetella pertussis"	Bpe50	P-MEXP-9739	ArrayExpress	Bpe50	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe50	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe50 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b29n105	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n105	b29n105.txt	P-MEXP-9965	ArrayExpress	b29n105	b29n105_norm.txt	Bpe50	"US Cincinnati non-outbreak"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n105	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n105	b29n105.txt	P-MEXP-9965	ArrayExpress	b29n105	b29n105_norm.txt	Bpe50	"US Cincinnati non- [...]
+Bpe52	whole_organism	"Bordetella pertussis"	Bpe52	P-MEXP-9739	ArrayExpress	Bpe52	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe52	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe52 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b24n052	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n052	b24n052.txt	P-MEXP-9965	ArrayExpress	b24n052	b24n052_norm.txt	Bpe52	"US Cincinnati non-outbreak"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n052	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n052	b24n052.txt	P-MEXP-9965	ArrayExpress	b24n052	b24n052_norm.txt	Bpe52	"US Cincinnati non- [...]
+Bpe37	whole_organism	"Bordetella pertussis"	Bpe37	P-MEXP-9739	ArrayExpress	Bpe37	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe37	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe37 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b24n054	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n054	b24n054.txt	P-MEXP-9965	ArrayExpress	b24n054	b24n054_norm.txt	Bpe37	"US Cincinnati non-outbreak"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n054	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n054	b24n054.txt	P-MEXP-9965	ArrayExpress	b24n054	b24n054_norm.txt	Bpe37	"US Cincinnati non- [...]
+Bpe33	whole_organism	"Bordetella pertussis"	Bpe33	P-MEXP-9739	ArrayExpress	Bpe33	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe33	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe33 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b24n113	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n113	b24n113.txt	P-MEXP-9965	ArrayExpress	b24n113	b24n113_norm.txt	Bpe33	"US Cincinnati non-outbreak"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n113	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n113	b24n113.txt	P-MEXP-9965	ArrayExpress	b24n113	b24n113_norm.txt	Bpe33	"US Cincinnati non- [...]
+Bpe34	whole_organism	"Bordetella pertussis"	Bpe34	P-MEXP-9739	ArrayExpress	Bpe34	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe34	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe34 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b24n114	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n114	b24n114.txt	P-MEXP-9965	ArrayExpress	b24n114	b24n114_norm.txt	Bpe34	"US Cincinnati non-outbreak"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n114	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n114	b24n114.txt	P-MEXP-9965	ArrayExpress	b24n114	b24n114_norm.txt	Bpe34	"US Cincinnati non- [...]
+Bpe35	whole_organism	"Bordetella pertussis"	Bpe35	P-MEXP-9739	ArrayExpress	Bpe35	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe35	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe35 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b24n115	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n115	b24n115.txt	P-MEXP-9965	ArrayExpress	b24n115	b24n115_norm.txt	Bpe35	"US Cincinnati non-outbreak"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n115	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n115	b24n115.txt	P-MEXP-9965	ArrayExpress	b24n115	b24n115_norm.txt	Bpe35	"US Cincinnati non- [...]
+Bpe36	whole_organism	"Bordetella pertussis"	Bpe36	P-MEXP-9739	ArrayExpress	Bpe36	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe36	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe36 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b24n116	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n116	b24n116.txt	P-MEXP-9965	ArrayExpress	b24n116	b24n116_norm.txt	Bpe36	"US Cincinnati non-outbreak"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n116	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n116	b24n116.txt	P-MEXP-9965	ArrayExpress	b24n116	b24n116_norm.txt	Bpe36	"US Cincinnati non- [...]
+Bpe38	whole_organism	"Bordetella pertussis"	Bpe38	P-MEXP-9739	ArrayExpress	Bpe38	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe38	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe38 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b24n117	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n117	b24n117.txt	P-MEXP-9965	ArrayExpress	b24n117	b24n117_norm.txt	Bpe38	"US Cincinnati non-outbreak"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n117	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n117	b24n117.txt	P-MEXP-9965	ArrayExpress	b24n117	b24n117_norm.txt	Bpe38	"US Cincinnati non- [...]
+Bpe40	whole_organism	"Bordetella pertussis"	Bpe40	P-MEXP-9739	ArrayExpress	Bpe40	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe40	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe40 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b24n118	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n118	b24n118.txt	P-MEXP-9965	ArrayExpress	b24n118	b24n118_norm.txt	Bpe40	"US Cincinnati non-outbreak"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n118	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n118	b24n118.txt	P-MEXP-9965	ArrayExpress	b24n118	b24n118_norm.txt	Bpe40	"US Cincinnati non- [...]
+Bpe47	whole_organism	"Bordetella pertussis"	Bpe47	P-MEXP-9739	ArrayExpress	Bpe47	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe47	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe47 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b24n120	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n120	b24n120.txt	P-MEXP-9965	ArrayExpress	b24n120	b24n120_norm.txt	Bpe47	"US Cincinnati non-outbreak"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n120	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n120	b24n120.txt	P-MEXP-9965	ArrayExpress	b24n120	b24n120_norm.txt	Bpe47	"US Cincinnati non- [...]
+Bpe49	whole_organism	"Bordetella pertussis"	Bpe49	P-MEXP-9739	ArrayExpress	Bpe49	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe49	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe49 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b24n121	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n121	b24n121.txt	P-MEXP-9965	ArrayExpress	b24n121	b24n121_norm.txt	Bpe49	"US Cincinnati non-outbreak"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n121	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n121	b24n121.txt	P-MEXP-9965	ArrayExpress	b24n121	b24n121_norm.txt	Bpe49	"US Cincinnati non- [...]
+Bpe59	whole_organism	"Bordetella pertussis"	Bpe59	P-MEXP-9739	ArrayExpress	Bpe59	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe59	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe59 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b29n108	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n108	b29n108.txt	P-MEXP-9965	ArrayExpress	b29n108	b29n108_norm.txt	Bpe59	"US Cincinnati non-outbreak"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n108	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n108	b29n108.txt	P-MEXP-9965	ArrayExpress	b29n108	b29n108_norm.txt	Bpe59	"US Cincinnati non- [...]
+Bpe58	whole_organism	"Bordetella pertussis"	Bpe58	P-MEXP-9739	ArrayExpress	Bpe58	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe58	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe58 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b29n106	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n106	b29n106.txt	P-MEXP-9965	ArrayExpress	b29n106	b29n106_norm.txt	Bpe58	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n106	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n106	b29n106.txt	P-MEXP-9965	ArrayExpress	b29n106	b29n106_norm.txt	Bpe58	"Other US post-vaccine"
+Bpe45	whole_organism	"Bordetella pertussis"	Bpe45	P-MEXP-9739	ArrayExpress	Bpe45	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe45	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe45 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b24n032	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n032	b24n032.txt	P-MEXP-9965	ArrayExpress	b24n032	b24n032_norm.txt	Bpe45	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n032	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n032	b24n032.txt	P-MEXP-9965	ArrayExpress	b24n032	b24n032_norm.txt	Bpe45	"Other US post-vaccine"
+Bpe329	whole_organism	"Bordetella pertussis"	Bpe329	P-MEXP-9739	ArrayExpress	Bpe329	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe329	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe329 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b29n132	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n132	b29n132.txt	P-MEXP-9965	ArrayExpress	b29n132	b29n132_norm.txt	Bpe329	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n132	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n132	b29n132.txt	P-MEXP-9965	ArrayExpress	b29n132	b29n132_norm.txt	Bpe329	"Other US post-vaccine"
+Bpe39	whole_organism	"Bordetella pertussis"	Bpe39	P-MEXP-9739	ArrayExpress	Bpe39	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe39	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe39 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b23n123	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n123	b23n123.txt	P-MEXP-9965	ArrayExpress	b23n123	b23n123_norm.txt	Bpe39	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n123	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n123	b23n123.txt	P-MEXP-9965	ArrayExpress	b23n123	b23n123_norm.txt	Bpe39	"Other US post-vaccine"
+Bpe42	whole_organism	"Bordetella pertussis"	Bpe42	P-MEXP-9739	ArrayExpress	Bpe42	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe42	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe42 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b23n125	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n125	b23n125.txt	P-MEXP-9965	ArrayExpress	b23n125	b23n125_norm.txt	Bpe42	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n125	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n125	b23n125.txt	P-MEXP-9965	ArrayExpress	b23n125	b23n125_norm.txt	Bpe42	"Other US post-vaccine"
+Bpe241	whole_organism	"Bordetella pertussis"	Bpe241	P-MEXP-9739	ArrayExpress	Bpe241	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe241	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe241 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b29n111	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n111	b29n111.txt	P-MEXP-9965	ArrayExpress	b29n111	b29n111_norm.txt	Bpe241	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n111	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n111	b29n111.txt	P-MEXP-9965	ArrayExpress	b29n111	b29n111_norm.txt	Bpe241	"Other US post-vaccine"
+Bpe46	whole_organism	"Bordetella pertussis"	Bpe46	P-MEXP-9739	ArrayExpress	Bpe46	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe46	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe46 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b24n119	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n119	b24n119.txt	P-MEXP-9965	ArrayExpress	b24n119	b24n119_norm.txt	Bpe46	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n119	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n119	b24n119.txt	P-MEXP-9965	ArrayExpress	b24n119	b24n119_norm.txt	Bpe46	"Other US post-vaccine"
+Bpe280	whole_organism	"Bordetella pertussis"	Bpe280	P-MEXP-9739	ArrayExpress	Bpe280	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe280	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe280 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b29n117	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n117	b29n117.txt	P-MEXP-9965	ArrayExpress	b29n117	b29n117_norm.txt	Bpe280	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n117	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n117	b29n117.txt	P-MEXP-9965	ArrayExpress	b29n117	b29n117_norm.txt	Bpe280	"Other US post-vaccine"
+Bpe281	whole_organism	"Bordetella pertussis"	Bpe281	P-MEXP-9739	ArrayExpress	Bpe281	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe281	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe281 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b29n118	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n118	b29n118.txt	P-MEXP-9965	ArrayExpress	b29n118	b29n118_norm.txt	Bpe281	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n118	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n118	b29n118.txt	P-MEXP-9965	ArrayExpress	b29n118	b29n118_norm.txt	Bpe281	"Other US post-vaccine"
+Bpe319	whole_organism	"Bordetella pertussis"	Bpe319	P-MEXP-9739	ArrayExpress	Bpe319	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe319	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe319 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b29n120	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n120	b29n120.txt	P-MEXP-9965	ArrayExpress	b29n120	b29n120_norm.txt	Bpe319	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n120	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n120	b29n120.txt	P-MEXP-9965	ArrayExpress	b29n120	b29n120_norm.txt	Bpe319	"Other US post-vaccine"
+Bpe320	whole_organism	"Bordetella pertussis"	Bpe320	P-MEXP-9739	ArrayExpress	Bpe320	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe320	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe320 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b29n121	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n121	b29n121.txt	P-MEXP-9965	ArrayExpress	b29n121	b29n121_norm.txt	Bpe320	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n121	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n121	b29n121.txt	P-MEXP-9965	ArrayExpress	b29n121	b29n121_norm.txt	Bpe320	"Other US post-vaccine"
+Bpe321	whole_organism	"Bordetella pertussis"	Bpe321	P-MEXP-9739	ArrayExpress	Bpe321	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe321	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe321 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b29n122	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n122	b29n122.txt	P-MEXP-9965	ArrayExpress	b29n122	b29n122_norm.txt	Bpe321	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n122	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n122	b29n122.txt	P-MEXP-9965	ArrayExpress	b29n122	b29n122_norm.txt	Bpe321	"Other US post-vaccine"
+Bpe322	whole_organism	"Bordetella pertussis"	Bpe322	P-MEXP-9739	ArrayExpress	Bpe322	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe322	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe322 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b29n125	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n125	b29n125.txt	P-MEXP-9965	ArrayExpress	b29n125	b29n125_norm.txt	Bpe322	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n125	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n125	b29n125.txt	P-MEXP-9965	ArrayExpress	b29n125	b29n125_norm.txt	Bpe322	"Other US post-vaccine"
+Bpe323	whole_organism	"Bordetella pertussis"	Bpe323	P-MEXP-9739	ArrayExpress	Bpe323	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe323	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe323 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b29n126	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n126	b29n126.txt	P-MEXP-9965	ArrayExpress	b29n126	b29n126_norm.txt	Bpe323	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n126	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n126	b29n126.txt	P-MEXP-9965	ArrayExpress	b29n126	b29n126_norm.txt	Bpe323	"Other US post-vaccine"
+Bpe324	whole_organism	"Bordetella pertussis"	Bpe324	P-MEXP-9739	ArrayExpress	Bpe324	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe324	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe324 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b29n127	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n127	b29n127.txt	P-MEXP-9965	ArrayExpress	b29n127	b29n127_norm.txt	Bpe324	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n127	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n127	b29n127.txt	P-MEXP-9965	ArrayExpress	b29n127	b29n127_norm.txt	Bpe324	"Other US post-vaccine"
+Bpe325	whole_organism	"Bordetella pertussis"	Bpe325	P-MEXP-9739	ArrayExpress	Bpe325	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe325	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe325 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b29n128	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n128	b29n128.txt	P-MEXP-9965	ArrayExpress	b29n128	b29n128_norm.txt	Bpe325	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n128	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n128	b29n128.txt	P-MEXP-9965	ArrayExpress	b29n128	b29n128_norm.txt	Bpe325	"Other US post-vaccine"
+Bpe326	whole_organism	"Bordetella pertussis"	Bpe326	P-MEXP-9739	ArrayExpress	Bpe326	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe326	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe326 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b29n129	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n129	b29n129.txt	P-MEXP-9965	ArrayExpress	b29n129	b29n129_norm.txt	Bpe326	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n129	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n129	b29n129.txt	P-MEXP-9965	ArrayExpress	b29n129	b29n129_norm.txt	Bpe326	"Other US post-vaccine"
+Bpe327	whole_organism	"Bordetella pertussis"	Bpe327	P-MEXP-9739	ArrayExpress	Bpe327	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe327	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe327 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b29n130	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n130	b29n130.txt	P-MEXP-9965	ArrayExpress	b29n130	b29n130_norm.txt	Bpe327	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n130	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n130	b29n130.txt	P-MEXP-9965	ArrayExpress	b29n130	b29n130_norm.txt	Bpe327	"Other US post-vaccine"
+Bpe328	whole_organism	"Bordetella pertussis"	Bpe328	P-MEXP-9739	ArrayExpress	Bpe328	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe328	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe328 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b29n131	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n131	b29n131.txt	P-MEXP-9965	ArrayExpress	b29n131	b29n131_norm.txt	Bpe328	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n131	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n131	b29n131.txt	P-MEXP-9965	ArrayExpress	b29n131	b29n131_norm.txt	Bpe328	"Other US post-vaccine"
+Bpe330	whole_organism	"Bordetella pertussis"	Bpe330	P-MEXP-9739	ArrayExpress	Bpe330	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe330	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe330 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b31n040	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n040	b31n040.txt	P-MEXP-9965	ArrayExpress	b31n040	b31n040_norm.txt	Bpe330	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b31n040	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n040	b31n040.txt	P-MEXP-9965	ArrayExpress	b31n040	b31n040_norm.txt	Bpe330	"Other US post-vaccine"
+Bpe331	whole_organism	"Bordetella pertussis"	Bpe331	P-MEXP-9739	ArrayExpress	Bpe331	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe331	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe331 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b31n041	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n041	b31n041.txt	P-MEXP-9965	ArrayExpress	b31n041	b31n041_norm.txt	Bpe331	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b31n041	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n041	b31n041.txt	P-MEXP-9965	ArrayExpress	b31n041	b31n041_norm.txt	Bpe331	"Other US post-vaccine"
+Bpe332	whole_organism	"Bordetella pertussis"	Bpe332	P-MEXP-9739	ArrayExpress	Bpe332	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe332	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe332 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b31n042	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n042	b31n042.txt	P-MEXP-9965	ArrayExpress	b31n042	b31n042_norm.txt	Bpe332	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b31n042	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n042	b31n042.txt	P-MEXP-9965	ArrayExpress	b31n042	b31n042_norm.txt	Bpe332	"Other US post-vaccine"
+Bpe333	whole_organism	"Bordetella pertussis"	Bpe333	P-MEXP-9739	ArrayExpress	Bpe333	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe333	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe333 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b31n043	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n043	b31n043.txt	P-MEXP-9965	ArrayExpress	b31n043	b31n043_norm.txt	Bpe333	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b31n043	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n043	b31n043.txt	P-MEXP-9965	ArrayExpress	b31n043	b31n043_norm.txt	Bpe333	"Other US post-vaccine"
+Bpe334	whole_organism	"Bordetella pertussis"	Bpe334	P-MEXP-9739	ArrayExpress	Bpe334	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe334	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe334 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b31n044	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n044	b31n044.txt	P-MEXP-9965	ArrayExpress	b31n044	b31n044_norm.txt	Bpe334	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b31n044	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n044	b31n044.txt	P-MEXP-9965	ArrayExpress	b31n044	b31n044_norm.txt	Bpe334	"Other US post-vaccine"
+Bpe335	whole_organism	"Bordetella pertussis"	Bpe335	P-MEXP-9739	ArrayExpress	Bpe335	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe335	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe335 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b31n045	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n045	b31n045.txt	P-MEXP-9965	ArrayExpress	b31n045	b31n045_norm.txt	Bpe335	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b31n045	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n045	b31n045.txt	P-MEXP-9965	ArrayExpress	b31n045	b31n045_norm.txt	Bpe335	"Other US post-vaccine"
+Bpe336	whole_organism	"Bordetella pertussis"	Bpe336	P-MEXP-9739	ArrayExpress	Bpe336	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe336	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe336 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b31n046	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n046	b31n046.txt	P-MEXP-9965	ArrayExpress	b31n046	b31n046_norm.txt	Bpe336	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b31n046	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n046	b31n046.txt	P-MEXP-9965	ArrayExpress	b31n046	b31n046_norm.txt	Bpe336	"Other US post-vaccine"
+Bpe337	whole_organism	"Bordetella pertussis"	Bpe337	P-MEXP-9739	ArrayExpress	Bpe337	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe337	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe337 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b31n047	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n047	b31n047.txt	P-MEXP-9965	ArrayExpress	b31n047	b31n047_norm.txt	Bpe337	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b31n047	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n047	b31n047.txt	P-MEXP-9965	ArrayExpress	b31n047	b31n047_norm.txt	Bpe337	"Other US post-vaccine"
+Bpe338	whole_organism	"Bordetella pertussis"	Bpe338	P-MEXP-9739	ArrayExpress	Bpe338	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe338	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe338 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b31n048	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n048	b31n048.txt	P-MEXP-9965	ArrayExpress	b31n048	b31n048_norm.txt	Bpe338	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b31n048	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n048	b31n048.txt	P-MEXP-9965	ArrayExpress	b31n048	b31n048_norm.txt	Bpe338	"Other US post-vaccine"
+Bpe339	whole_organism	"Bordetella pertussis"	Bpe339	P-MEXP-9739	ArrayExpress	Bpe339	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe339	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe339 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b31n049	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n049	b31n049.txt	P-MEXP-9965	ArrayExpress	b31n049	b31n049_norm.txt	Bpe339	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b31n049	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n049	b31n049.txt	P-MEXP-9965	ArrayExpress	b31n049	b31n049_norm.txt	Bpe339	"Other US post-vaccine"
+Bpe340	whole_organism	"Bordetella pertussis"	Bpe340	P-MEXP-9739	ArrayExpress	Bpe340	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe340	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe340 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b31n050	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n050	b31n050.txt	P-MEXP-9965	ArrayExpress	b31n050	b31n050_norm.txt	Bpe340	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b31n050	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n050	b31n050.txt	P-MEXP-9965	ArrayExpress	b31n050	b31n050_norm.txt	Bpe340	"Other US post-vaccine"
+Bpe341	whole_organism	"Bordetella pertussis"	Bpe341	P-MEXP-9739	ArrayExpress	Bpe341	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe341	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe341 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b31n051	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n051	b31n051.txt	P-MEXP-9965	ArrayExpress	b31n051	b31n051_norm.txt	Bpe341	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b31n051	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n051	b31n051.txt	P-MEXP-9965	ArrayExpress	b31n051	b31n051_norm.txt	Bpe341	"Other US post-vaccine"
+Bpe342	whole_organism	"Bordetella pertussis"	Bpe342	P-MEXP-9739	ArrayExpress	Bpe342	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe342	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe342 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b31n052	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n052	b31n052.txt	P-MEXP-9965	ArrayExpress	b31n052	b31n052_norm.txt	Bpe342	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b31n052	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n052	b31n052.txt	P-MEXP-9965	ArrayExpress	b31n052	b31n052_norm.txt	Bpe342	"Other US post-vaccine"
+Bpe343	whole_organism	"Bordetella pertussis"	Bpe343	P-MEXP-9739	ArrayExpress	Bpe343	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe343	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe343 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b31n053	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n053	b31n053.txt	P-MEXP-9965	ArrayExpress	b31n053	b31n053_norm.txt	Bpe343	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b31n053	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n053	b31n053.txt	P-MEXP-9965	ArrayExpress	b31n053	b31n053_norm.txt	Bpe343	"Other US post-vaccine"
+Bpe344	whole_organism	"Bordetella pertussis"	Bpe344	P-MEXP-9739	ArrayExpress	Bpe344	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe344	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe344 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b31n054	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n054	b31n054.txt	P-MEXP-9965	ArrayExpress	b31n054	b31n054_norm.txt	Bpe344	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b31n054	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n054	b31n054.txt	P-MEXP-9965	ArrayExpress	b31n054	b31n054_norm.txt	Bpe344	"Other US post-vaccine"
+Bpe345	whole_organism	"Bordetella pertussis"	Bpe345	P-MEXP-9739	ArrayExpress	Bpe345	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe345	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe345 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b31n055	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n055	b31n055.txt	P-MEXP-9965	ArrayExpress	b31n055	b31n055_norm.txt	Bpe345	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b31n055	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n055	b31n055.txt	P-MEXP-9965	ArrayExpress	b31n055	b31n055_norm.txt	Bpe345	"Other US post-vaccine"
+Bpe346	whole_organism	"Bordetella pertussis"	Bpe346	P-MEXP-9739	ArrayExpress	Bpe346	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe346	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe346 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b31n056	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n056	b31n056.txt	P-MEXP-9965	ArrayExpress	b31n056	b31n056_norm.txt	Bpe346	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b31n056	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n056	b31n056.txt	P-MEXP-9965	ArrayExpress	b31n056	b31n056_norm.txt	Bpe346	"Other US post-vaccine"
+Bpe347	whole_organism	"Bordetella pertussis"	Bpe347	P-MEXP-9739	ArrayExpress	Bpe347	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe347	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe347 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b31n057	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n057	b31n057.txt	P-MEXP-9965	ArrayExpress	b31n057	b31n057_norm.txt	Bpe347	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b31n057	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n057	b31n057.txt	P-MEXP-9965	ArrayExpress	b31n057	b31n057_norm.txt	Bpe347	"Other US post-vaccine"
+Bpe348	whole_organism	"Bordetella pertussis"	Bpe348	P-MEXP-9739	ArrayExpress	Bpe348	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe348	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe348 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b31n058	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n058	b31n058.txt	P-MEXP-9965	ArrayExpress	b31n058	b31n058_norm.txt	Bpe348	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b31n058	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n058	b31n058.txt	P-MEXP-9965	ArrayExpress	b31n058	b31n058_norm.txt	Bpe348	"Other US post-vaccine"
+Bpe349	whole_organism	"Bordetella pertussis"	Bpe349	P-MEXP-9739	ArrayExpress	Bpe349	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe349	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe349 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b31n059	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n059	b31n059.txt	P-MEXP-9965	ArrayExpress	b31n059	b31n059_norm.txt	Bpe349	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b31n059	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n059	b31n059.txt	P-MEXP-9965	ArrayExpress	b31n059	b31n059_norm.txt	Bpe349	"Other US post-vaccine"
+Bpe350	whole_organism	"Bordetella pertussis"	Bpe350	P-MEXP-9739	ArrayExpress	Bpe350	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe350	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe350 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b31n060	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n060	b31n060.txt	P-MEXP-9965	ArrayExpress	b31n060	b31n060_norm.txt	Bpe350	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b31n060	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n060	b31n060.txt	P-MEXP-9965	ArrayExpress	b31n060	b31n060_norm.txt	Bpe350	"Other US post-vaccine"
+Bpe351	whole_organism	"Bordetella pertussis"	Bpe351	P-MEXP-9739	ArrayExpress	Bpe351	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe351	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe351 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b31n061	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n061	b31n061.txt	P-MEXP-9965	ArrayExpress	b31n061	b31n061_norm.txt	Bpe351	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b31n061	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n061	b31n061.txt	P-MEXP-9965	ArrayExpress	b31n061	b31n061_norm.txt	Bpe351	"Other US post-vaccine"
+Bpe352	whole_organism	"Bordetella pertussis"	Bpe352	P-MEXP-9739	ArrayExpress	Bpe352	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe352	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe352 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b31n062	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n062	b31n062.txt	P-MEXP-9965	ArrayExpress	b31n062	b31n062_norm.txt	Bpe352	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b31n062	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n062	b31n062.txt	P-MEXP-9965	ArrayExpress	b31n062	b31n062_norm.txt	Bpe352	"Other US post-vaccine"
+Bpe353	whole_organism	"Bordetella pertussis"	Bpe353	P-MEXP-9739	ArrayExpress	Bpe353	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe353	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe353 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b31n063	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n063	b31n063.txt	P-MEXP-9965	ArrayExpress	b31n063	b31n063_norm.txt	Bpe353	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b31n063	a-mexp-281!	P-MEXP-9747	ArrayExpress	b31n063	b31n063.txt	P-MEXP-9965	ArrayExpress	b31n063	b31n063_norm.txt	Bpe353	"Other US post-vaccine"
+Bpe159	whole_organism	"Bordetella pertussis"	Bpe159	P-MEXP-9739	ArrayExpress	Bpe159	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe159	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe159 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b23n006	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n006	b23n006.txt	P-MEXP-9965	ArrayExpress	b23n006	b23n006_norm.txt	Bpe159	"Other US post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n006	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n006	b23n006.txt	P-MEXP-9965	ArrayExpress	b23n006	b23n006_norm.txt	Bpe159	"Other US post-vaccine"
+Bpe205	whole_organism	"Bordetella pertussis"	Bpe205	P-MEXP-9739	ArrayExpress	Bpe205	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe205	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe205 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b23n083	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n083	b23n083.txt	P-MEXP-9965	ArrayExpress	b23n083	b23n083_norm.txt	Bpe205	"Dutch pre-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n083	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n083	b23n083.txt	P-MEXP-9965	ArrayExpress	b23n083	b23n083_norm.txt	Bpe205	"Dutch pre-vaccine"
+Bpe186	whole_organism	"Bordetella pertussis"	Bpe186	P-MEXP-9739	ArrayExpress	Bpe186	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe186	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe186 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b23n109	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n109	b23n109.txt	P-MEXP-9965	ArrayExpress	b23n109	b23n109_norm.txt	Bpe186	"Dutch pre-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n109	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n109	b23n109.txt	P-MEXP-9965	ArrayExpress	b23n109	b23n109_norm.txt	Bpe186	"Dutch pre-vaccine"
+Bpe191	whole_organism	"Bordetella pertussis"	Bpe191	P-MEXP-9739	ArrayExpress	Bpe191	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe191	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe191 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b23n114	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n114	b23n114.txt	P-MEXP-9965	ArrayExpress	b23n114	b23n114_norm.txt	Bpe191	"Dutch pre-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n114	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n114	b23n114.txt	P-MEXP-9965	ArrayExpress	b23n114	b23n114_norm.txt	Bpe191	"Dutch pre-vaccine"
+Bpe202	whole_organism	"Bordetella pertussis"	Bpe202	P-MEXP-9739	ArrayExpress	Bpe202	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe202	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe202 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b24n061	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n061	b24n061.txt	P-MEXP-9965	ArrayExpress	b24n061	b24n061_norm.txt	Bpe202	"Dutch pre-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n061	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n061	b24n061.txt	P-MEXP-9965	ArrayExpress	b24n061	b24n061_norm.txt	Bpe202	"Dutch pre-vaccine"
+Bpe197	whole_organism	"Bordetella pertussis"	Bpe197	P-MEXP-9739	ArrayExpress	Bpe197	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe197	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe197 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b24n056	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n056	b24n056.txt	P-MEXP-9965	ArrayExpress	b24n056	b24n056_norm.txt	Bpe197	"Dutch pre-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n056	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n056	b24n056.txt	P-MEXP-9965	ArrayExpress	b24n056	b24n056_norm.txt	Bpe197	"Dutch pre-vaccine"
+Bpe182	whole_organism	"Bordetella pertussis"	Bpe182	P-MEXP-9739	ArrayExpress	Bpe182	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe182	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe182 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b23n105	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n105	b23n105.txt	P-MEXP-9965	ArrayExpress	b23n105	b23n105_norm.txt	Bpe182	"Dutch pre-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n105	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n105	b23n105.txt	P-MEXP-9965	ArrayExpress	b23n105	b23n105_norm.txt	Bpe182	"Dutch pre-vaccine"
+Bpe195	whole_organism	"Bordetella pertussis"	Bpe195	P-MEXP-9739	ArrayExpress	Bpe195	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe195	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe195 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b23n118	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n118	b23n118.txt	P-MEXP-9965	ArrayExpress	b23n118	b23n118_norm.txt	Bpe195	"Dutch pre-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n118	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n118	b23n118.txt	P-MEXP-9965	ArrayExpress	b23n118	b23n118_norm.txt	Bpe195	"Dutch pre-vaccine"
+Bpe196	whole_organism	"Bordetella pertussis"	Bpe196	P-MEXP-9739	ArrayExpress	Bpe196	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe196	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe196 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b23n119	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n119	b23n119.txt	P-MEXP-9965	ArrayExpress	b23n119	b23n119_norm.txt	Bpe196	"Dutch pre-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n119	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n119	b23n119.txt	P-MEXP-9965	ArrayExpress	b23n119	b23n119_norm.txt	Bpe196	"Dutch pre-vaccine"
+Bpe179	whole_organism	"Bordetella pertussis"	Bpe179	P-MEXP-9739	ArrayExpress	Bpe179	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe179	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe179 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b23n101	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n101	b23n101.txt	P-MEXP-9965	ArrayExpress	b23n101	b23n101_norm.txt	Bpe179	"Dutch pre-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n101	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n101	b23n101.txt	P-MEXP-9965	ArrayExpress	b23n101	b23n101_norm.txt	Bpe179	"Dutch pre-vaccine"
+Bpe1	whole_organism	"Bordetella pertussis"	Bpe1	P-MEXP-9739	ArrayExpress	Bpe1	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe1	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe1 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b29n101	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n101	b29n101.txt	P-MEXP-9965	ArrayExpress	b29n101	b29n101_norm.txt	Bpe1	"Dutch post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n101	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n101	b29n101.txt	P-MEXP-9965	ArrayExpress	b29n101	b29n101_norm.txt	Bpe1	"Dutch post-vaccine"
+Bpe11	whole_organism	"Bordetella pertussis"	Bpe11	P-MEXP-9739	ArrayExpress	Bpe11	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe11	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe11 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b29n102	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n102	b29n102.txt	P-MEXP-9965	ArrayExpress	b29n102	b29n102_norm.txt	Bpe11	"Dutch post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n102	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n102	b29n102.txt	P-MEXP-9965	ArrayExpress	b29n102	b29n102_norm.txt	Bpe11	"Dutch post-vaccine"
+Bpe14	whole_organism	"Bordetella pertussis"	Bpe14	P-MEXP-9739	ArrayExpress	Bpe14	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe14	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe14 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b29n103	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n103	b29n103.txt	P-MEXP-9965	ArrayExpress	b29n103	b29n103_norm.txt	Bpe14	"Dutch post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n103	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n103	b29n103.txt	P-MEXP-9965	ArrayExpress	b29n103	b29n103_norm.txt	Bpe14	"Dutch post-vaccine"
+Bpe15	whole_organism	"Bordetella pertussis"	Bpe15	P-MEXP-9739	ArrayExpress	Bpe15	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe15	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe15 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b29n104	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n104	b29n104.txt	P-MEXP-9965	ArrayExpress	b29n104	b29n104_norm.txt	Bpe15	"Dutch post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n104	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n104	b29n104.txt	P-MEXP-9965	ArrayExpress	b29n104	b29n104_norm.txt	Bpe15	"Dutch post-vaccine"
+Bpe206	whole_organism	"Bordetella pertussis"	Bpe206	P-MEXP-9739	ArrayExpress	Bpe206	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe206	genomic_DNA	P-MEXP-9967	ArrayExpress	"Bpe206 Cy5"	synthetic_DNA	Cy5	P-MEXP-9746	ArrayExpress	b23n085	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n085	b23n085.txt	P-MEXP-9965	ArrayExpress	b23n085	b23n085_norm.txt	Bpe206	"Dutch post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n085	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n085	b23n085.txt	P-MEXP-9965	ArrayExpress	b23n085	b23n085_norm.txt	Bpe206	"Dutch post-vaccine"
+Bpe180	whole_organism	"Bordetella pertussis"	Bpe180	P-MEXP-9739	ArrayExpress	Bpe180	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe180	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bpe180 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n103	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n103	b23n103.txt	P-MEXP-9965	ArrayExpress	b23n103	b23n103_norm.txt	Bpe180	"Dutch post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n103	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n103	b23n103.txt	P-MEXP-9965	ArrayExpress	b23n103	b23n103_norm.txt	Bpe180	"Dutch post-vaccine"
+Bpe181	whole_organism	"Bordetella pertussis"	Bpe181	P-MEXP-9739	ArrayExpress	Bpe181	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe181	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bpe181 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n104	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n104	b23n104.txt	P-MEXP-9965	ArrayExpress	b23n104	b23n104_norm.txt	Bpe181	"Dutch post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n104	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n104	b23n104.txt	P-MEXP-9965	ArrayExpress	b23n104	b23n104_norm.txt	Bpe181	"Dutch post-vaccine"
+Bpe183	whole_organism	"Bordetella pertussis"	Bpe183	P-MEXP-9739	ArrayExpress	Bpe183	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe183	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bpe183 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n106	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n106	b23n106.txt	P-MEXP-9965	ArrayExpress	b23n106	b23n106_norm.txt	Bpe183	"Dutch post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n106	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n106	b23n106.txt	P-MEXP-9965	ArrayExpress	b23n106	b23n106_norm.txt	Bpe183	"Dutch post-vaccine"
+Bpe184	whole_organism	"Bordetella pertussis"	Bpe184	P-MEXP-9739	ArrayExpress	Bpe184	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe184	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bpe184 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n107	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n107	b23n107.txt	P-MEXP-9965	ArrayExpress	b23n107	b23n107_norm.txt	Bpe184	"Dutch post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n107	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n107	b23n107.txt	P-MEXP-9965	ArrayExpress	b23n107	b23n107_norm.txt	Bpe184	"Dutch post-vaccine"
+Bpe185	whole_organism	"Bordetella pertussis"	Bpe185	P-MEXP-9739	ArrayExpress	Bpe185	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe185	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bpe185 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n108	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n108	b23n108.txt	P-MEXP-9965	ArrayExpress	b23n108	b23n108_norm.txt	Bpe185	"Dutch post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n108	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n108	b23n108.txt	P-MEXP-9965	ArrayExpress	b23n108	b23n108_norm.txt	Bpe185	"Dutch post-vaccine"
+Bpe187	whole_organism	"Bordetella pertussis"	Bpe187	P-MEXP-9739	ArrayExpress	Bpe187	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe187	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bpe187 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n110	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n110	b23n110.txt	P-MEXP-9965	ArrayExpress	b23n110	b23n110_norm.txt	Bpe187	"Dutch post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n110	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n110	b23n110.txt	P-MEXP-9965	ArrayExpress	b23n110	b23n110_norm.txt	Bpe187	"Dutch post-vaccine"
+Bpe188	whole_organism	"Bordetella pertussis"	Bpe188	P-MEXP-9739	ArrayExpress	Bpe188	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe188	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bpe188 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n111	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n111	b23n111.txt	P-MEXP-9965	ArrayExpress	b23n111	b23n111_norm.txt	Bpe188	"Dutch post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n111	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n111	b23n111.txt	P-MEXP-9965	ArrayExpress	b23n111	b23n111_norm.txt	Bpe188	"Dutch post-vaccine"
+Bpe189	whole_organism	"Bordetella pertussis"	Bpe189	P-MEXP-9739	ArrayExpress	Bpe189	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe189	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bpe189 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n112	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n112	b23n112.txt	P-MEXP-9965	ArrayExpress	b23n112	b23n112_norm.txt	Bpe189	"Dutch post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n112	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n112	b23n112.txt	P-MEXP-9965	ArrayExpress	b23n112	b23n112_norm.txt	Bpe189	"Dutch post-vaccine"
+Bpe190	whole_organism	"Bordetella pertussis"	Bpe190	P-MEXP-9739	ArrayExpress	Bpe190	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe190	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bpe190 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n113	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n113	b23n113.txt	P-MEXP-9965	ArrayExpress	b23n113	b23n113_norm.txt	Bpe190	"Dutch post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n113	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n113	b23n113.txt	P-MEXP-9965	ArrayExpress	b23n113	b23n113_norm.txt	Bpe190	"Dutch post-vaccine"
+Bpe192	whole_organism	"Bordetella pertussis"	Bpe192	P-MEXP-9739	ArrayExpress	Bpe192	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe192	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bpe192 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n115	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n115	b23n115.txt	P-MEXP-9965	ArrayExpress	b23n115	b23n115_norm.txt	Bpe192	"Dutch post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n115	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n115	b23n115.txt	P-MEXP-9965	ArrayExpress	b23n115	b23n115_norm.txt	Bpe192	"Dutch post-vaccine"
+Bpe193	whole_organism	"Bordetella pertussis"	Bpe193	P-MEXP-9739	ArrayExpress	Bpe193	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe193	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bpe193 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n116	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n116	b23n116.txt	P-MEXP-9965	ArrayExpress	b23n116	b23n116_norm.txt	Bpe193	"Dutch post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n116	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n116	b23n116.txt	P-MEXP-9965	ArrayExpress	b23n116	b23n116_norm.txt	Bpe193	"Dutch post-vaccine"
+Bpe194	whole_organism	"Bordetella pertussis"	Bpe194	P-MEXP-9739	ArrayExpress	Bpe194	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe194	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bpe194 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n117	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n117	b23n117.txt	P-MEXP-9965	ArrayExpress	b23n117	b23n117_norm.txt	Bpe194	"Dutch post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n117	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n117	b23n117.txt	P-MEXP-9965	ArrayExpress	b23n117	b23n117_norm.txt	Bpe194	"Dutch post-vaccine"
+Bpe204	whole_organism	"Bordetella pertussis"	Bpe204	P-MEXP-9739	ArrayExpress	Bpe204	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe204	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bpe204 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n009	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n009	b24n009.txt	P-MEXP-9965	ArrayExpress	b24n009	b24n009_norm.txt	Bpe204	"Dutch post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n009	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n009	b24n009.txt	P-MEXP-9965	ArrayExpress	b24n009	b24n009_norm.txt	Bpe204	"Dutch post-vaccine"
+Bpe198	whole_organism	"Bordetella pertussis"	Bpe198	P-MEXP-9739	ArrayExpress	Bpe198	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe198	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bpe198 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n057	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n057	b24n057.txt	P-MEXP-9965	ArrayExpress	b24n057	b24n057_norm.txt	Bpe198	"Dutch post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n057	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n057	b24n057.txt	P-MEXP-9965	ArrayExpress	b24n057	b24n057_norm.txt	Bpe198	"Dutch post-vaccine"
+Bpe203	whole_organism	"Bordetella pertussis"	Bpe203	P-MEXP-9739	ArrayExpress	Bpe203	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe203	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bpe203 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n110	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n110	b29n110.txt	P-MEXP-9965	ArrayExpress	b29n110	b29n110_norm.txt	Bpe203	"Dutch post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b29n110	A-MEXP-236	P-MEXP-9747	ArrayExpress	b29n110	b29n110.txt	P-MEXP-9965	ArrayExpress	b29n110	b29n110_norm.txt	Bpe203	"Dutch post-vaccine"
+Bpe199	whole_organism	"Bordetella pertussis"	Bpe199	P-MEXP-9739	ArrayExpress	Bpe199	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe199	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bpe199 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n058	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n058	b24n058.txt	P-MEXP-9965	ArrayExpress	b24n058	b24n058_norm.txt	Bpe199	"Dutch post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n058	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n058	b24n058.txt	P-MEXP-9965	ArrayExpress	b24n058	b24n058_norm.txt	Bpe199	"Dutch post-vaccine"
+Bpe200	whole_organism	"Bordetella pertussis"	Bpe200	P-MEXP-9739	ArrayExpress	Bpe200	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe200	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bpe200 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n059	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n059	b24n059.txt	P-MEXP-9965v	ArrayExpress	b24n059	b24n059_norm.txt	Bpe200	"Dutch post-vaccine"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n059	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n059	b24n059.txt	P-MEXP-9965	ArrayExpress	b24n059	b24n059_norm.txt	Bpe200	"Dutch post-vaccine"
+WCH01	whole_organism	"Bordetella pertussis"	WCH01	P-MEXP-9739	ArrayExpress	WCH01	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	WCH01	genomic_DNA	P-MEXP-9745	ArrayExpress	"WCH01 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n086	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n086	b23n086.txt	P-MEXP-9965	ArrayExpress	b23n086	b23n086_norm.txt	WCH01	Australian
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n086	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n086	b23n086.txt	P-MEXP-9965	ArrayExpress	b23n086	b23n086_norm.txt	WCH01	Australian
+WCH02	whole_organism	"Bordetella pertussis"	WCH02	P-MEXP-9739	ArrayExpress	WCH02	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	WCH02	genomic_DNA	P-MEXP-9745	ArrayExpress	"WCH02 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n087	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n087	b23n087.txt	P-MEXP-9965	ArrayExpress	b23n087	b23n087_norm.txt	WCH02	Australian
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n087	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n087	b23n087.txt	P-MEXP-9965	ArrayExpress	b23n087	b23n087_norm.txt	WCH02	Australian
+WCH03	whole_organism	"Bordetella pertussis"	WCH03	P-MEXP-9739	ArrayExpress	WCH03	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	WCH03	genomic_DNA	P-MEXP-9745	ArrayExpress	"WCH03 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n088	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n088	b23n088.txt	P-MEXP-9965	ArrayExpress	b23n088	b23n088_norm.txt	WCH03	Australian
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n088	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n088	b23n088.txt	P-MEXP-9965	ArrayExpress	b23n088	b23n088_norm.txt	WCH03	Australian
+WCH04	whole_organism	"Bordetella pertussis"	WCH04	P-MEXP-9739	ArrayExpress	WCH04	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	WCH04	genomic_DNA	P-MEXP-9745	ArrayExpress	"WCH04 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n089	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n089	b23n089.txt	P-MEXP-9965	ArrayExpress	b23n089	b23n089_norm.txt	WCH04	Australian
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n089	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n089	b23n089.txt	P-MEXP-9965	ArrayExpress	b23n089	b23n089_norm.txt	WCH04	Australian
+WCH07	whole_organism	"Bordetella pertussis"	WCH07	P-MEXP-9739	ArrayExpress	WCH07	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	WCH07	genomic_DNA	P-MEXP-9745	ArrayExpress	"WCH07 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n090	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n090	b23n090.txt	P-MEXP-9965	ArrayExpress	b23n090	b23n090_norm.txt	WCH07	Australian
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n090	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n090	b23n090.txt	P-MEXP-9965	ArrayExpress	b23n090	b23n090_norm.txt	WCH07	Australian
+WCH08	whole_organism	"Bordetella pertussis"	WCH08	P-MEXP-9739	ArrayExpress	WCH08	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	WCH08	genomic_DNA	P-MEXP-9745	ArrayExpress	"WCH08 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n091	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n091	b23n091.txt	P-MEXP-9965	ArrayExpress	b23n091	b23n091_norm.txt	WCH08	Australian
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n091	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n091	b23n091.txt	P-MEXP-9965	ArrayExpress	b23n091	b23n091_norm.txt	WCH08	Australian
+WCH23	whole_organism	"Bordetella pertussis"	WCH23	P-MEXP-9739	ArrayExpress	WCH23	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	WCH23	genomic_DNA	P-MEXP-9745	ArrayExpress	"WCH23 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n092	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n092	b23n092.txt	P-MEXP-9965	ArrayExpress	b23n092	b23n092_norm.txt	WCH23	Australian
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n092	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n092	b23n092.txt	P-MEXP-9965	ArrayExpress	b23n092	b23n092_norm.txt	WCH23	Australian
+WCH05	whole_organism	"Bordetella pertussis"	WCH05	P-MEXP-9739	ArrayExpress	WCH05	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	WCH05	genomic_DNA	P-MEXP-9745	ArrayExpress	"WCH05 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n085	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n085	b24n085.txt	P-MEXP-9965	ArrayExpress	b24n085	b24n085_norm.txt	WCH05	Australian
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n085	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n085	b24n085.txt	P-MEXP-9965	ArrayExpress	b24n085	b24n085_norm.txt	WCH05	Australian
+WCH06	whole_organism	"Bordetella pertussis"	WCH06	P-MEXP-9739	ArrayExpress	WCH06	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	WCH06	genomic_DNA	P-MEXP-9745	ArrayExpress	"WCH06 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n086	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n086	b24n086.txt	P-MEXP-9965	ArrayExpress	b24n086	b24n086_norm.txt	WCH06	Australian
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n086	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n086	b24n086.txt	P-MEXP-9965	ArrayExpress	b24n086	b24n086_norm.txt	WCH06	Australian
+WCH11	whole_organism	"Bordetella pertussis"	WCH11	P-MEXP-9739	ArrayExpress	WCH11	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	WCH11	genomic_DNA	P-MEXP-9745	ArrayExpress	"WCH11 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n087	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n087	b24n087.txt	P-MEXP-9965	ArrayExpress	b24n087	b24n087_norm.txt	WCH11	Australian
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n087	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n087	b24n087.txt	P-MEXP-9965	ArrayExpress	b24n087	b24n087_norm.txt	WCH11	Australian
+WCH12	whole_organism	"Bordetella pertussis"	WCH12	P-MEXP-9739	ArrayExpress	WCH12	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	WCH12	genomic_DNA	P-MEXP-9745	ArrayExpress	"WCH12 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n088	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n088	b24n088.txt	P-MEXP-9965	ArrayExpress	b24n088	b24n088_norm.txt	WCH12	Australian
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n088	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n088	b24n088.txt	P-MEXP-9965	ArrayExpress	b24n088	b24n088_norm.txt	WCH12	Australian
+WCH19	whole_organism	"Bordetella pertussis"	WCH19	P-MEXP-9739	ArrayExpress	WCH19	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	WCH19	genomic_DNA	P-MEXP-9745	ArrayExpress	"WCH19 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n089	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n089	b24n089.txt	P-MEXP-9965	ArrayExpress	b24n089	b24n089_norm.txt	WCH19	Australian
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n089	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n089	b24n089.txt	P-MEXP-9965	ArrayExpress	b24n089	b24n089_norm.txt	WCH19	Australian
+WCH20	whole_organism	"Bordetella pertussis"	WCH20	P-MEXP-9739	ArrayExpress	WCH20	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	WCH20	genomic_DNA	P-MEXP-9745	ArrayExpress	"WCH20 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n090	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n090	b24n090.txt	P-MEXP-9965	ArrayExpress	b24n090	b24n090_norm.txt	WCH20	Australian
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n090	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n090	b24n090.txt	P-MEXP-9965	ArrayExpress	b24n090	b24n090_norm.txt	WCH20	Australian
+WCH21	whole_organism	"Bordetella pertussis"	WCH21	P-MEXP-9739	ArrayExpress	WCH21	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	WCH21	genomic_DNA	P-MEXP-9745	ArrayExpress	"WCH21 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n091	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n091	b24n091.txt	P-MEXP-9965	ArrayExpress	b24n091	b24n091_norm.txt	WCH21	Australian
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n091	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n091	b24n091.txt	P-MEXP-9965	ArrayExpress	b24n091	b24n091_norm.txt	WCH21	Australian
+WCH22	whole_organism	"Bordetella pertussis"	WCH22	P-MEXP-9739	ArrayExpress	WCH22	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	WCH22	genomic_DNA	P-MEXP-9745	ArrayExpress	"WCH22 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n092	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n092	b24n092.txt	P-MEXP-9965	ArrayExpress	b24n092	b24n092_norm.txt	WCH22	Australian
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n092	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n092	b24n092.txt	P-MEXP-9965	ArrayExpress	b24n092	b24n092_norm.txt	WCH22	Australian
+WCH24	whole_organism	"Bordetella pertussis"	WCH24	P-MEXP-9739	ArrayExpress	WCH24	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	WCH24	genomic_DNA	P-MEXP-9745	ArrayExpress	"WCH24 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n093	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n093	b24n093.txt	P-MEXP-9965	ArrayExpress	b24n093	b24n093_norm.txt	WCH24	Australian
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n093	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n093	b24n093.txt	P-MEXP-9965	ArrayExpress	b24n093	b24n093_norm.txt	WCH24	Australian
+3588	whole_organism	"Bordetella pertussis"	3588	P-MEXP-9739	ArrayExpress	3588	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	3588	genomic_DNA	P-MEXP-9745	ArrayExpress	"3588 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n043	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n043	b23n043.txt	P-MEXP-9965	ArrayExpress	b23n043	b23n043_norm.txt	3588	"Italian (unvaccinated children)"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n043	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n043	b23n043.txt	P-MEXP-9965	ArrayExpress	b23n043	b23n043_norm.txt	3588	"Italian (unvaccinat [...]
+10641	whole_organism	"Bordetella pertussis"	10641	P-MEXP-9739	ArrayExpress	10641	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	10641	genomic_DNA	P-MEXP-9745	ArrayExpress	"10641 Cy3"	synthetic_DvNA	Cy3	P-MEXP-9746	ArrayExpress	b23n044	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n044	b23n044.txt	P-MEXP-9965	ArrayExpress	b23n044	b23n044_norm.txt	10641	"Italian (unvaccinated children)"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n044	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n044	b23n044.txt	P-MEXP-9965	ArrayExpress	b23n044	b23n044_norm.txt	10641	"Italian (unvaccina [...]
+15550	whole_organism	"Bordetella pertussis"	15550	P-MEXP-9739	ArrayExpress	15550	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	15550	genomic_DNA	P-MEXP-9745	ArrayExpress	"15550 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n047	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n047	b23n047.txt	P-MEXP-9965	ArrayExpress	b23n047	b23n047_norm.txt	15550	"Italian (unvaccinated children)"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n047	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n047	b23n047.txt	P-MEXP-9965	ArrayExpress	b23n047	b23n047_norm.txt	15550	"Italian (unvaccina [...]
+9859	whole_organism	"Bordetella pertussis"	9859	P-MEXP-9739	ArrayExpress	9859	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	9859	genomic_DNA	P-MEXP-9745	ArrayExpress	"9859 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n051	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n051	b23n051.txt	P-MEXP-9965	ArrayExpress	b23n051	b23n051_norm.txt	9859	"Italian (unvaccinated children)"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n051	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n051	b23n051.txt	P-MEXP-9965	ArrayExpress	b23n051	b23n051_norm.txt	9859	"Italian (unvaccinat [...]
+4558	whole_organism	"Bordetella pertussis"	4558	P-MEXP-9739	ArrayExpress	4558	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	4558	genomic_DNA	P-MEXP-9745	ArrayExpress	"4558 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n058	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n058	b23n058.txt	P-MEXP-9965	ArrayExpress	b23n058	b23n058_norm.txt	4558	"Italian (unvaccinated children)"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n058	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n058	b23n058.txt	P-MEXP-9965	ArrayExpress	b23n058	b23n058_norm.txt	4558	"Italian (unvaccinat [...]
+Bp01568	whole_organism	"Bordetella pertussis"	Bp01568	P-MEXP-9739	ArrayExpress	Bp01568	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bp01568	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bp01568 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n011	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n011	b24n011.txt	P-MEXP-9965	ArrayExpress	b24n011	b24n011_norm.txt	Bp01568	"Italian (unvaccinated children)"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n011	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n011	b24n011.txt	P-MEXP-9965	ArrayExpress	b24n011	b24n011_norm.txt	Bp01568	"Italian (unvacci [...]
+Bp12892	whole_organism	"Bordetella pertussis"	Bp12892	P-MEXP-9739	ArrayExpress	Bp12892	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bp12892	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bp12892 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n012	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n012	b24n012.txt	P-MEXP-9965	ArrayExpress	b24n012	b24n012_norm.txt	Bp12892	"Italian (unvaccinated children)"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n012	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n012	b24n012.txt	P-MEXP-9965	ArrayExpress	b24n012	b24n012_norm.txt	Bp12892	"Italian (unvacci [...]
+Bp05275	whole_organism	"Bordetella pertussis"	Bp05275	P-MEXP-9739	ArrayExpress	Bp05275	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bp05275	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bp05275 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n013	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n013	b24n013.txt	P-MEXP-9965	ArrayExpress	b24n013	b24n013_norm.txt	Bp05275	"Italian (unvaccinated children)"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n013	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n013	b24n013.txt	P-MEXP-9965	ArrayExpress	b24n013	b24n013_norm.txt	Bp05275	"Italian (unvacci [...]
+Bp13732	whole_organism	"Bordetella pertussis"	Bp13732	P-MEXP-9739	ArrayExpress	Bp13732	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bp13732	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bp13732 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n080	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n080	b24n080.txt	P-MEXP-9965	ArrayExpress	b24n080	b24n080_norm.txt	Bp13732	"Italian (unvaccinated children)"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n080	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n080	b24n080.txt	P-MEXP-9965	ArrayExpress	b24n080	b24n080_norm.txt	Bp13732	"Italian (unvacci [...]
+Bp12262	whole_organism	"Bordetella pertussis"	Bp12262	P-MEXP-9739	ArrayExpress	Bp12262	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bp12262	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bp12262 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n082	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n082	b24n082.txt	P-MEXP-9965	ArrayExpress	b24n082	b24n082_norm.txt	Bp12262	"Italian (unvaccinated children)"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n082	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n082	b24n082.txt	P-MEXP-9965	ArrayExpress	b24n082	b24n082_norm.txt	Bp12262	"Italian (unvacci [...]
+Bp15622	whole_organism	"Bordetella pertussis"	Bp15622	P-MEXP-9739	ArrayExpress	Bp15622	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bp15622	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bp15622 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n083	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n083	b24n083.txt	P-MEXP-9965	ArrayExpress	b24n083	b24n083_norm.txt	Bp15622	"Italian (unvaccinated children)"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n083	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n083	b24n083.txt	P-MEXP-9965	ArrayExpress	b24n083	b24n083_norm.txt	Bp15622	"Italian (unvacci [...]
+3520	whole_organism	"Bordetella pertussis"	3520	P-MEXP-9739	ArrayExpress	3520	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	3520	genomic_DNA	P-MEXP-9745	ArrayExpress	"3520 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n041	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n041	b23n041.txt	P-MEXP-9965	ArrayExpress	b23n041	b23n041_norm.txt	3520	"Italian (vaccinated children)"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n041	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n041	b23n041.txt	P-MEXP-9965	ArrayExpress	b23n041	b23n041_norm.txt	3520	"Italian (vaccinated [...]
+9878	whole_organism	"Bordetella pertussis"	9878	P-MEXP-9739	ArrayExpress	9878	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	9878	genomic_DNA	P-MEXP-9745	ArrayExpress	"9878 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n042	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n042	b23n042.txt	P-MEXP-9965	ArrayExpress	b23n042	b23n042_norm.txt	9878	"Italian (vaccinated children)"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n042	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n042	b23n042.txt	P-MEXP-9965	ArrayExpress	b23n042	b23n042_norm.txt	9878	"Italian (vaccinated [...]
+15637	whole_organism	"Bordetella pertussis"	15637	P-MEXP-9739	ArrayExpress	15637	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	15637	genomic_DNA	P-MEXP-9745	ArrayExpress	"15637 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n050	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n050	b23n050.txt	P-MEXP-9965	ArrayExpress	b23n050	b23n050_norm.txt	15637	"Italian (vaccinated children)"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n050	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n050	b23n050.txt	P-MEXP-9965	ArrayExpress	b23n050	b23n050_norm.txt	15637	"Italian (vaccinate [...]
+6453	whole_organism	"Bordetella pertussis"	6453	P-MEXP-9739	ArrayExpress	6453	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	6453	genomic_DNA	P-MEXP-9745	ArrayExpress	"6453 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n052	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n052	b23n052.txt	P-MEXP-9965	ArrayExpress	b23n052	b23n052_norm.txt	6453	"Italian (vaccinated children)"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n052	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n052	b23n052.txt	P-MEXP-9965	ArrayExpress	b23n052	b23n052_norm.txt	6453	"Italian (vaccinated [...]
+9585	whole_organism	"Bordetella pertussis"	9585	P-MEXP-9739	ArrayExpress	9585	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	9585	genomic_DNA	P-MEXP-9745	ArrayExpress	"9585 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n056	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n056	b23n056.txt	P-MEXP-9965	ArrayExpress	b23n056	b23n056_norm.txt	9585	"Italian (vaccinated children)"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n056	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n056	b23n056.txt	P-MEXP-9965	ArrayExpress	b23n056	b23n056_norm.txt	9585	"Italian (vaccinated [...]
+14782	whole_organism	"Bordetella pertussis"	14782	P-MEXP-9739	ArrayExpress	14782	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	14782	genomic_DNA	P-MEXP-9745	ArrayExpress	"14782 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n057	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n057	b23n057.txt	P-MEXP-9965	ArrayExpress	b23n057	b23n057_norm.txt	14782	"Italian (vaccinated children)"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n057	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n057	b23n057.txt	P-MEXP-9965	ArrayExpress	b23n057	b23n057_norm.txt	14782	"Italian (vaccinate [...]
+12464	whole_organism	"Bordetella pertussis"	12464	P-MEXP-9739	ArrayExpress	12464	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	12464	genomic_DNA	P-MEXP-9745	ArrayExpress	"12464 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n059	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n059	b23n059.txt	P-MEXP-9965	ArrayExpress	b23n059	b23n059_norm.txt	12464	"Italian (vaccinated children)"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n059	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n059	b23n059.txt	P-MEXP-9965	ArrayExpress	b23n059	b23n059_norm.txt	12464	"Italian (vaccinate [...]
+Bp01994	whole_organism	"Bordetella pertussis"	Bp01994	P-MEXP-9739	ArrayExpress	Bp01994	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bp01994	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bp01994 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n076	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n076	b24n076.txt	P-MEXP-9965	ArrayExpress	b24n076	b24n076_norm.txt	Bp01994	"Italian (vaccinated children)"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n076	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n076	b24n076.txt	P-MEXP-9965	ArrayExpress	b24n076	b24n076_norm.txt	Bp01994	"Italian (vaccina [...]
+Bp03975	whole_organism	"Bordetella pertussis"	Bp03975	P-MEXP-9739	ArrayExpress	Bp03975	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bp03975	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bp03975 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n081	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n081	b24n081.txt	P-MEXP-9965	ArrayExpress	b24n081	b24n081_norm.txt	Bp03975	"Italian (vaccinated children)"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n081	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n081	b24n081.txt	P-MEXP-9965	ArrayExpress	b24n081	b24n081_norm.txt	Bp03975	"Italian (vaccina [...]
+Bp04844	whole_organism	"Bordetella pertussis"	Bp04844	P-MEXP-9739	ArrayExpress	Bp04844	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bp04844	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bp04844 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n084	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n084	b24n084.txt	P-MEXP-9965	ArrayExpress	b24n084	b24n084_norm.txt	Bp04844	"Italian (vaccinated children)"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b24n084	A-MEXP-234	P-MEXP-9747	ArrayExpress	b24n084	b24n084.txt	P-MEXP-9965	ArrayExpress	b24n084	b24n084_norm.txt	Bp04844	"Italian (vaccina [...]
+Bpe53	whole_organism	"Bordetella pertussis"	Bpe53	P-MEXP-9739	ArrayExpress	Bpe53	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	Bpe53	genomic_DNA	P-MEXP-9745	ArrayExpress	"Bpe53 Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n093	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n093	b23n093.txt	P-MEXP-9965	ArrayExpress	b23n093	b23n093_norm.txt	Bpe53	"18323 laboratory strain"
+"Bordetella reference strains"	whole_organism	"B. bronchiseptica + B. parapertussis + B. pertussis"	"Bordetella reference strains"	P-MEXP-9739	ArrayExpress	"DNA reference"	whole_organism	P-MEXP-9744	ArrayExpress	P-MEXP-9958	ArrayExpress	"DNA reference"	genomic_DNA	P-MEXP-9745	ArrayExpress	"DNA reference Cy3"	synthetic_DNA	Cy3	P-MEXP-9746	ArrayExpress	b23n093	A-MEXP-234	P-MEXP-9747	ArrayExpress	b23n093	b23n093.txt	P-MEXP-9965	ArrayExpress	b23n093	b23n093_norm.txt	Bpe53	"18323 laboratory strain"
diff --git a/examples/magetab/real/E-TABM-66.idf b/examples/magetab/real/E-TABM-66.idf
new file mode 100644
index 0000000..1fd8320
--- /dev/null
+++ b/examples/magetab/real/E-TABM-66.idf
@@ -0,0 +1,29 @@
+"Investigation Title"	"Comparison of normal and malignant human breast epithelial cells"						
+"Experimental Design"	cell_type_comparison_design						
+"Experimental Factor Name"	DISEASESTATE	CELLTYPE					
+"Experimental Factor Type"	disease_state	cell_type					
+							
+"Person Last Name"	Grigoriadis						
+"Person First Name"	Anita						
+"Person Mid Initial"							
+"Person Affiliation"							
+"Person Roles"	submitter						
+							
+"Public Release Date"							
+							
+Comment[ArrayExpressSubmissionDate]							
+							
+"Publication Author List"							
+"Publication Title"							
+"Experiment Description"	"Multiple genome-wide microarrays have been used to analyse the transcriptomes of immunomagnetically separated normal human luminal epithelial and myoepithelial cells, as well as primary malignant breast epithelial cells."						
+							
+"Protocol Name"	P-MEXP-10843	P-MEXP-10844	P-MEXP-10845	P-TABM-AnitaG-10115	P-MEXP-14985	P-MEXP-14987	P-MEXP-14988
+"Protocol Type"	pool	pool	pool	labeling	labeling	hybridization	image_acquisition
+"Protocol Description"	"100 ug total RNA of 352,453,463,621,688,697,723,729,794,1098 were combined."	"100 ug total RNA of 723,729,697,822,1016,1031,1040,1053,1123,1130 were combined."	"50ug total RNA of 489,552,593,695,725,842,845,896,983,1000,1033,1045,1052,1062,1063 were combined"	"1. One round of amplification through to dye coupling reaction performed according to Amino Allyl MessageAmp aRNA Kit protocol (Ambion, cat #1752   http://www.ambion.com/techlib/prot/fm_1752.pdf ). Cy3 monor [...]
+"Protocol Parameters"							
+"Protocol Software"							
+							
+"SDRF File"	E-TABM-66_sdrf.txt						
+							
+"Term Source Name"	ArrayExpress						
+"Term Source File"	http://www.ebi.ac.uk/arrayexpress/						
diff --git a/examples/magetab/real/E-TABM-66_sdrf.txt b/examples/magetab/real/E-TABM-66_sdrf.txt
new file mode 100644
index 0000000..025e89b
--- /dev/null
+++ b/examples/magetab/real/E-TABM-66_sdrf.txt
@@ -0,0 +1,154 @@
+"Source Name"	"Material Type"	"Characteristics [Age]"	"Characteristics [BioSourceProvider]"	"Characteristics [CellType]"	"Characteristics [ClinicalHistory]"	"Characteristics [DevelopmentalStage]"	"Characteristics [DiseaseState]"	"Characteristics [Individual]"	"Characteristics [OrganismPart]"	"Characteristics [Organism]"	"Characteristics [Sex]"	"Characteristics [TimeUnit]"	"Protocol REF"	"Term Source REF"	"Sample Name"	"Material Type"	"Protocol REF"	"Term Source REF"	"Protocol REF"	"Extra [...]
+SAMPLE_human_1016	organism_part	30	"St. Anthony's Hospital, London,UK"	"luminal epithelial"	"Reduction mammoplasties"	adult	normal	Human_1016	"mammary gland"	"Homo sapiens"	female	years	P-MEXP-975	ArrayExpress	SAMPLE_human_1016	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10844	E_luminal_pool	total_RNA	P-TABM-AnitaG-10115		"L_pool_cy3_agil2"	synthetic_DNA	Cy3	P-MEXP-6162	ArrayExpress	L_pool_cy3_agil2	A-MEXP-218	P-MEXP-10145	ArrayExpress	L_pool_cy3_agil2	251239125464.gpr				"luminal epith [...]
+SAMPLE_human_1031	organism_part	33	"St. Elisabeth Hospital, London,UK"	"luminal epithelial"	"Reduction mammoplasties"	adult	normal	Human_1031	"mammary gland"	"Homo sapiens"	female	years	P-MEXP-975	ArrayExpress	SAMPLE_human_1031	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10844	E_luminal_pool	total_RNA	P-TABM-AnitaG-10115		"L_pool_cy3_agil3"	synthetic_DNA	Cy3	P-MEXP-6162	ArrayExpress	L_pool_cy3_agil3	A-MEXP-218	P-MEXP-10145	ArrayExpress	L_pool_cy3_agil3	251239125951.gpr				"luminal epith [...]
+SAMPLE_human_1040	organism_part	33	"Middlesex Hospital, London,UK"	"luminal epithelial"	"Reduction mammoplasties"	adult	normal	Human_1040	"mammary gland"	"Homo sapiens"	female	years	P-MEXP-975	ArrayExpress	SAMPLE_human_1040	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10844	E_luminal_pool	total_RNA	P-TABM-AnitaG-10115		"L_pool_cy5_agil1"	synthetic_DNA	Cy5	P-MEXP-6162	ArrayExpress	L_pool_cy5_agil1	A-MEXP-218	P-MEXP-10145	ArrayExpress	L_pool_cy5_agil1	251239119797.gpr				"luminal epithelia [...]
+SAMPLE_human_1053	organism_part	22	"Ashstead Hospital, London,UK"	"luminal epithelial"	"Reduction mammoplasties"	adult	normal	Human_1053	"mammary gland"	"Homo sapiens"	female	years	P-MEXP-975	ArrayExpress	SAMPLE_human_1053	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10844	E_luminal_pool	total_RNA	P-TABM-AnitaG-10115		"L_pool_cy5_agil2"	synthetic_DNA	Cy5	P-MEXP-6162	ArrayExpress	L_pool_cy5_agil2	A-MEXP-218	P-MEXP-10145	ArrayExpress	L_pool_cy5_agil2	251239125237.gpr				"luminal epithelial [...]
+SAMPLE_human_1123	organism_part	32	"University College London Hospital, London,UK"	"luminal epithelial"	"Reduction mammoplasties"	adult	normal	Human_1123	"mammary gland"	"Homo sapiens"	female	years	P-MEXP-975	ArrayExpress	SAMPLE_human_1123	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10844	E_luminal_pool	total_RNA	P-TABM-AnitaG-10115		"L_pool_cy5_agil3"	synthetic_DNA	Cy5	P-MEXP-6162	ArrayExpress	L_pool_cy5_agil3	A-MEXP-218	P-MEXP-10145	ArrayExpress	L_pool_cy5_agil3	251239125466.gpr				"l [...]
+SAMPLE_human_1130	organism_part	32	"University College London Hospital, London,UK"	"luminal epithelial"	"Reduction mammoplasties"	adult	normal	Human_1130	"mammary gland"	"Homo sapiens"	female	years	P-MEXP-975	ArrayExpress	SAMPLE_human_1130	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10844	E_luminal_pool	total_RNA	P-TABM-AnitaG-10115		"L_pool_cy3_breakthrough1"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	L_pool_cy3_breakthrough1	A-MEXP-259	P-MEXP-10145	ArrayExpress	L_pool_cy3_breakthroug [...]
+SAMPLE_human_697L	organism_part	29	"St. George's Hospital, London,UK"	"luminal epithelial"	"Reduction mammoplasties"	adult	normal	Human_697L	"mammary gland"	"Homo sapiens"	female	years	P-MEXP-975	ArrayExpress	SAMPLE_human_697L	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10844	E_luminal_pool	total_RNA	P-AFFY-2	ArrayExpress	"L_pool_affy3 biotin"	synthetic_DNA	biotin			L_pool_affy3	A-AFFY-44			L_pool_affy3	L3.CEL	L3.EXP			"luminal epithelial"	normal
+SAMPLE_human_723L	organism_part	32	"St. George's Hospital, London,UK"	"luminal epithelial"	"Reduction mammoplasties"	adult	normal	Human_723L	"mammary gland"	"Homo sapiens"	female	years	P-MEXP-975	ArrayExpress	SAMPLE_human_723L	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10844	E_luminal_pool	total_RNA	P-AFFY-2	ArrayExpress	"L_pool_affy1 biotin"	synthetic_DNA	biotin			L_pool_affy1	A-AFFY-44			L_pool_affy1	L1.CEL	L1.EXP			"luminal epithelial"	normal
+SAMPLE_human_729L	organism_part	31	"St. George's Hospital, London,UK"	"luminal epithelial"	"Reduction mammoplasties"	adult	normal	Human_729L	"mammary gland"	"Homo sapiens"	female	years	P-MEXP-975	ArrayExpress	SAMPLE_human_729L	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10844	E_luminal_pool	total_RNA	P-AFFY-2	ArrayExpress	"L_pool_affy2 biotin"	synthetic_DNA	biotin			L_pool_affy2	A-AFFY-44			L_pool_affy2	L2.CEL	L2.EXP			"luminal epithelial"	normal
+SAMPLE_human_822	organism_part		"Royal Free Hospital, London,UK"	"luminal epithelial"	"Reduction mammoplasties"	adult	normal	Human_822	"mammary gland"	"Homo sapiens"	female		P-MEXP-975	ArrayExpress	SAMPLE_human_822	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10844	E_luminal_pool	total_RNA	P-TABM-AnitaG-10115		"L_pool_cy3_agil1"	synthetic_DNA	Cy3	P-MEXP-6162	ArrayExpress	L_pool_cy3_agil1	A-MEXP-218	P-MEXP-10145	ArrayExpress	L_pool_cy3_agil1	251239114659.gpr				"luminal epithelial"	normal
+																				E_luminal_pool	total_RNA	P-MEXP-14985		"L_pool_amersham1"	synthetic_DNA	Cy5	P-MEXP-14987		L_pool_amersham1	A-GEHB-1	P-MEXP-14988		L_pool_amersham1			T00297797	T00297797.txt	"luminal epithelial"	normal
+																				E_luminal_pool	total_RNA	P-MEXP-14985		"L_pool_amersham2"	synthetic_DNA	Cy5	P-MEXP-14987		L_pool_amersham2	A-GEHB-1	P-MEXP-14988		L_pool_amersham2			T00297799	T00297799.txt	"luminal epithelial"	normal
+																				E_luminal_pool	total_RNA	P-TABM-AnitaG-10115		"L_pool_cy5_breakthrough2"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	L_pool_cy5_breakthrough2	A-MEXP-259	P-MEXP-10145	ArrayExpress	L_pool_cy5_breakthrough2	90000340.gpr				"luminal epithelial"	normal
+																				E_luminal_pool	total_RNA	P-TABM-AnitaG-10115		"L_pool_cy5_breakthrough3"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	L_pool_cy5_breakthrough3	A-MEXP-259	P-MEXP-10145	ArrayExpress	L_pool_cy5_breakthrough3	90000418.gpr				"luminal epithelial"	normal
+																				E_luminal_pool	total_RNA	P-MEXP-14985		"L_pool_amersham3"	synthetic_DNA	Cy5	P-MEXP-14987		L_pool_amersham3	A-GEHB-1	P-MEXP-14988		L_pool_amersham3			T00297800	T00297800.txt	"luminal epithelial"	normal
+																				E_luminal_pool	total_RNA	P-TABM-AnitaG-10115		"L_pool_cy3_breakthrough3"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	L_pool_cy3_breakthrough3	A-MEXP-259	P-MEXP-10145	ArrayExpress	L_pool_cy3_breakthrough3	90000421.gpr				"luminal epithelial"	normal
+																				E_luminal_pool	total_RNA	P-TABM-AnitaG-10115		"L_pool_cy3_breakthrough2"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	L_pool_cy3_breakthrough2	A-MEXP-259	P-MEXP-10145	ArrayExpress	L_pool_cy3_breakthrough2	90000343.gpr				"luminal epithelial"	normal
+																				E_luminal_pool	total_RNA	P-MEXP-14985		"L_pool_amersham4"	synthetic_DNA	Cy5	P-MEXP-14987		L_pool_amersham4	A-GEHB-1	P-MEXP-14988		L_pool_amersham4			T00297801	T00297801.txt	"luminal epithelial"	normal
+																				E_luminal_pool	total_RNA	P-TABM-AnitaG-10115		"L_pool_cy5_breakthrough1"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	L_pool_cy5_breakthrough1	A-MEXP-259	P-MEXP-10145	ArrayExpress	L_pool_cy5_breakthrough1	90000310.gpr				"luminal epithelial"	normal
+																				E_luminal_pool	total_RNA	P-TABM-AnitaG-10115		"2nd_L_pool_L_cy5_breakthrough1"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	2nd_L_pool_cy5_breakthrough1	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_L_pool_cy5_breakthrough1	95000067.gpr				luminal	normal
+																				E_luminal_pool	total_RNA	P-TABM-AnitaG-10115		"2nd_L_pool_L_cy3_breakthrough4"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	2nd_L_pool_cy3_breakthrough4	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_L_pool_cy3_breakthrough4	95000080.gpr				luminal	normal
+																				E_luminal_pool	total_RNA	P-TABM-AnitaG-10115		"2nd_L_pool_L_cy3_breakthrough3"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	2nd_L_pool_cy3_breakthrough3	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_L_pool_cy3_breakthrough3	95000079.gpr				luminal	normal
+																				E_luminal_pool	total_RNA	P-TABM-AnitaG-10115		"2nd_L_pool_L_cy5_breakthrough4"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	2nd_L_pool_cy5_breakthrough4	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_L_pool_cy5_breakthrough4	95000074.gpr				luminal	normal
+																				E_luminal_pool	total_RNA	P-TABM-AnitaG-10115		"2nd_L_pool_L_cy5_breakthrough3"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	2nd_L_pool_cy5_breakthrough3	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_L_pool_cy5_breakthrough3	95000073.gpr				luminal	normal
+																				E_luminal_pool	total_RNA	P-TABM-AnitaG-10115		"2nd_L_pool_L_cy5_breakthrough2"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	2nd_L_pool_cy5_breakthrough2	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_L_pool_cy5_breakthrough2	95000068.gpr				luminal	normal
+																				E_luminal_pool	total_RNA	P-MEXP-14985		"2nd_L_pool_amersham1"	synthetic_DNA	Cy5	P-MEXP-14987		2nd_L_pool_amersham1	A-GEHB-1	P-MEXP-14988		2nd_L_pool_amersham1			T00253433	T00253433.txt	luminal	normal
+																				E_luminal_pool	total_RNA	P-MEXP-14985		"2nd_L_pool_amersham3"	synthetic_DNA	Cy5	P-MEXP-14987		2nd_L_pool_amersham3	A-GEHB-1	P-MEXP-14988		2nd_L_pool_amersham3			T00250228	T00250228.txt	luminal	normal
+																				E_luminal_pool	total_RNA	P-MEXP-14985		"2nd_L_pool_amersham2"	synthetic_DNA	Cy5	P-MEXP-14987		2nd_L_pool_amersham2	A-GEHB-1	P-MEXP-14988		2nd_L_pool_amersham2			T00253434	T00253434.txt	luminal	normal
+																				E_luminal_pool	total_RNA	P-TABM-AnitaG-10115		"2nd_L_pool_L_cy3_breakthrough2"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	2nd_L_pool_cy3_breakthrough2	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_L_pool_cy3_breakthrough2	95000062.gpr				luminal	normal
+																				E_luminal_pool	total_RNA	P-TABM-AnitaG-10115		"2nd_L_pool_L_cy3_breakthrough1"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	2nd_L_pool_cy3_breakthrough1	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_L_pool_cy3_breakthrough1	95000061.gpr				luminal	normal
+																				E_luminal_pool	total_RNA	P-MEXP-14985		"2nd_L_pool_amersham4"	synthetic_DNA	Cy5	P-MEXP-14987		2nd_L_pool_amersham4	A-GEHB-1	P-MEXP-14988		2nd_L_pool_amersham4			T00250229	T00250229.txt	luminal	normal
+SAMPLE_human_1098	organism_part	26	"Queen Victoria Hospital, London,UK"	myoepithelial	"Reduction mammoplasties"	adult	normal	Human_1098	"mammary gland"	"Homo sapiens"	female	years	P-MEXP-976	ArrayExpress	SAMPLE_human_1098	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10843	E_myoepithelial_pool	total_RNA	P-TABM-AnitaG-10115		"M_pool_cy5_agil3"	synthetic_DNA	Cy5	P-MEXP-6162	ArrayExpress	M_pool_cy5_agil3	A-MEXP-218	P-MEXP-10145	ArrayExpress	M_pool_cy5_agil3	251239125240.gpr				myoepithelial	normal
+SAMPLE_human_352	organism_part	30	"Queen Mary's University Hospital, London,UK"	myoepithelial	"Reduction mammoplasties"	adult	normal	Human_352	"mammary gland"	"Homo sapiens"	female	years	P-MEXP-976	ArrayExpress	SAMPLE_human_352	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10843	E_myoepithelial_pool	total_RNA	P-AFFY-2	ArrayExpress	"M_pool_affy1 biotin"	synthetic_DNA	biotin			M_pool_affy1	A-AFFY-44			M_pool_affy1	M1.CEL	M1.EXP			myoepithelial	normal
+SAMPLE_human_453	organism_part	22	"Queen Mary's University Hospital, London,UK"	myoepithelial	"Reduction mammoplasties"	adult	normal	Human_453	"mammary gland"	"Homo sapiens"	female	years	P-MEXP-976	ArrayExpress	SAMPLE_human_453	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10843	E_myoepithelial_pool	total_RNA	P-AFFY-2	ArrayExpress	"M_pool_affy2 biotin"	synthetic_DNA	biotin			M_pool_affy2	A-AFFY-44			M_pool_affy2	M2.CEL	M2.EXP			myoepithelial	normal
+SAMPLE_human_463	organism_part	31	"Queen Mary's University Hospital, London,UK"	myoepithelial	"Reduction mammoplasties"	adult	normal	Human_463	"mammary gland"	"Homo sapiens"	female	years	P-MEXP-976	ArrayExpress	SAMPLE_human_463	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10843	E_myoepithelial_pool	total_RNA	P-AFFY-2	ArrayExpress	"M_pool_affy3 biotin"	synthetic_DNA	biotin			M_pool_affy3	A-AFFY-44			M_pool_affy3	M3.CEL	M3.EXP			myoepithelial	normal
+SAMPLE_human_621	organism_part	38	"Middlesex University Hospital, London,UK"	myoepithelial	"Reduction mammoplasties"	adult	normal	Human_621	"mammary gland"	"Homo sapiens"	female	years	P-MEXP-976	ArrayExpress	SAMPLE_human_621	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10843	E_myoepithelial_pool	total_RNA	P-TABM-AnitaG-10115		"M_pool_cy3_agil1"	synthetic_DNA	Cy3	P-MEXP-6162	ArrayExpress	M_pool_cy3_agil1	A-MEXP-218	P-MEXP-10145	ArrayExpress	M_pool_cy3_agil1	251239114660.gpr				myoepitheli [...]
+SAMPLE_human_688	organism_part	26	"St. George's Hospital, London,UK"	myoepithelial	"Reduction mammoplasties"	adult	normal	Human_688	"mammary gland"	"Homo sapiens"	female	years	P-MEXP-976	ArrayExpress	SAMPLE_human_688	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10843	E_myoepithelial_pool	total_RNA	P-TABM-AnitaG-10115		"M_pool_cy3_agil2"	synthetic_DNA	Cy3	P-MEXP-6162	ArrayExpress	M_pool_cy3_agil2	A-MEXP-218	P-MEXP-10145	ArrayExpress	M_pool_cy3_agil2	251239125465.gpr				myoepithelial	normal
+SAMPLE_human_697	organism_part	29	"St. George's University Hospital, London,UK"	myoepithelial	"Reduction mammoplasties"	adult	normal	Human_697	"mammary gland"	"Homo sapiens"	female	years	P-MEXP-976	ArrayExpress	SAMPLE_human_697	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10843	E_myoepithelial_pool	total_RNA	P-TABM-AnitaG-10115		"M_pool_cy3_agil3"	synthetic_DNA	Cy3	P-MEXP-6162	ArrayExpress	M_pool_cy3_agil3	A-MEXP-218	P-MEXP-10145	ArrayExpress	M_pool_cy3_agil3	251239125953.gpr				myoepith [...]
+SAMPLE_human_723	organism_part	32	"St. George's University Hospital, London,UK"	myoepithelial	"Reduction mammoplasties"	adult	normal	Human_723	"mammary gland"	"Homo sapiens"	female	years	P-MEXP-976	ArrayExpress	SAMPLE_human_723	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10843	E_myoepithelial_pool	total_RNA	P-TABM-AnitaG-10115		"M_pool_cy5_agil1"	synthetic_DNA	Cy5	P-MEXP-6162	ArrayExpress	M_pool_cy5_agil1	A-MEXP-218	P-MEXP-10145	ArrayExpress	M_pool_cy5_agil1	251239119798.gpr				myoepith [...]
+SAMPLE_human_729	organism_part	31	"St. George's Hospital, London,UK"	myoepithelial	"Reduction mammoplasties"	adult	normal	Human_729	"mammary gland"	"Homo sapiens"	female	years	P-MEXP-976	ArrayExpress	SAMPLE_human_729	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10843	E_myoepithelial_pool	total_RNA	P-TABM-AnitaG-10115		"M_pool_cy5_agil2"	synthetic_DNA	Cy5	P-MEXP-6162	ArrayExpress	M_pool_cy5_agil2	A-MEXP-218	P-MEXP-10145	ArrayExpress	M_pool_cy5_agil2	251239125238.gpr				myoepithelial	normal
+SAMPLE_human_794	organism_part	39	"Ashstead Hospital, London,UK"	myoepithelial	"Reduction mammoplasties"	adult	normal	Human_794	"mammary gland"	"Homo sapiens"	female	years	P-MEXP-976	ArrayExpress	SAMPLE_human_794	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10843	E_myoepithelial_pool	total_RNA	P-TABM-AnitaG-10115		"M_pool_cy3_breakthrough1"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	M_pool_cy3_breakthrough1	A-MEXP-259	P-MEXP-10145	ArrayExpress	M_pool_cy3_breakthrough1	90000338.gpr				my [...]
+																				E_myoepithelial_pool	total_RNA	P-MEXP-14985		"2nd_M_pool_amersham1"	synthetic_DNA	Cy5	P-MEXP-14987		2nd_M_pool_amersham1	A-GEHB-1	P-MEXP-14988		2nd_M_pool_amersham1			T00253435	T00253435.txt	myoepithelial	normal
+																				E_myoepithelial_pool	total_RNA	P-TABM-AnitaG-10115		"2nd_M_pool_L_cy3_breakthrough3"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	2nd_M_pool_cy3_breakthrough3	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_M_pool_cy3_breakthrough3	95000081.gpr				myoepithelial	normal
+																				E_myoepithelial_pool	total_RNA	P-MEXP-14985		"M_pool_amersham4"	synthetic_DNA	Cy5	P-MEXP-14987		M_pool_amersham4	A-GEHB-1	P-MEXP-14988		M_pool_amersham4			T00297805	T00297805.txt	myoepithelial	normal
+																				E_myoepithelial_pool	total_RNA	P-TABM-AnitaG-10115		"M_pool_cy3_breakthrough3"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	M_pool_cy3_breakthrough3	A-MEXP-259	P-MEXP-10145	ArrayExpress	M_pool_cy3_breakthrough3	90000422.gpr				myoepithelial	normal
+																				E_myoepithelial_pool	total_RNA	P-MEXP-14985		"2nd_M_pool_amersham2"	synthetic_DNA	Cy5	P-MEXP-14987		2nd_M_pool_amersham2	A-GEHB-1	P-MEXP-14988		2nd_M_pool_amersham2			T00253436	T00253436.txt	myoepithelial	normal
+																				E_myoepithelial_pool	total_RNA	P-TABM-AnitaG-10115		"M_pool_cy3_breakthrough2"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	M_pool_cy3_breakthrough2	A-MEXP-259	P-MEXP-10145	ArrayExpress	M_pool_cy3_breakthrough2	90000344.gpr				myoepithelial	normal
+																				E_myoepithelial_pool	total_RNA	P-MEXP-14985		"M_pool_amersham3"	synthetic_DNA	Cy5	P-MEXP-14987		M_pool_amersham3	A-GEHB-1	P-MEXP-14988		M_pool_amersham3			T00297804	T00297804.txt	myoepithelial	normal
+																				E_myoepithelial_pool	total_RNA	P-TABM-AnitaG-10115		"2nd_M_pool_L_cy3_breakthrough1"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	2nd_M_pool_cy3_breakthrough1	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_M_pool_cy3_breakthrough1	95000063.gpr				myoepithelial	normal
+																				E_myoepithelial_pool	total_RNA	P-MEXP-14985		"M_pool_amersham2"	synthetic_DNA	Cy5	P-MEXP-14987		M_pool_amersham2	A-GEHB-1	P-MEXP-14988		M_pool_amersham2			T00297803	T00297803.txt	myoepithelial	normal
+																				E_myoepithelial_pool	total_RNA	P-MEXP-14985		"2nd_M_pool_amersham3"	synthetic_DNA	Cy5	P-MEXP-14987		2nd_M_pool_amersham3	A-GEHB-1	P-MEXP-14988		2nd_M_pool_amersham3			T00250230	T00250230.txt	myoepithelial	normal
+																				E_myoepithelial_pool	total_RNA	P-MEXP-14985		"2nd_M_pool_amersham4"	synthetic_DNA	Cy5	P-MEXP-14987		2nd_M_pool_amersham4	A-GEHB-1	P-MEXP-14988		2nd_M_pool_amersham4			T00250231	T00250231.txt	myoepithelial	normal
+																				E_myoepithelial_pool	total_RNA	P-TABM-AnitaG-10115		"2nd_M_pool_L_cy3_breakthrough2"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	2nd_M_pool_cy3_breakthrough2	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_M_pool_cy3_breakthrough2	95000064.gpr				myoepithelial	normal
+																				E_myoepithelial_pool	total_RNA	P-MEXP-14985		"M_pool_amersham1"	synthetic_DNA	Cy5	P-MEXP-14987		M_pool_amersham1	A-GEHB-1	P-MEXP-14988		M_pool_amersham1			T00297802	T00297802.txt	myoepithelial	normal
+																				E_myoepithelial_pool	total_RNA	P-TABM-AnitaG-10115		"M_pool_cy5_breakthrough2"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	M_pool_cy5_breakthrough2	A-MEXP-259	P-MEXP-10145	ArrayExpress	M_pool_cy5_breakthrough2	90000341.gpr				myoepithelial	normal
+																				E_myoepithelial_pool	total_RNA	P-TABM-AnitaG-10115		"2nd_M_pool_L_cy5_breakthrough2"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	2nd_M_pool_cy5_breakthrough2	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_M_pool_cy5_breakthrough2	95000070.gpr				myoepithelial	normal
+																				E_myoepithelial_pool	total_RNA	P-TABM-AnitaG-10115		"M_pool_cy5_breakthrough1"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	M_pool_cy5_breakthrough1	A-MEXP-259	P-MEXP-10145	ArrayExpress	M_pool_cy5_breakthrough1	90000311.gpr				myoepithelial	normal
+																				E_myoepithelial_pool	total_RNA	P-TABM-AnitaG-10115		"2nd_M_pool_L_cy5_breakthrough4"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	2nd_M_pool_cy5_breakthrough4	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_M_pool_cy5_breakthrough4	95000076.gpr				myoepithelial	normal
+																				E_myoepithelial_pool	total_RNA	P-TABM-AnitaG-10115		"2nd_M_pool_L_cy5_breakthrough3"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	2nd_M_pool_cy5_breakthrough3	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_M_pool_cy5_breakthrough3	95000075.gpr				myoepithelial	normal
+																				E_myoepithelial_pool	total_RNA	P-TABM-AnitaG-10115		"M_pool_cy5_breakthrough3"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	M_pool_cy5_breakthrough3	A-MEXP-259	P-MEXP-10145	ArrayExpress	M_pool_cy5_breakthrough3	90000419.gpr				myoepithelial	normal
+																				E_myoepithelial_pool	total_RNA	P-TABM-AnitaG-10115		"2nd_M_pool_L_cy3_breakthrough4"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	2nd_M_pool_cy3_breakthrough4	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_M_pool_cy3_breakthrough4	95000082.gpr				myoepithelial	normal
+																				E_myoepithelial_pool	total_RNA	P-TABM-AnitaG-10115		"2nd_M_pool_L_cy5_breakthrough1"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	2nd_M_pool_cy5_breakthrough1	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_M_pool_cy5_breakthrough1	95000069.gpr				myoepithelial	normal
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy3_breakthrough2"	synthetic_DNA	Cy3			T_pool_cy5_breakthrough2				T_pool_cy5_breakthrough2						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy3_agil3"	synthetic_DNA	Cy3			M_pool_cy5_agil3				M_pool_cy5_agil3						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy5_breakthrough1"	synthetic_DNA	Cy5			T_pool_cy3_breakthrough1				T_pool_cy3_breakthrough1						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy5_agil2"	synthetic_DNA	Cy5			T_pool_cy3_agil2				T_pool_cy3_agil2						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy5_agil3"	synthetic_DNA	Cy5			T_pool_cy3_agil3				T_pool_cy3_agil3						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy3_agil1"	synthetic_DNA	Cy3			T_pool_cy5_agil1				T_pool_cy5_agil1						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy3_breakthrough2"	synthetic_DNA	Cy3			L_pool_cy5_breakthrough2				L_pool_cy5_breakthrough2						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy3_breakthrough3"	synthetic_DNA	Cy3			T_pool_cy5_breakthrough3				T_pool_cy5_breakthrough3						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy5_breakthrough2"	synthetic_DNA	Cy5			T_pool_cy3_breakthrough2				T_pool_cy3_breakthrough2						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy5_breakthrough3"	synthetic_DNA	Cy5			T_pool_cy3_breakthrough3				T_pool_cy3_breakthrough3						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy3_breakthrough3"	synthetic_DNA	Cy3			L_pool_cy5_breakthrough3				L_pool_cy5_breakthrough3						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy5_breakthrough1"	synthetic_DNA	Cy5			M_pool_cy3_breakthrough1				M_pool_cy3_breakthrough1						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy3_agil2"	synthetic_DNA	Cy3			T_pool_cy5_agil2				T_pool_cy5_agil2						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy3_breakthrough1"	synthetic_DNA	Cy3			M_pool_cy5_breakthrough1				M_pool_cy5_breakthrough1						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy3_agil1"	synthetic_DNA	Cy3			M_pool_cy5_agil1				M_pool_cy5_agil1						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy5_breakthrough3"	synthetic_DNA	Cy5			M_pool_cy3_breakthrough3				M_pool_cy3_breakthrough3						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy3_agil2"	synthetic_DNA	Cy3			M_pool_cy5_agil2				M_pool_cy5_agil2						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy5_breakthrough2"	synthetic_DNA	Cy5			M_pool_cy3_breakthrough2				M_pool_cy3_breakthrough2						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy3_breakthrough2"	synthetic_DNA	Cy3			M_pool_cy5_breakthrough2				M_pool_cy5_breakthrough2						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy5_agil1"	synthetic_DNA	Cy5			M_pool_cy3_agil1				M_pool_cy3_agil1						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy3_breakthrough3"	synthetic_DNA	Cy3			M_pool_cy5_breakthrough3				M_pool_cy5_breakthrough3						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy5_agil1"	synthetic_DNA	Cy5			T_pool_cy3_agil1				T_pool_cy3_agil1						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy5_agil3"	synthetic_DNA	Cy5			M_pool_cy3_agil3				M_pool_cy3_agil3						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy3_agil3"	synthetic_DNA	Cy3			T_pool_cy5_agil3				T_pool_cy5_agil3						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy3_breakthrough1"	synthetic_DNA	Cy3			T_pool_cy5_breakthrough1				T_pool_cy5_breakthrough1						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy5_agil2"	synthetic_DNA	Cy5			M_pool_cy3_agil2				M_pool_cy3_agil2						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"2nd_REF_pool_L_cy3_breakthrough4"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	2nd_M_pool_cy5_breakthrough4	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_M_pool_cy5_breakthrough4	95000076.gpr				myoepithelial	normal
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"2nd_REF_pool_L_cy5_breakthrough95000083"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	2nd_T_pool_cy3_breakthrough95000083	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_T_pool_cy3_breakthrough95000083	95000083.gpr				"breast epithelial"	"invasive ductual carcinoma"
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"2nd_REF_pool_L_cy5_breakthrough95000084"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	2nd_T_pool_cy3_breakthrough95000084	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_T_pool_cy3_breakthrough95000084	95000084.gpr				"breast epithelial"	"invasive ductual carcinoma"
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"2nd_REF_pool_L_cy5_breakthrough95000065"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	2nd_T_pool_cy3_breakthrough95000065	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_T_pool_cy3_breakthrough95000065	95000065.gpr				"breast epithelial"	"invasive ductual carcinoma"
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"2nd_REF_pool_L_cy5_breakthrough95000066"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	2nd_T_pool_cy3_breakthrough95000066	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_T_pool_cy3_breakthrough95000066	95000066.gpr				"breast epithelial"	"invasive ductual carcinoma"
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"2nd_REF_pool_L_cy3_breakthrough3"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	2nd_T_pool_cy5_breakthrough3	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_T_pool_cy5_breakthrough3	95000077.gpr				"breast epithelial"	"invasive ductual carcinoma"
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"2nd_REF_pool_L_cy3_breakthrough4"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	2nd_T_pool_cy5_breakthrough4	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_T_pool_cy5_breakthrough4	95000078.gpr				"breast epithelial"	"invasive ductual carcinoma"
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"2nd_REF_pool_L_cy3_breakthrough1"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	2nd_T_pool_cy5_breakthrough1	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_T_pool_cy5_breakthrough1	95000071.gpr				"breast epithelial"	"invasive ductual carcinoma"
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"2nd_REF_pool_L_cy3_breakthrough4"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	2nd_L_pool_cy5_breakthrough4	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_L_pool_cy5_breakthrough4	95000074.gpr				luminal	normal
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"2nd_REF_pool_L_cy5_breakthrough3"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	2nd_M_pool_cy3_breakthrough3	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_M_pool_cy3_breakthrough3	95000081.gpr				myoepithelial	normal
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"2nd_REF_pool_L_cy5_breakthrough4"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	2nd_M_pool_cy3_breakthrough4	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_M_pool_cy3_breakthrough4	95000082.gpr				myoepithelial	normal
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"2nd_REF_pool_L_cy5_breakthrough1"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	2nd_M_pool_cy3_breakthrough1	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_M_pool_cy3_breakthrough1	95000063.gpr				myoepithelial	normal
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"2nd_REF_pool_L_cy3_breakthrough2"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	2nd_M_pool_cy5_breakthrough2	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_M_pool_cy5_breakthrough2	95000070.gpr				myoepithelial	normal
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"2nd_REF_pool_L_cy3_breakthrough3"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	2nd_M_pool_cy5_breakthrough3	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_M_pool_cy5_breakthrough3	95000075.gpr				myoepithelial	normal
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"2nd_REF_pool_L_cy5_breakthrough2"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	2nd_M_pool_cy3_breakthrough2	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_M_pool_cy3_breakthrough2	95000064.gpr				myoepithelial	normal
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"2nd_REF_pool_L_cy3_breakthrough2"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	2nd_T_pool_cy5_breakthrough2	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_T_pool_cy5_breakthrough2	95000072.gpr				"breast epithelial"	"invasive ductual carcinoma"
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"2nd_REF_pool_L_cy3_breakthrough1"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	2nd_M_pool_cy5_breakthrough1	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_M_pool_cy5_breakthrough1	95000069.gpr				myoepithelial	normal
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy5_breakthrough2"	synthetic_DNA	Cy5			L_pool_cy3_breakthrough2				L_pool_cy3_breakthrough2						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"2nd_REF_pool_L_cy5_breakthrough2"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	2nd_L_pool_cy3_breakthrough2	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_L_pool_cy3_breakthrough2	95000062.gpr				luminal	normal
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy3_agil2"	synthetic_DNA	Cy3			L_pool_cy5_agil2				L_pool_cy5_agil2						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"2nd_REF_pool_L_cy5_breakthrough3"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	2nd_L_pool_cy3_breakthrough3	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_L_pool_cy3_breakthrough3	95000079.gpr				luminal	normal
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy5_agil3"	synthetic_DNA	Cy5			L_pool_cy3_agil3				L_pool_cy3_agil3						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy5_breakthrough1"	synthetic_DNA	Cy5			L_pool_cy3_breakthrough1				L_pool_cy3_breakthrough1						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy3_agil3"	synthetic_DNA	Cy3			L_pool_cy5_agil3				L_pool_cy5_agil3						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy3_breakthrough1"	synthetic_DNA	Cy3			L_pool_cy5_breakthrough1				L_pool_cy5_breakthrough1						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy5_breakthrough3"	synthetic_DNA	Cy5			L_pool_cy3_breakthrough3				L_pool_cy3_breakthrough3						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"2nd_REF_pool_L_cy5_breakthrough1"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	2nd_L_pool_cy3_breakthrough1	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_L_pool_cy3_breakthrough1	95000061.gpr				luminal	normal
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy3_agil1"	synthetic_DNA	Cy3			L_pool_cy5_agil1				L_pool_cy5_agil1						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy5_agil1"	synthetic_DNA	Cy5			L_pool_cy3_agil1				L_pool_cy3_agil1						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"2nd_REF_pool_L_cy3_breakthrough3"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	2nd_L_pool_cy5_breakthrough3	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_L_pool_cy5_breakthrough3	95000073.gpr				luminal	normal
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"2nd_REF_pool_L_cy3_breakthrough1"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	2nd_L_pool_cy5_breakthrough1	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_L_pool_cy5_breakthrough1	95000067.gpr				luminal	normal
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"2nd_REF_pool_L_cy5_breakthrough4"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	2nd_L_pool_cy3_breakthrough4	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_L_pool_cy3_breakthrough4	95000080.gpr				luminal	normal
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"REF_pool_cy5_agil2"	synthetic_DNA	Cy5			L_pool_cy3_agil2				L_pool_cy3_agil2						
+"REF sources"	whole_organism														"REF sources"	organism_part				E_REF	total_RNA	P-TABM-AnitaG-10115		"2nd_REF_pool_L_cy3_breakthrough2"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	2nd_L_pool_cy5_breakthrough2	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_L_pool_cy5_breakthrough2	95000068.gpr				luminal	normal
+SAMPLE_human_1000	organism_part		"University College London Hospital, London,UK"	"breast epithelial"		adult	"invasive ductual carcinoma"	Human_1000	"mammary gland"	"Homo sapiens"	female				SAMPLE_human_1000	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10845	E_tumour_pool	total_RNA	P-TABM-AnitaG-10115		"T_pool_cy3_breakthrough1"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	T_pool_cy3_breakthrough1	A-MEXP-259	P-MEXP-10145	ArrayExpress	T_pool_cy3_breakthrough1	90000339.gpr				"breast epithel [...]
+SAMPLE_human_1033	organism_part		"University College London Hospital, London,UK"	"breast epithelial"	"grad3,size58, 1/15 lymph node, ESR1 negative, HER2 negative, progesteron receptor negative"	adult	"invasive ductual carcinoma"	Human_1033	"mammary gland"	"Homo sapiens"	female				SAMPLE_human_1033	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10845	E_tumour_pool	total_RNA	P-TABM-AnitaG-10115		"T_pool_cy3_breakthrough2"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	T_pool_cy3_breakthrough2	A [...]
+SAMPLE_human_1045	organism_part		"University College London Hospital, London,UK"	"breast epithelial"	"grad2,size50, ESR1 negative, HER2 negative"	adult	"invasive ductual carcinoma"	Human_1045	"mammary gland"	"Homo sapiens"	female				SAMPLE_human_1045	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10845	E_tumour_pool	total_RNA	P-TABM-AnitaG-10115		"T_pool_cy3_breakthrough3"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	T_pool_cy3_breakthrough3	A-MEXP-259	P-MEXP-10145	ArrayExpress	T_pool_cy3_b [...]
+SAMPLE_human_1052	organism_part		"University College London Hospital, London,UK"	"breast epithelial"	"grad3,size35, ESR1 negative, progesteron receptor negative"	adult	"invasive ductual carcinoma"	Human_1052	"mammary gland"	"Homo sapiens"	female				SAMPLE_human_1052	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10845	E_tumour_pool	total_RNA	P-TABM-AnitaG-10115		"T_pool_cy5_breakthrough1"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	T_pool_cy5_breakthrough1	A-MEXP-259	P-MEXP-10145	ArrayExpr [...]
+SAMPLE_human_1062	organism_part		"University College London Hospital, London,UK"	"breast epithelial"	"grad2,size20,vascular invasion, 1/25 lymph node, ESR1 positive, HER2 negative, progesteron receptor negative"	adult	"invasive ductual carcinoma"	Human_1062	"mammary gland"	"Homo sapiens"	female				SAMPLE_human_1062	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10845	E_tumour_pool	total_RNA	P-TABM-AnitaG-10115		"T_pool_cy5_breakthrough2"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	T_pool_c [...]
+SAMPLE_human_1063	organism_part		"University College London Hospital, London,UK"	"breast epithelial"	"grad3,size45,vascular invasion, ESR1 positive, HER2 positive, progesteron receptor positive"	adult	"invasive ductual carcinoma"	Human_1063	"mammary gland"	"Homo sapiens"	female				SAMPLE_human_1063	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10845	E_tumour_pool	total_RNA	P-TABM-AnitaG-10115		"T_pool_cy5_breakthrough3"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	T_pool_cy5_breakthrough3	 [...]
+SAMPLE_human_489	organism_part		"University College London Hospital, London,UK"	"breast epithelial"	"grad3,size25,vascular invasion, ESR1 positive, HER2 negative"	adult	"invasive ductual carcinoma"	Human_489	"mammary gland"	"Homo sapiens"	female				SAMPLE_human_489	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10845	E_tumour_pool	total_RNA	P-AFFY-2	ArrayExpress	"T_pool_affy1 biotin"	synthetic_DNA	biotin			T_pool_affy1	A-AFFY-44			T_pool_affy1	T1.CEL	T1.EXP			"breast epithelial"	"invasive  [...]
+SAMPLE_human_552	organism_part		"University College London Hospital, London,UK"	"breast epithelial"	"grad3,size40,vascular invasion, 8/9 lymph node, ESR1 negative, HER2 negative"	adult	"invasive ductual carcinoma"	Human_552	"mammary gland"	"Homo sapiens"	female				SAMPLE_human_552	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10845	E_tumour_pool	total_RNA	P-AFFY-2	ArrayExpress	"T_pool_affy2 biotin"	synthetic_DNA	biotin			T_pool_affy2	A-AFFY-44			T_pool_affy2	T2.CEL	T2.EXP			"breast epithe [...]
+SAMPLE_human_593	organism_part		"University College London Hospital, London,UK"	"breast epithelial"	"grad2,size35, 1/9 lymph node, ESR1 positive"	adult	"invasive lobular carcinoma"	Human_593	"mammary gland"	"Homo sapiens"	female				SAMPLE_human_593	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10845	E_tumour_pool	total_RNA	P-AFFY-2	ArrayExpress	"T_pool_affy3 biotin"	synthetic_DNA	biotin			T_pool_affy3	A-AFFY-44			T_pool_affy3	T3.CEL	T3.EXP			"breast epithelial"	"invasive lobular carcinoma"
+SAMPLE_human_695	organism_part		"University College London Hospital, London,UK"	"breast epithelial"	"grad1,size42, 9/15 lymph node, ESR1 positive, progesteron receptor negative"	adult	"invasive lobular carcinoma"	Human_695	"mammary gland"	"Homo sapiens"	female				SAMPLE_human_695	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10845	E_tumour_pool	total_RNA	P-TABM-AnitaG-10115		"T_pool_cy3_agil1"	synthetic_DNA	Cy3	P-MEXP-6162	ArrayExpress	T_pool_cy3_agil1	A-MEXP-218	P-MEXP-10145	ArrayExpress [...]
+SAMPLE_human_725	organism_part		"University College London Hospital, London,UK"	"breast epithelial"	"grad3,size35,vascular invasion, 1/15 lymph node, ESR1 positive"	adult	"invasive ductual carcinoma"	Human_725	"mammary gland"	"Homo sapiens"	female				SAMPLE_human_725	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10845	E_tumour_pool	total_RNA	P-TABM-AnitaG-10115		"T_pool_cy3_agil2"	synthetic_DNA	Cy3	P-MEXP-6162	ArrayExpress	T_pool_cy3_agil2	A-MEXP-218	P-MEXP-10145	ArrayExpress	T_pool_cy3_a [...]
+SAMPLE_human_842	organism_part		"University College London Hospital, London,UK"	"breast epithelial"	"grad3,vascular invasion, 1/10 lymph node, ESR1 positive, progesteron receptor positive"	adult	"invasive ductual carcinoma"	Human_842	"mammary gland"	"Homo sapiens"	female				SAMPLE_human_842	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10845	E_tumour_pool	total_RNA	P-TABM-AnitaG-10115		"T_pool_cy3_agil3"	synthetic_DNA	Cy3	P-MEXP-6162	ArrayExpress	T_pool_cy3_agil3	A-MEXP-218	P-MEXP-10145	A [...]
+SAMPLE_human_845	organism_part		"University College London Hospital, London,UK"	"breast epithelial"	"grad3,size50,vascular invasion, 2/9 lymph node,ESR1 positive, progesteron receptor negative"	adult	"invasive ductual carcinoma"	Human_845	"mammary gland"	"Homo sapiens"	female				SAMPLE_human_845	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10845	E_tumour_pool	total_RNA	P-TABM-AnitaG-10115		"T_pool_cy5_agil1"	synthetic_DNA	Cy5	P-MEXP-6162	ArrayExpress	T_pool_cy5_agil1	A-MEXP-218	P-MEXP-10 [...]
+SAMPLE_human_896	organism_part		"University College London Hospital, London,UK"	"breast epithelial"	"size45, 0/2 lymph node"	adult	"invasive ductual carcinoma"	Human_896	"mammary gland"	"Homo sapiens"	female				SAMPLE_human_896	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10845	E_tumour_pool	total_RNA	P-TABM-AnitaG-10115		"T_pool_cy5_agil2"	synthetic_DNA	Cy5	P-MEXP-6162	ArrayExpress	T_pool_cy5_agil2	A-MEXP-218	P-MEXP-10145	ArrayExpress	T_pool_cy5_agil2	251239125239.gpr				"breast epithel [...]
+SAMPLE_human_983	organism_part		"University College London Hospital, London,UK"	"breast epithelial"	"grad3,size15,vascular invasion, ESR1 negative, HER2 positive, progesteron receptor negative"	adult	"invasive ductual carcinoma"	Human_983	"mammary gland"	"Homo sapiens"	female				SAMPLE_human_983	organism_part	P-MEXP-972	ArrayExpress	P-MEXP-10845	E_tumour_pool	total_RNA	P-TABM-AnitaG-10115		"T_pool_cy5_agil3"	synthetic_DNA	Cy5	P-MEXP-6162	ArrayExpress	T_pool_cy5_agil3	A-MEXP-218	P-MEXP-10 [...]
+																				E_tumour_pool	total_RNA	P-TABM-AnitaG-10115		"2nd_T_pool_L_cy5_breakthrough4"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	2nd_T_pool_cy5_breakthrough4	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_T_pool_cy5_breakthrough4	95000078.gpr				"breast epithelial"	"invasive ductual carcinoma"
+																				E_tumour_pool	total_RNA	P-TABM-AnitaG-10115		"2nd_T_pool_L_cy5_breakthrough2"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	2nd_T_pool_cy5_breakthrough2	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_T_pool_cy5_breakthrough2	95000072.gpr				"breast epithelial"	"invasive ductual carcinoma"
+																				E_tumour_pool	total_RNA	P-TABM-AnitaG-10115		"2nd_T_pool_L_cy5_breakthrough3"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	2nd_T_pool_cy5_breakthrough3	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_T_pool_cy5_breakthrough3	95000077.gpr				"breast epithelial"	"invasive ductual carcinoma"
+																				E_tumour_pool	total_RNA	P-TABM-AnitaG-10115		"2nd_T_pool_L_cy3_breakthrough95000083"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	2nd_T_pool_cy3_breakthrough95000083	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_T_pool_cy3_breakthrough95000083	95000083.gpr				"breast epithelial"	"invasive ductual carcinoma"
+																				E_tumour_pool	total_RNA	P-MEXP-14985		"T_pool_amersham1"	synthetic_DNA	Cy5	P-MEXP-14987		T_pool_amersham1	A-GEHB-1	P-MEXP-14988		T_pool_amersham1			T00297808	T00297808.txt	"breast epithelial"	"invasive ductual carcinoma"
+																				E_tumour_pool	total_RNA	P-MEXP-14985		"T_pool_amersham2"	synthetic_DNA	Cy5	P-MEXP-14987		T_pool_amersham2	A-GEHB-1	P-MEXP-14988		T_pool_amersham2			T00297810	T00297810.txt	"breast epithelial"	"invasive ductual carcinoma"
+																				E_tumour_pool	total_RNA	P-MEXP-14985		"T_pool_amersham3"	synthetic_DNA	Cy5	P-MEXP-14987		T_pool_amersham3	A-GEHB-1	P-MEXP-14988		T_pool_amersham3			T00297811	T00297811.txt	"breast epithelial"	"invasive ductual carcinoma"
+																				E_tumour_pool	total_RNA	P-TABM-AnitaG-10115		"2nd_T_pool_L_cy3_breakthrough95000066"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	2nd_T_pool_cy3_breakthrough95000066	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_T_pool_cy3_breakthrough95000066	95000066.gpr				"breast epithelial"	"invasive ductual carcinoma"
+																				E_tumour_pool	total_RNA	P-TABM-AnitaG-10115		"2nd_T_pool_L_cy3_breakthrough95000065"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	2nd_T_pool_cy3_breakthrough95000065	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_T_pool_cy3_breakthrough95000065	95000065.gpr				"breast epithelial"	"invasive ductual carcinoma"
+																				E_tumour_pool	total_RNA	P-MEXP-14985		"2nd_T_pool_amersham4"	synthetic_DNA	Cy5	P-MEXP-14987		2nd_T_pool_amersham4	A-GEHB-1	P-MEXP-14988		2nd_T_pool_amersham4			T00250233	T00250233.txt	"breast epithelial"	"invasive ductual carcinoma"
+																				E_tumour_pool	total_RNA	P-MEXP-14985		"2nd_T_pool_amersham1"	synthetic_DNA	Cy5	P-MEXP-14987		2nd_T_pool_amersham1	A-GEHB-1	P-MEXP-14988		2nd_T_pool_amersham1			T00253437	T00253437.txt	"breast epithelial"	"invasive ductual carcinoma"
+																				E_tumour_pool	total_RNA	P-TABM-AnitaG-10115		"2nd_T_pool_L_cy3_breakthrough95000084"	synthetic_DNA	Cy3	P-MEXP-10140	ArrayExpress	2nd_T_pool_cy3_breakthrough95000084	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_T_pool_cy3_breakthrough95000084	95000084.gpr				"breast epithelial"	"invasive ductual carcinoma"
+																				E_tumour_pool	total_RNA	P-TABM-AnitaG-10115		"2nd_T_pool_L_cy5_breakthrough1"	synthetic_DNA	Cy5	P-MEXP-10140	ArrayExpress	2nd_T_pool_cy5_breakthrough1	A-MEXP-339	P-MEXP-10145	ArrayExpress	2nd_T_pool_cy5_breakthrough1	95000071.gpr				"breast epithelial"	"invasive ductual carcinoma"
+																				E_tumour_pool	total_RNA	P-MEXP-14985		"T_pool_amersham4"	synthetic_DNA	Cy5	P-MEXP-14987		T_pool_amersham4	A-GEHB-1	P-MEXP-14988		T_pool_amersham4			T00297812	T00297812.txt	"breast epithelial"	"invasive ductual carcinoma"
+																				E_tumour_pool	total_RNA	P-MEXP-14985		"2nd_T_pool_amersham3"	synthetic_DNA	Cy5	P-MEXP-14987		2nd_T_pool_amersham3	A-GEHB-1	P-MEXP-14988		2nd_T_pool_amersham3			T00250232	T00250232.txt	"breast epithelial"	"invasive ductual carcinoma"
+																				E_tumour_pool	total_RNA	P-MEXP-14985		"2nd_T_pool_amersham2"	synthetic_DNA	Cy5	P-MEXP-14987		2nd_T_pool_amersham2	A-GEHB-1	P-MEXP-14988		2nd_T_pool_amersham2			T00253438	T00253438.txt	"breast epithelial"	"invasive ductual carcinoma"
diff --git a/examples/magetab/real/E-TABM-70.idf b/examples/magetab/real/E-TABM-70.idf
new file mode 100644
index 0000000..bc27d11
--- /dev/null
+++ b/examples/magetab/real/E-TABM-70.idf
@@ -0,0 +1,28 @@
+"Investigation Title"	IGR_PLOIDY_STUDY_GK									
+"Experimental Design"	compound_treatment_design									
+"Experimental Factor Name"	CELLLINE	PLOIDY								
+"Experimental Factor Type"	cell_line	ploidy								
+										
+"Person Last Name"	Dessen									
+"Person First Name"	Philippe									
+"Person Mid Initial"										
+"Person Email"	dessen at igr.fr									
+"Person Address"	"Centre Natiol de la Recherche Scientifique, UMR8125,�; Unit� de G�nomique Fonctionnelle, Institut Gustave Roussy, 39 rue Camille-Desmoulins, F-94805 Villejuif, France; INSERM U-517, Faculty of Medicine and Pharmacy, 7 Boulevard Jeanne d;Arc, 21033 Dijon, France�; INSERM U362,Institut Gustave Roussy, 39 rue Camille-Desmoulins, F-94805 Villejuif, France; Unit� d�Immunologie, ERM0208 INSERM, IFR54, Institut Gustave Roussy, Villejuif, France"									
+"Person Roles"	submitter									
+										
+"Quality Control Types"	dye_swap_quality_control									
+"Public Release Date"	"2006-01-23"									
+										
+Comment[ArrayExpressSubmissionDate]	"2005-12-19"									
+										
+"Publication Author List"	"Maria Castedo; Arud Coquelle; Sonia Vivet; Audrey Kauffmann; Marie O. Pequignot; Noelia Casares; Alexander Valent; Shahul Mouhamad; Elise Schmitt; Nazanine Modjtahedi; William Vainchenker; Laurence Zitvogel; Vladimir Lazar; Carmen Garrido; Guido Kroemer"									
+"Publication Title"	"Apoptosis regulation in tetraploid cells"									
+"Experiment Description"	"During the oncogenic process, tetraploidy is a candidate intermediate stage leading from diploidy to aneuploidy. The aim of our experiment was to establish tetraploid clones and to characterize their transcriptome."									
+										
+"Protocol Name"	P_IGR_SAMPLE_04_TETRA_1	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_POOL_04B	P_IGR_LABEL_CY3_04	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04
+"Protocol Type"	compound_based_treatment	compound_based_treatment	nucleic_acid_extraction	pool	pool	labeling	labeling	hybridization	image_acquisition	"Transformation protocol"
+"Protocol Description"	"RKO cells were grown in McCoy' 5A medium supplemented with 10%FCS. In order to generate tetraploid and diploid clones, the cell line RKO containing ~5% tetraploid cells was subcloned by limiting dilution into diploid and tetraploid clones. One diploid clone was transfected with a cDNA encoding H2B-GFP, selected in blasticidine (20 ᄉg/ml), FACS-separated into subsets of cells enriched in a diploid or tetraploid DNA content to generate diploユd and tetraploid H2B-GFP [...]
+"Protocol Parameters"										
+"Protocol Software"										
+										
+"SDRF File"	E-TABM-70_sdrf.txt									
diff --git a/examples/magetab/real/E-TABM-70_sdrf.txt b/examples/magetab/real/E-TABM-70_sdrf.txt
new file mode 100644
index 0000000..1b0b711
--- /dev/null
+++ b/examples/magetab/real/E-TABM-70_sdrf.txt
@@ -0,0 +1,65 @@
+"Source Name"	"Material Type"	"Characteristics [CellLine]"	"Characteristics [Organism]"	"Characteristics [Ploidy]"	"Characteristics [Provider]"	"Characteristics [Treatment]"	"Protocol REF"	"Sample Name"	"Material Type"	"Protocol REF"	"Protocol REF"	"Extract Name"	"Material Type"	"Protocol REF"	"Labeled Extract Name"	"Material Type"	Label	"Protocol REF"	"Hybridization Name"	"Array Design REF"	"Protocol REF"	"Scan Name"	"Array Data File"	"Protocol REF"	"Normalization Name"	"Comment[Normali [...]
+DW	"cell line"	"colon RKO"	"Homo sapiens"	diploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	"clone selection as diploid"	P_IGR_SAMPLE_04_TETRA_1	"DW sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"DW extract"	total_RNA	P_IGR_LABEL_CY5_04	"DW Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"DW Cy5_Pool1 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119523_S01_A01	US14702370_251239119523_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.tx [...]
+Pool1	"cell line"	"colon RKO"	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_1	"Pool1 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"Pool1 extract"	total_RNA	P_IGR_LABEL_CY3_04	"Pool1 Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"DW Cy5_Pool1 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119523_S01_A01	US14702370_251239119523_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_ [...]
+DW	"cell line"	"colon RKO"	"Homo sapiens"	diploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	"clone selection as diploid"	P_IGR_SAMPLE_04_TETRA_1	"DW sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"DW extract"	total_RNA	P_IGR_LABEL_CY3_04	"DW Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"DW Cy3_Pool1 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119541_S01_A01	US14702370_251239119541_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.tx [...]
+Pool1	"cell line"	"colon RKO"	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_1	"Pool1 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"Pool1 extract"	total_RNA	P_IGR_LABEL_CY5_04	"Pool1 Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"DW Cy3_Pool1 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119541_S01_A01	US14702370_251239119541_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_ [...]
+DX	"cell line"	"colon RKO"	"Homo sapiens"	diploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	"clone selection as diploid"	P_IGR_SAMPLE_04_TETRA_1	"DX sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"DX extract"	total_RNA	P_IGR_LABEL_CY5_04	"DX Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"DX Cy5_Pool1 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119609_S01_A01	US14702370_251239119609_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.tx [...]
+Pool1	"cell line"	"colon RKO"	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_1	"Pool1 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"Pool1 extract"	total_RNA	P_IGR_LABEL_CY3_04	"Pool1 Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"DX Cy5_Pool1 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119609_S01_A01	US14702370_251239119609_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_ [...]
+DX	"cell line"	"colon RKO"	"Homo sapiens"	diploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	"clone selection as diploid"	P_IGR_SAMPLE_04_TETRA_1	"DX sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"DX extract"	total_RNA	P_IGR_LABEL_CY3_04	"DX Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"DX Cy3_Pool1 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119524_S01_A01	US14702370_251239119524_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.tx [...]
+Pool1	"cell line"	"colon RKO"	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_1	"Pool1 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"Pool1 extract"	total_RNA	P_IGR_LABEL_CY5_04	"Pool1 Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"DX Cy3_Pool1 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119524_S01_A01	US14702370_251239119524_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_ [...]
+DY	"cell line"	"colon RKO"	"Homo sapiens"	diploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	"clone selection as diploid"	P_IGR_SAMPLE_04_TETRA_1	"DY sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"DY extract"	total_RNA	P_IGR_LABEL_CY5_04	"DY Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"DY Cy5_Pool1 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119530_S01_A01	US14702370_251239119530_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.tx [...]
+Pool1	"cell line"	"colon RKO"	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_1	"Pool1 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"Pool1 extract"	total_RNA	P_IGR_LABEL_CY3_04	"Pool1 Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"DY Cy5_Pool1 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119530_S01_A01	US14702370_251239119530_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_ [...]
+DY	"cell line"	"colon RKO"	"Homo sapiens"	diploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	"clone selection as diploid"	P_IGR_SAMPLE_04_TETRA_1	"DY sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"DY extract"	total_RNA	P_IGR_LABEL_CY3_04	"DY Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"DY Cy3_Pool1 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119525_S01_A01	US14702370_251239119525_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.tx [...]
+Pool1	"cell line"	"colon RKO"	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_1	"Pool1 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"Pool1 extract"	total_RNA	P_IGR_LABEL_CY5_04	"Pool1 Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"DY Cy3_Pool1 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119525_S01_A01	US14702370_251239119525_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_ [...]
+DZ	"cell line"	"colon RKO"	"Homo sapiens"	diploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	"clone selection as diploid"	P_IGR_SAMPLE_04_TETRA_1	"DZ sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"DZ extract"	total_RNA	P_IGR_LABEL_CY5_04	"DZ Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"DZ Cy5_Pool1 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119531_S01_A01	US14702370_251239119531_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.tx [...]
+Pool1	"cell line"	"colon RKO"	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_1	"Pool1 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"Pool1 extract"	total_RNA	P_IGR_LABEL_CY3_04	"Pool1 Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"DZ Cy5_Pool1 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119531_S01_A01	US14702370_251239119531_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_ [...]
+DZ	"cell line"	"colon RKO"	"Homo sapiens"	diploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	"clone selection as diploid"	P_IGR_SAMPLE_04_TETRA_1	"DZ sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"DZ extract"	total_RNA	P_IGR_LABEL_CY3_04	"DZ Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"DZ Cy3_Pool1 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119614_S01_A01	US14702370_251239119614_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.tx [...]
+Pool1	"cell line"	"colon RKO"	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_1	"Pool1 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"Pool1 extract"	total_RNA	P_IGR_LABEL_CY5_04	"Pool1 Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"DZ Cy3_Pool1 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119614_S01_A01	US14702370_251239119614_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_ [...]
+TA	"cell line"	"colon RKO"	"Homo sapiens"	tetraploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	"clone selection as tetraploid"	P_IGR_SAMPLE_04_TETRA_1	"TA sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"TA extract"	total_RNA	P_IGR_LABEL_CY5_04	"TA Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"TA Cy5_Pool1 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119519_S01_A01	US14702370_251239119519_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps [...]
+Pool1	"cell line"	"colon RKO"	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_1	"Pool1 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"Pool1 extract"	total_RNA	P_IGR_LABEL_CY3_04	"Pool1 Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"TA Cy5_Pool1 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119519_S01_A01	US14702370_251239119519_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_ [...]
+TA	"cell line"	"colon RKO"	"Homo sapiens"	tetraploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	"clone selection as tetraploid"	P_IGR_SAMPLE_04_TETRA_1	"TA sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"TA extract"	total_RNA	P_IGR_LABEL_CY3_04	"TA Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"TA Cy3_Pool1 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119487_S01_A01	US14702370_251239119487_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps [...]
+Pool1	"cell line"	"colon RKO"	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_1	"Pool1 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"Pool1 extract"	total_RNA	P_IGR_LABEL_CY5_04	"Pool1 Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"TA Cy3_Pool1 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119487_S01_A01	US14702370_251239119487_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_ [...]
+TB	"cell line"	"colon RKO"	"Homo sapiens"	tetraploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	"clone selection as tetraploid"	P_IGR_SAMPLE_04_TETRA_1	"TB sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"TB extract"	total_RNA	P_IGR_LABEL_CY5_04	"TB Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"TB Cy5_Pool1 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119520_S01_A01	US14702370_251239119520_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps [...]
+Pool1	"cell line"	"colon RKO"	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_1	"Pool1 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"Pool1 extract"	total_RNA	P_IGR_LABEL_CY3_04	"Pool1 Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"TB Cy5_Pool1 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119520_S01_A01	US14702370_251239119520_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_ [...]
+TB	"cell line"	"colon RKO"	"Homo sapiens"	tetraploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	"clone selection as tetraploid"	P_IGR_SAMPLE_04_TETRA_1	"TB sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"TB extract"	total_RNA	P_IGR_LABEL_CY3_04	"TB Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"TB Cy3_Pool1 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119489_S01_A01	US14702370_251239119489_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps [...]
+Pool1	"cell line"	"colon RKO"	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_1	"Pool1 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"Pool1 extract"	total_RNA	P_IGR_LABEL_CY5_04	"Pool1 Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"TB Cy3_Pool1 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119489_S01_A01	US14702370_251239119489_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_ [...]
+TC	"cell line"	"colon RKO"	"Homo sapiens"	tetraploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	"clone selection as tetraploid"	P_IGR_SAMPLE_04_TETRA_1	"TC sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"TC extract"	total_RNA	P_IGR_LABEL_CY5_04	"TC Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"TC Cy5_Pool1 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119608_S01_A01	US14702370_251239119608_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps [...]
+Pool1	"cell line"	"colon RKO"	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_1	"Pool1 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"Pool1 extract"	total_RNA	P_IGR_LABEL_CY3_04	"Pool1 Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"TC Cy5_Pool1 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119608_S01_A01	US14702370_251239119608_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_ [...]
+TC	"cell line"	"colon RKO"	"Homo sapiens"	tetraploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	"clone selection as tetraploid"	P_IGR_SAMPLE_04_TETRA_1	"TC sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"TC extract"	total_RNA	P_IGR_LABEL_CY3_04	"TC Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"TC Cy3_Pool1 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119538_S01_A01	US14702370_251239119538_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps [...]
+Pool1	"cell line"	"colon RKO"	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_1	"Pool1 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"Pool1 extract"	total_RNA	P_IGR_LABEL_CY5_04	"Pool1 Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"TC Cy3_Pool1 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119538_S01_A01	US14702370_251239119538_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_ [...]
+TD	"cell line"	"colon RKO"	"Homo sapiens"	tetraploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	"clone selection as tetraploid"	P_IGR_SAMPLE_04_TETRA_1	"TD sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"TD extract"	total_RNA	P_IGR_LABEL_CY5_04	"TD Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"TD Cy5_Pool1 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119522_S01_A01	US14702370_251239119522_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps [...]
+Pool1	"cell line"	"colon RKO"	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_1	"Pool1 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"Pool1 extract"	total_RNA	P_IGR_LABEL_CY3_04	"Pool1 Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"TD Cy5_Pool1 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119522_S01_A01	US14702370_251239119522_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_ [...]
+TD	"cell line"	"colon RKO"	"Homo sapiens"	tetraploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	"clone selection as tetraploid"	P_IGR_SAMPLE_04_TETRA_1	"TD sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"TD extract"	total_RNA	P_IGR_LABEL_CY3_04	"TD Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"TD Cy3_Pool1 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119540_S01_A01	US14702370_251239119540_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps [...]
+Pool1	"cell line"	"colon RKO"	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_1	"Pool1 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04A	"Pool1 extract"	total_RNA	P_IGR_LABEL_CY5_04	"Pool1 Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"TD Cy3_Pool1 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239119540_S01_A01	US14702370_251239119540_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_ [...]
+Co3	"cell line"	HCT116	"Homo sapiens"	diploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_2	"Co3 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"Co3 extract"	total_RNA	P_IGR_LABEL_CY5_04	"Co3 Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"Co3 Cy5-Pool2 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124865_S01_A01	US14702370_251239124865_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_all_s [...]
+Pool2	"cell line"	HCT116	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_2	"Pool2 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"Pool2 extract"	total_RNA	P_IGR_LABEL_CY5_04	"Pool2 Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"Co3 Cy5-Pool2 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124865_S01_A01	US14702370_251239124865_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_all_ [...]
+Co3	"cell line"	HCT116	"Homo sapiens"	diploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_2	"Co3 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"Co3 extract"	total_RNA	P_IGR_LABEL_CY3_04	"Co3 Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"Co3 Cy3-Pool2 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124928_S01_A01	US14702370_251239124928_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_all_s [...]
+Pool2	"cell line"	HCT116	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_2	"Pool2 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"Pool2 extract"	total_RNA	P_IGR_LABEL_CY3_04	"Pool2 Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"Co3 Cy3-Pool2 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124928_S01_A01	US14702370_251239124928_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_all_ [...]
+Co4	"cell line"	HCT116	"Homo sapiens"	diploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_2	"Co4 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"Co4 extract"	total_RNA	P_IGR_LABEL_CY5_04	"Co4 Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"Co4 Cy5-Pool2 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124866_S01_A01	US14702370_251239124866_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_all_s [...]
+Pool2	"cell line"	HCT116	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_2	"Pool2 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"Pool2 extract"	total_RNA	P_IGR_LABEL_CY5_04	"Pool2 Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"Co4 Cy5-Pool2 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124866_S01_A01	US14702370_251239124866_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_all_ [...]
+Co4	"cell line"	HCT116	"Homo sapiens"	diploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_2	"Co4 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"Co4 extract"	total_RNA	P_IGR_LABEL_CY3_04	"Co4 Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"Co4 Cy3-Pool2 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124929_S01_A01	US14702370_251239124929_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_all_s [...]
+Pool2	"cell line"	HCT116	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_2	"Pool2 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"Pool2 extract"	total_RNA	P_IGR_LABEL_CY3_04	"Pool2 Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"Co4 Cy3-Pool2 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124929_S01_A01	US14702370_251239124929_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_all_ [...]
+Co1	"cell line"	HCT116	"Homo sapiens"	diploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_2	"Co1 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"Co1 extract"	total_RNA	P_IGR_LABEL_CY5_04	"Co1 Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"Co1 Cy5-Pool2 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124869_S01_A01	US14702370_251239124869_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_all_s [...]
+Pool2	"cell line"	HCT116	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_2	"Pool2 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"Pool2 extract"	total_RNA	P_IGR_LABEL_CY5_04	"Pool2 Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"Co1 Cy5-Pool2 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124869_S01_A01	US14702370_251239124869_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_all_ [...]
+Co1	"cell line"	HCT116	"Homo sapiens"	diploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_2	"Co1 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"Co1 extract"	total_RNA	P_IGR_LABEL_CY3_04	"Co1 Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"Co1 Cy3-Pool2 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124932_S01_A01	US14702370_251239124932_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_all_s [...]
+Pool2	"cell line"	HCT116	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_2	"Pool2 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"Pool2 extract"	total_RNA	P_IGR_LABEL_CY3_04	"Pool2 Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"Co1 Cy3-Pool2 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124932_S01_A01	US14702370_251239124932_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_all_ [...]
+Co2	"cell line"	HCT116	"Homo sapiens"	diploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_2	"Co2 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"Co2 extract"	total_RNA	P_IGR_LABEL_CY5_04	"Co2 Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"Co2 Cy5-Pool2 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124925_S01_A01	US14702370_251239124925_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_all_s [...]
+Pool2	"cell line"	HCT116	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_2	"Pool2 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"Pool2 extract"	total_RNA	P_IGR_LABEL_CY5_04	"Pool2 Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"Co2 Cy5-Pool2 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124925_S01_A01	US14702370_251239124925_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_all_ [...]
+Co2	"cell line"	HCT116	"Homo sapiens"	diploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_2	"Co2 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"Co2 extract"	total_RNA	P_IGR_LABEL_CY3_04	"Co2 Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"Co2 Cy3-Pool2 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124933_S01_A01	US14702370_251239124933_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_all_s [...]
+Pool2	"cell line"	HCT116	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_2	"Pool2 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"Pool2 extract"	total_RNA	P_IGR_LABEL_CY3_04	"Pool2 Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"Co2 Cy3-Pool2 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124933_S01_A01	US14702370_251239124933_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_all_ [...]
+C1	"cell line"	HCT116	"Homo sapiens"	tetraploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	"cytochalasin D "	P_IGR_SAMPLE_04_TETRA_2	"C1 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"C1 extract"	total_RNA	P_IGR_LABEL_CY5_04	"C1 Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"C1 Cy5-Pool2 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124926_S01_A01	US14702370_251239124926_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ [...]
+Pool2	"cell line"	HCT116	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_2	"Pool2 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"Pool2 extract"	total_RNA	P_IGR_LABEL_CY5_04	"Pool2 Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"C1 Cy5-Pool2 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124926_S01_A01	US14702370_251239124926_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_all_s [...]
+C1	"cell line"	HCT116	"Homo sapiens"	tetraploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	"cytochalasin D "	P_IGR_SAMPLE_04_TETRA_2	"C1 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"C1 extract"	total_RNA	P_IGR_LABEL_CY3_04	"C1 Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"C1 Cy3-Pool2 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124934_S01_A01	US14702370_251239124934_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ [...]
+Pool2	"cell line"	HCT116	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_2	"Pool2 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"Pool2 extract"	total_RNA	P_IGR_LABEL_CY3_04	"Pool2 Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"C1 Cy3-Pool2 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124934_S01_A01	US14702370_251239124934_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_all_s [...]
+C2	"cell line"	HCT116	"Homo sapiens"	tetraploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	"cytochalasin D "	P_IGR_SAMPLE_04_TETRA_2	"C2 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"C2 extract"	total_RNA	P_IGR_LABEL_CY5_04	"C2 Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"C2 Cy5-Pool2 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124927_S01_A01	US14702370_251239124927_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ [...]
+Pool2	"cell line"	HCT116	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_2	"Pool2 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"Pool2 extract"	total_RNA	P_IGR_LABEL_CY5_04	"Pool2 Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"C2 Cy5-Pool2 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124927_S01_A01	US14702370_251239124927_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_all_s [...]
+C2	"cell line"	HCT116	"Homo sapiens"	tetraploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	"cytochalasin D "	P_IGR_SAMPLE_04_TETRA_2	"C2 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"C2 extract"	total_RNA	P_IGR_LABEL_CY3_04	"C2 Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"C2 Cy3-Pool2 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124940_S01_A01	US14702370_251239124940_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ [...]
+Pool2	"cell line"	HCT116	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_2	"Pool2 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"Pool2 extract"	total_RNA	P_IGR_LABEL_CY3_04	"Pool2 Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"C2 Cy3-Pool2 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124940_S01_A01	US14702370_251239124940_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_all_s [...]
+N1	"cell line"	HCT116	"Homo sapiens"	tetraploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	nocodazole	P_IGR_SAMPLE_04_TETRA_2	"N1 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"N1 extract"	total_RNA	P_IGR_LABEL_CY5_04	"N1 Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"N1 Cy5-Pool2 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124867_S01_A01	US14702370_251239124867_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_ [...]
+Pool2	"cell line"	HCT116	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_2	"Pool2 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"Pool2 extract"	total_RNA	P_IGR_LABEL_CY5_04	"Pool2 Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"N1 Cy5-Pool2 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124867_S01_A01	US14702370_251239124867_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_all_s [...]
+N1	"cell line"	HCT116	"Homo sapiens"	tetraploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	nocodazole	P_IGR_SAMPLE_04_TETRA_2	"N1 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"N1 extract"	total_RNA	P_IGR_LABEL_CY3_04	"N1 Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"N1 Cy3-Pool2 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124930_S01_A01	US14702370_251239124930_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_ [...]
+Pool2	"cell line"	HCT116	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_2	"Pool2 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"Pool2 extract"	total_RNA	P_IGR_LABEL_CY3_04	"Pool2 Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"N1 Cy3-Pool2 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124930_S01_A01	US14702370_251239124930_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_all_s [...]
+N2	"cell line"	HCT116	"Homo sapiens"	tetraploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	nocodazole	P_IGR_SAMPLE_04_TETRA_2	"N2 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"N2 extract"	total_RNA	P_IGR_LABEL_CY5_04	"N2 Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"N2 Cy5-Pool2 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124868_S01_A01	US14702370_251239124868_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_ [...]
+Pool2	"cell line"	HCT116	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_2	"Pool2 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"Pool2 extract"	total_RNA	P_IGR_LABEL_CY5_04	"Pool2 Cy5 Cy5"	synthetic_RNA	Cy5	P_IGR_HYBRID_04	"N2 Cy5-Pool2 Cy5 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124868_S01_A01	US14702370_251239124868_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_all_s [...]
+N2	"cell line"	HCT116	"Homo sapiens"	tetraploid	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	nocodazole	P_IGR_SAMPLE_04_TETRA_2	"N2 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"N2 extract"	total_RNA	P_IGR_LABEL_CY3_04	"N2 Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"N2 Cy3-Pool2 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124931_S01_A01	US14702370_251239124931_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_ [...]
+Pool2	"cell line"	HCT116	"Homo sapiens"		"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"		P_IGR_SAMPLE_04_TETRA_2	"Pool2 sample"	organism_part	P_IGR_EXTRACT_04	P_IGR_POOL_04B	"Pool2 extract"	total_RNA	P_IGR_LABEL_CY3_04	"Pool2 Cy3 Cy3"	synthetic_RNA	Cy3	P_IGR_HYBRID_04	"N2 Cy3-Pool2 Cy3 hyb"	A-MEXP-304	P_IGR_SCANNING_04	US14702370_251239124931_S01_A01	US14702370_251239124931_S01_A01.txt	P_IGR_TRANSFORM_04	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	igr_ploidy_gk_all_s [...]
diff --git a/examples/magetab/real/solexa_example.idf b/examples/magetab/real/solexa_example.idf
new file mode 100644
index 0000000..53e419b
--- /dev/null
+++ b/examples/magetab/real/solexa_example.idf
@@ -0,0 +1,36 @@
+"Investigation Title"	"Solexa analysis of Galapagos finches"			
+"Experiment Description"	"Solexa analysis of Galapagos finches"			
+"Experimental Design"	"growth_condition_design"	"replicate_design"	"co-expression_design"	
+				
+"Experimental Factor Name"	"STRAINORLINE"			
+"Experimental Factor Type"	"strain_or_line"			
+"Experimental Factor Term Source REF"	"MGED Ontology"			
+				
+"Quality Control Type"	"biological_replicate"			
+				
+"Public Release Date"	"2008-01-01"			
+				
+"Person Last Name"	"Darwin"			
+"Person First Name"	"Charles"			
+"Person Mid Initials"				
+"Person Email"	"Charles at darwin.com"			
+"Person Phone"				
+"Person Address"	"Beagle Institute of Theoretical Experiments, Down House, Downe, Kent, UK"			
+"Person Affiliation"	"Beagle Institute of Theoretical Experiments"			
+"Person Roles"	"submitter"			
+				
+"PubMed ID"				
+"Publication Author List"	"Darwin, C"			
+"Publication Title"	"Solexa analysis of Galapagos finches"			
+"Publication Status"	"Submitted"			
+				
+"Protocol Name"	"EXTRACTION"	"NORMALIZATION"	"SOLEXA SEQ"	"SOLEXA DATA ACQUISITION"
+"Protocol Type"	"nucleic_acid_extraction"	"bioassay_data_transformation"		
+"Protocol Description"	"All cDNA samples were prepared by treating ~1 mg of total RNA for 30 min with amplification grade RNase-free DNase (Invitrogen), according to the manufacturer's protocols. PolyA enriched RNA was then prepared using an oligo(dT) selection kit (Oligotex Direct mRNA MiniKit, Qiagen). The resulting poly(A)-enriched RNA was then converted to double-stranded cDNA using a cDNA synthesis kit (Superscript choice system for cDNA synthesis, Invitrogen)."	"FASTQ files of sequ [...]
+"Protocol Term Source REF"	"MGED Ontology"			
+				
+"Term Source Name"	"MGED Ontology"			
+"Term Source File"	"http://mged.sourceforge.net/ontologies/MGEDontology.php"			
+"Term Source Version"	"1.3.0.1"			
+				
+"SDRF File"	"solexa_example.sdrf.txt"			
diff --git a/examples/magetab/real/solexa_example.sdrf.txt b/examples/magetab/real/solexa_example.sdrf.txt
new file mode 100644
index 0000000..3fb8bf5
--- /dev/null
+++ b/examples/magetab/real/solexa_example.sdrf.txt
@@ -0,0 +1,11 @@
+"Source Name"	"Material Type"	"Term Source REF"	"Characteristics[Organism]"	"Characteristics[Sex]"	"Characteristics[StrainOrLine]"	"Protocol REF"	"Extract Name"	"Protocol REF"	"Assay Name"	"Technology Type"	"Protocol REF"	"Array Data File"	"Protocol REF"	"Derived Array Data File"	"Factor Value[STRAINORLINE]"
+"finch 1"	"organism_part"	"MGED Ontology"	"Geospiza fortis"	"male"	"Pinta"	"EXTRACTION"	"extract pinta 1"	"SOLEXA SEQ"	"pinta 1"	"high_throughput_sequencing"	"SOLEXA DATA ACQUISITION"	"run1.fastq"	"NORMALIZATION"	"run1_norm_log2score.txt.gz"	"Pinta"
+"finch 2"	"organism_part"	"MGED Ontology"	"Geospiza fortis"	"male"	"Pinta"	"EXTRACTION"	"extract pinta 2"	"SOLEXA SEQ"	"pinta 2"	"high_throughput_sequencing"	"SOLEXA DATA ACQUISITION"	"run2.fastq"	"NORMALIZATION"	"run2_norm_log2score.txt.gz"	"Pinta"
+"finch 3"	"organism_part"	"MGED Ontology"	"Geospiza fortis"	"male"	"Marchesa"	"EXTRACTION"	"extract marchesa 1"	"SOLEXA SEQ"	"marchesa 1"	"high_throughput_sequencing"	"SOLEXA DATA ACQUISITION"	"run3.fastq"	"NORMALIZATION"	"run3_norm_log2score.txt.gz"	"Marchesa"
+"finch 4"	"organism_part"	"MGED Ontology"	"Geospiza fortis"	"male"	"Marchesa"	"EXTRACTION"	"extract marchesa 2"	"SOLEXA SEQ"	"marchesa 2"	"high_throughput_sequencing"	"SOLEXA DATA ACQUISITION"	"run4.fastq"	"NORMALIZATION"	"run4_norm_log2score.txt.gz"	"Marchesa"
+"finch 5"	"organism_part"	"MGED Ontology"	"Geospiza fortis"	"male"	"Santiago"	"EXTRACTION"	"extract santiago 1"	"SOLEXA SEQ"	"santiago 1"	"high_throughput_sequencing"	"SOLEXA DATA ACQUISITION"	"run5.fastq"	"NORMALIZATION"	"run5_norm_log2score.txt.gz"	"Santiago"
+"finch 6"	"organism_part"	"MGED Ontology"	"Geospiza fortis"	"male"	"Santiago"	"EXTRACTION"	"extract santiago 2"	"SOLEXA SEQ"	"santiago 2"	"high_throughput_sequencing"	"SOLEXA DATA ACQUISITION"	"run6.fastq"	"NORMALIZATION"	"run6_norm_log2score.txt.gz"	"Santiago"
+"finch 7"	"organism_part"	"MGED Ontology"	"Geospiza fortis"	"male"	"Floreana"	"EXTRACTION"	"extract floreana 1"	"SOLEXA SEQ"	"floreana 1"	"high_throughput_sequencing"	"SOLEXA DATA ACQUISITION"	"run7.fastq"	"NORMALIZATION"	"run7_norm_log2score.txt.gz"	"Floreana"
+"finch 8"	"organism_part"	"MGED Ontology"	"Geospiza fortis"	"male"	"Floreana"	"EXTRACTION"	"extract floreana 2"	"SOLEXA SEQ"	"floreana 2"	"high_throughput_sequencing"	"SOLEXA DATA ACQUISITION"	"run8.fastq"	"NORMALIZATION"	"run8_norm_log2score.txt.gz"	"Floreana"
+"finch 9"	"organism_part"	"MGED Ontology"	"Geospiza fortis"	"male"	"Pinzon"	"EXTRACTION"	"extract pinzon 1"	"SOLEXA SEQ"	"pinzon 1"	"high_throughput_sequencing"	"SOLEXA DATA ACQUISITION"	"run9.fastq"	"NORMALIZATION"	"run9_norm_log2score.txt.gz"	"Pinzon"
+"finch 10"	"organism_part"	"MGED Ontology"	"Geospiza fortis"	"male"	"Pinzon"	"EXTRACTION"	"extract pinzon 2"	"SOLEXA SEQ"	"pinzon 2"	"high_throughput_sequencing"	"SOLEXA DATA ACQUISITION"	"run10.fastq"	"NORMALIZATION"	"run10_norm_log2score.txt.gz"	"Pinzon"
diff --git a/examples/magetab/two_color/Data1.txt b/examples/magetab/two_color/Data1.txt
new file mode 100644
index 0000000..6d8b389
--- /dev/null
+++ b/examples/magetab/two_color/Data1.txt
@@ -0,0 +1,37 @@
+ATF	1																																	
+24	43																																	
+"Type=GenePix Results 1.2"																																		
+"DateTime=2003/01/23 11:27:59"																																		
+"Settings=E:\settings files\array.gps"																																		
+"GalFile="																																		
+"Scanner=GenePix 4000A"																																		
+"Comment="																																		
+"PixelSize=10"																																		
+"ImageName=635 nm	532 nm"																																	
+"FileName=\\Server\Data1.tif"																																		
+"PMTVolts=600	590"																																	
+"NormalizationFactor:RatioOfMedians=1.90064"																																		
+"NormalizationFactor:RatioOfMeans=1.90384"																																		
+"NormalizationFactor:MedianOfRatios=1.86554"																																		
+"NormalizationFactor:MeanOfRatios=1.23878"																																		
+"NormalizationFactor:RegressionRatio=2.45261"																																		
+"JpegImage=\\Server\Data1.jpg"																																		
+"RatioFormulation=W1/W2 (635 nm/532 nm)"																																		
+"Barcode="																																		
+"ImageOrigin=1240, 6320"																																		
+"JpegOrigin=1840, 7160"																																		
+"Creator=GenePix Pro 3.0.0.98"																																		
+"Temperature=1.41243"																																		
+"LaserPower=2.19092	0.863674"																																	
+"LaserOnTime=86000	85977"																																	
+"Block"	"Column"	"Row"	"Name"	"ID"	"X"	"Y"	"Dia."	"F635 Median"	"F635 Mean"	"F635 SD"	"B635 Median"	"B635 Mean"	"B635 SD"	"% > B635+1SD"	"% > B635+2SD"	"F635 % Sat."	"F532 Median"	"F532 Mean"	"F532 SD"	"B532 Median"	"B532 Mean"	"B532 SD"	"% > B532+1SD"	"% > B532+2SD"	"F532 % Sat."	"F Pixels"	"B Pixels"	"Sum of Medians"	"Sum of Means"	"F635 Median - B635"	"F532 Median - B532"	"F635 Mean - B635"	"F532 Mean - B532"	"Flags"
+1	1	1			2100	7300	90	240	240	54	40	44	21	100	100	0	502	497	98	81	86	32	100	100	0	52	376	621	616	200	421	200	416	0
+1	2	1			2410	7300	120	694	671	209	40	41	11	98	98	0	1406	1354	413	81	81	20	100	100	0	120	778	1979	1904	654	1325	631	1273	0
+1	3	1			2690	7320	120	501	531	267	41	41	10	99	99	0	998	1080	498	79	82	21	99	98	0	120	810	1379	1491	460	919	490	1001	0
+1	4	1			2980	7310	120	783	770	284	40	41	10	99	96	0	1034	1014	331	81	83	22	100	99	0	120	796	1696	1663	743	953	730	933	0
+1	5	1			3260	7320	130	457	434	219	40	41	10	100	99	0	998	938	493	81	83	21	100	98	0	120	930	1334	1251	417	917	394	857	0
+1	6	1			3570	7310	130	166	172	67	40	41	10	94	94	0	382	388	112	82	83	20	99	98	0	120	914	426	438	126	300	132	306	0
+1	7	1			3860	7320	100	63	65	17	40	41	10	76	55	0	182	188	53	79	80	20	100	96	0	80	552	126	134	23	103	25	109	0
+1	8	1			4130	7330	120	415	392	122	40	41	10	96	95	0	793	779	215	79	81	20	98	97	0	120	816	1089	1052	375	714	352	700	0
+1	9	1			4430	7320	120	153	155	61	39	41	10	97	94	0	374	382	125	80	82	20	99	99	0	120	796	408	418	114	294	116	302	0
+1	10	1			4730	7330	120	865	826	343	40	41	10	100	98	0	1011	941	389	79	81	21	99	98	0	120	810	1757	1648	825	932	786	862	0
diff --git a/examples/magetab/two_color/Data2.txt b/examples/magetab/two_color/Data2.txt
new file mode 100644
index 0000000..6975cfa
--- /dev/null
+++ b/examples/magetab/two_color/Data2.txt
@@ -0,0 +1,40 @@
+ATF	1																																	
+27	43																																	
+"Type=GenePix Results 1.4"																																		
+"DateTime=2003/05/19 15:39:10"																																		
+"Settings=E:\settings files\arrays.gps"																																		
+"GalFile="																																		
+"Scanner=GenePix 4000A [54807]"																																		
+"Comment="																																		
+"PixelSize=10"																																		
+"ImageName=635 nm	532 nm"																																	
+"FileName=\\Server\Data2.tif"																																		
+"PMTVolts=680	610"																																	
+"ScanPower=100	100"																																	
+"FocusPosition=0"																																		
+"NormalizationFactor:RatioOfMedians=0.93502"																																		
+"NormalizationFactor:RatioOfMeans=0.917873"																																		
+"NormalizationFactor:MedianOfRatios=0.933921"																																		
+"NormalizationFactor:MeanOfRatios=0.954675"																																		
+"NormalizationFactor:RegressionRatio=0.787143"																																		
+"JpegImage=\\Server\Data2.jpg"																																		
+"RatioFormulation=W1/W2 (635 nm/532 nm)"																																		
+"Barcode="																																		
+"ImageOrigin=560, 5360"																																		
+"JpegOrigin=1650, 6810"																																		
+"Creator=GenePix Pro 3.0.6.90"																																		
+"Temperature=41.3353"																																		
+"LaserPower=1.50975	1.13071"																																	
+"LaserOnTime=41930	44902"																																	
+"Supplier="																																		
+"Block"	"Column"	"Row"	"Name"	"ID"	"X"	"Y"	"Dia."	"F635 Median"	"F635 Mean"	"F635 SD"	"B635 Median"	"B635 Mean"	"B635 SD"	"% > B635+1SD"	"% > B635+2SD"	"F635 % Sat."	"F532 Median"	"F532 Mean"	"F532 SD"	"B532 Median"	"B532 Mean"	"B532 SD"	"% > B532+1SD"	"% > B532+2SD"	"F532 % Sat."	"F Pixels"	"B Pixels"	"Sum of Medians"	"Sum of Means"	"F635 Median - B635"	"F532 Median - B532"	"F635 Mean - B635"	"F532 Mean - B532"	"Flags"
+1	1	1	""	""	2040	7130	110	112	120	55	49	53	24	80	63	0	191	200	78	50	51	13	100	100	0	80	656	204	221	63	141	71	150	0
+1	2	1	""	""	2330	7120	120	412	435	197	51	55	26	95	95	0	731	843	375	50	51	11	99	99	0	120	820	1042	1177	361	681	384	793	0
+1	3	1	""	""	2610	7130	120	297	367	232	52	56	25	95	90	0	417	587	397	51	52	12	97	95	0	120	820	611	851	245	366	315	536	0
+1	4	1	""	""	2900	7120	120	214	297	230	50	54	25	92	88	0	272	412	327	51	52	12	94	93	0	120	820	385	608	164	221	247	361	0
+1	5	1	""	""	3200	7130	120	355	386	203	52	56	26	92	91	0	683	701	245	51	52	12	100	100	0	120	820	935	984	303	632	334	650	0
+1	6	1	""	""	3500	7110	120	134	135	56	52	55	26	82	71	0	213	209	61	51	52	14	95	93	0	120	810	244	241	82	162	83	158	0
+1	7	1	""	""	3770	7120	80	79	80	33	50	54	27	51	26	0	100	100	26	51	52	11	98	82	0	52	340	78	79	29	49	30	49	0
+1	8	1	""	""	4070	7120	120	332	339	152	55	59	27	92	90	0	609	613	227	52	52	12	95	95	0	120	820	834	845	277	557	284	561	0
+1	9	1	""	""	4370	7090	110	167	179	82	53	57	26	91	86	0	240	254	102	52	54	15	97	96	0	80	616	302	328	114	188	126	202	0
+1	10	1	""	""	4650	7110	130	241	291	200	54	57	25	90	85	0	371	522	320	51	52	12	99	99	0	120	930	507	708	187	320	237	471	0
diff --git a/examples/magetab/two_color/Data3.txt b/examples/magetab/two_color/Data3.txt
new file mode 100644
index 0000000..6d8b389
--- /dev/null
+++ b/examples/magetab/two_color/Data3.txt
@@ -0,0 +1,37 @@
+ATF	1																																	
+24	43																																	
+"Type=GenePix Results 1.2"																																		
+"DateTime=2003/01/23 11:27:59"																																		
+"Settings=E:\settings files\array.gps"																																		
+"GalFile="																																		
+"Scanner=GenePix 4000A"																																		
+"Comment="																																		
+"PixelSize=10"																																		
+"ImageName=635 nm	532 nm"																																	
+"FileName=\\Server\Data1.tif"																																		
+"PMTVolts=600	590"																																	
+"NormalizationFactor:RatioOfMedians=1.90064"																																		
+"NormalizationFactor:RatioOfMeans=1.90384"																																		
+"NormalizationFactor:MedianOfRatios=1.86554"																																		
+"NormalizationFactor:MeanOfRatios=1.23878"																																		
+"NormalizationFactor:RegressionRatio=2.45261"																																		
+"JpegImage=\\Server\Data1.jpg"																																		
+"RatioFormulation=W1/W2 (635 nm/532 nm)"																																		
+"Barcode="																																		
+"ImageOrigin=1240, 6320"																																		
+"JpegOrigin=1840, 7160"																																		
+"Creator=GenePix Pro 3.0.0.98"																																		
+"Temperature=1.41243"																																		
+"LaserPower=2.19092	0.863674"																																	
+"LaserOnTime=86000	85977"																																	
+"Block"	"Column"	"Row"	"Name"	"ID"	"X"	"Y"	"Dia."	"F635 Median"	"F635 Mean"	"F635 SD"	"B635 Median"	"B635 Mean"	"B635 SD"	"% > B635+1SD"	"% > B635+2SD"	"F635 % Sat."	"F532 Median"	"F532 Mean"	"F532 SD"	"B532 Median"	"B532 Mean"	"B532 SD"	"% > B532+1SD"	"% > B532+2SD"	"F532 % Sat."	"F Pixels"	"B Pixels"	"Sum of Medians"	"Sum of Means"	"F635 Median - B635"	"F532 Median - B532"	"F635 Mean - B635"	"F532 Mean - B532"	"Flags"
+1	1	1			2100	7300	90	240	240	54	40	44	21	100	100	0	502	497	98	81	86	32	100	100	0	52	376	621	616	200	421	200	416	0
+1	2	1			2410	7300	120	694	671	209	40	41	11	98	98	0	1406	1354	413	81	81	20	100	100	0	120	778	1979	1904	654	1325	631	1273	0
+1	3	1			2690	7320	120	501	531	267	41	41	10	99	99	0	998	1080	498	79	82	21	99	98	0	120	810	1379	1491	460	919	490	1001	0
+1	4	1			2980	7310	120	783	770	284	40	41	10	99	96	0	1034	1014	331	81	83	22	100	99	0	120	796	1696	1663	743	953	730	933	0
+1	5	1			3260	7320	130	457	434	219	40	41	10	100	99	0	998	938	493	81	83	21	100	98	0	120	930	1334	1251	417	917	394	857	0
+1	6	1			3570	7310	130	166	172	67	40	41	10	94	94	0	382	388	112	82	83	20	99	98	0	120	914	426	438	126	300	132	306	0
+1	7	1			3860	7320	100	63	65	17	40	41	10	76	55	0	182	188	53	79	80	20	100	96	0	80	552	126	134	23	103	25	109	0
+1	8	1			4130	7330	120	415	392	122	40	41	10	96	95	0	793	779	215	79	81	20	98	97	0	120	816	1089	1052	375	714	352	700	0
+1	9	1			4430	7320	120	153	155	61	39	41	10	97	94	0	374	382	125	80	82	20	99	99	0	120	796	408	418	114	294	116	302	0
+1	10	1			4730	7330	120	865	826	343	40	41	10	100	98	0	1011	941	389	79	81	21	99	98	0	120	810	1757	1648	825	932	786	862	0
diff --git a/examples/magetab/two_color/Data4.txt b/examples/magetab/two_color/Data4.txt
new file mode 100644
index 0000000..6975cfa
--- /dev/null
+++ b/examples/magetab/two_color/Data4.txt
@@ -0,0 +1,40 @@
+ATF	1																																	
+27	43																																	
+"Type=GenePix Results 1.4"																																		
+"DateTime=2003/05/19 15:39:10"																																		
+"Settings=E:\settings files\arrays.gps"																																		
+"GalFile="																																		
+"Scanner=GenePix 4000A [54807]"																																		
+"Comment="																																		
+"PixelSize=10"																																		
+"ImageName=635 nm	532 nm"																																	
+"FileName=\\Server\Data2.tif"																																		
+"PMTVolts=680	610"																																	
+"ScanPower=100	100"																																	
+"FocusPosition=0"																																		
+"NormalizationFactor:RatioOfMedians=0.93502"																																		
+"NormalizationFactor:RatioOfMeans=0.917873"																																		
+"NormalizationFactor:MedianOfRatios=0.933921"																																		
+"NormalizationFactor:MeanOfRatios=0.954675"																																		
+"NormalizationFactor:RegressionRatio=0.787143"																																		
+"JpegImage=\\Server\Data2.jpg"																																		
+"RatioFormulation=W1/W2 (635 nm/532 nm)"																																		
+"Barcode="																																		
+"ImageOrigin=560, 5360"																																		
+"JpegOrigin=1650, 6810"																																		
+"Creator=GenePix Pro 3.0.6.90"																																		
+"Temperature=41.3353"																																		
+"LaserPower=1.50975	1.13071"																																	
+"LaserOnTime=41930	44902"																																	
+"Supplier="																																		
+"Block"	"Column"	"Row"	"Name"	"ID"	"X"	"Y"	"Dia."	"F635 Median"	"F635 Mean"	"F635 SD"	"B635 Median"	"B635 Mean"	"B635 SD"	"% > B635+1SD"	"% > B635+2SD"	"F635 % Sat."	"F532 Median"	"F532 Mean"	"F532 SD"	"B532 Median"	"B532 Mean"	"B532 SD"	"% > B532+1SD"	"% > B532+2SD"	"F532 % Sat."	"F Pixels"	"B Pixels"	"Sum of Medians"	"Sum of Means"	"F635 Median - B635"	"F532 Median - B532"	"F635 Mean - B635"	"F532 Mean - B532"	"Flags"
+1	1	1	""	""	2040	7130	110	112	120	55	49	53	24	80	63	0	191	200	78	50	51	13	100	100	0	80	656	204	221	63	141	71	150	0
+1	2	1	""	""	2330	7120	120	412	435	197	51	55	26	95	95	0	731	843	375	50	51	11	99	99	0	120	820	1042	1177	361	681	384	793	0
+1	3	1	""	""	2610	7130	120	297	367	232	52	56	25	95	90	0	417	587	397	51	52	12	97	95	0	120	820	611	851	245	366	315	536	0
+1	4	1	""	""	2900	7120	120	214	297	230	50	54	25	92	88	0	272	412	327	51	52	12	94	93	0	120	820	385	608	164	221	247	361	0
+1	5	1	""	""	3200	7130	120	355	386	203	52	56	26	92	91	0	683	701	245	51	52	12	100	100	0	120	820	935	984	303	632	334	650	0
+1	6	1	""	""	3500	7110	120	134	135	56	52	55	26	82	71	0	213	209	61	51	52	14	95	93	0	120	810	244	241	82	162	83	158	0
+1	7	1	""	""	3770	7120	80	79	80	33	50	54	27	51	26	0	100	100	26	51	52	11	98	82	0	52	340	78	79	29	49	30	49	0
+1	8	1	""	""	4070	7120	120	332	339	152	55	59	27	92	90	0	609	613	227	52	52	12	95	95	0	120	820	834	845	277	557	284	561	0
+1	9	1	""	""	4370	7090	110	167	179	82	53	57	26	91	86	0	240	254	102	52	54	15	97	96	0	80	616	302	328	114	188	126	202	0
+1	10	1	""	""	4650	7110	130	241	291	200	54	57	25	90	85	0	371	522	320	51	52	12	99	99	0	120	930	507	708	187	320	237	471	0
diff --git a/examples/magetab/two_color/NormData1.txt b/examples/magetab/two_color/NormData1.txt
new file mode 100644
index 0000000..ade2275
--- /dev/null
+++ b/examples/magetab/two_color/NormData1.txt
@@ -0,0 +1,11 @@
+MetaColumn	MetaRow	Column	Row	Reporter Identifier	"Ratio of Medians"	"Ratio of Means"	"Median of Ratios"	"Mean of Ratios"	"Ratios SD"	"Rgn Ratio"	"Rgn R�"	"Log Ratio"
+1	1	1	1	A102340	0.475	0.481	0.494	0.491	0.129	0.413	0.731	-1.074
+1	1	2	1	A102341	0.494	0.496	0.5	0.599	0.409	0.433	0.777	-1.019
+1	1	3	1	A102342	0.501	0.49	0.493	0.641	1.28	0.46	0.837	-0.998
+1	1	4	1	A102343	0.78	0.782	0.819	1.372	3.252	0.675	0.722	-0.359
+1	1	5	1	A102344	0.455	0.46	0.45	0.6	0.632	0.382	0.751	-1.137
+1	1	6	1	A102345	0.42	0.431	0.473	0.696	2.121	0.362	0.589	-1.252
+1	1	7	1	A102346	0.223	0.229	0.226	0.313	0.289	0.16	0.283	-2.163
+1	1	8	1	A102347	0.525	0.503	0.522	0.741	1.71	0.445	0.754	-0.929
+1	1	9	1	A102348	0.388	0.384	0.368	0.443	0.369	0.351	0.721	-1.367
+1	1	10	1	A102349	0.885	0.912	0.923	1.477	3.031	0.751	0.698	-0.176
diff --git a/examples/magetab/two_color/NormData2.txt b/examples/magetab/two_color/NormData2.txt
new file mode 100644
index 0000000..52c7687
--- /dev/null
+++ b/examples/magetab/two_color/NormData2.txt
@@ -0,0 +1,11 @@
+Reporter Identifier	"Ratio of Medians"	"Ratio of Means"	"Median of Ratios"	"Mean of Ratios"	"Ratios SD"	"Rgn Ratio"	"Rgn R�"	"Log Ratio"
+A102340	0.447	0.473	0.502	0.428	2.467	0.472	0.332	-1.162
+A102341	0.53	0.484	0.477	0.503	3.009	0.418	0.611	-0.916
+A102342	0.669	0.588	0.592	0.604	3.17	0.511	0.592	-0.579
+A102343	0.742	0.684	0.755	0.675	3.312	0.576	0.412	-0.43
+A102344	0.479	0.514	0.506	0.502	2.251	0.507	0.593	-1.061
+A102345	0.506	0.525	0.546	0.488	3.047	0.532	0.398	-0.982
+A102346	0.592	0.612	1.025	0.705	2.93	1.819	0.071	-0.757
+A102347	0.497	0.506	0.543	0.522	3.195	0.482	0.645	-1.008
+A102348	0.606	0.624	0.669	0.614	2.841	0.577	0.414	-0.722
+A102349	0.584	0.503	0.457	0.508	2.361	0.481	0.581	-0.775
diff --git a/examples/magetab/two_color/NormData3.txt b/examples/magetab/two_color/NormData3.txt
new file mode 100644
index 0000000..ade2275
--- /dev/null
+++ b/examples/magetab/two_color/NormData3.txt
@@ -0,0 +1,11 @@
+MetaColumn	MetaRow	Column	Row	Reporter Identifier	"Ratio of Medians"	"Ratio of Means"	"Median of Ratios"	"Mean of Ratios"	"Ratios SD"	"Rgn Ratio"	"Rgn R�"	"Log Ratio"
+1	1	1	1	A102340	0.475	0.481	0.494	0.491	0.129	0.413	0.731	-1.074
+1	1	2	1	A102341	0.494	0.496	0.5	0.599	0.409	0.433	0.777	-1.019
+1	1	3	1	A102342	0.501	0.49	0.493	0.641	1.28	0.46	0.837	-0.998
+1	1	4	1	A102343	0.78	0.782	0.819	1.372	3.252	0.675	0.722	-0.359
+1	1	5	1	A102344	0.455	0.46	0.45	0.6	0.632	0.382	0.751	-1.137
+1	1	6	1	A102345	0.42	0.431	0.473	0.696	2.121	0.362	0.589	-1.252
+1	1	7	1	A102346	0.223	0.229	0.226	0.313	0.289	0.16	0.283	-2.163
+1	1	8	1	A102347	0.525	0.503	0.522	0.741	1.71	0.445	0.754	-0.929
+1	1	9	1	A102348	0.388	0.384	0.368	0.443	0.369	0.351	0.721	-1.367
+1	1	10	1	A102349	0.885	0.912	0.923	1.477	3.031	0.751	0.698	-0.176
diff --git a/examples/magetab/two_color/NormData4.txt b/examples/magetab/two_color/NormData4.txt
new file mode 100644
index 0000000..52c7687
--- /dev/null
+++ b/examples/magetab/two_color/NormData4.txt
@@ -0,0 +1,11 @@
+Reporter Identifier	"Ratio of Medians"	"Ratio of Means"	"Median of Ratios"	"Mean of Ratios"	"Ratios SD"	"Rgn Ratio"	"Rgn R�"	"Log Ratio"
+A102340	0.447	0.473	0.502	0.428	2.467	0.472	0.332	-1.162
+A102341	0.53	0.484	0.477	0.503	3.009	0.418	0.611	-0.916
+A102342	0.669	0.588	0.592	0.604	3.17	0.511	0.592	-0.579
+A102343	0.742	0.684	0.755	0.675	3.312	0.576	0.412	-0.43
+A102344	0.479	0.514	0.506	0.502	2.251	0.507	0.593	-1.061
+A102345	0.506	0.525	0.546	0.488	3.047	0.532	0.398	-0.982
+A102346	0.592	0.612	1.025	0.705	2.93	1.819	0.071	-0.757
+A102347	0.497	0.506	0.543	0.522	3.195	0.482	0.645	-1.008
+A102348	0.606	0.624	0.669	0.614	2.841	0.577	0.414	-0.722
+A102349	0.584	0.503	0.457	0.508	2.361	0.481	0.581	-0.775
diff --git a/examples/magetab/two_color/test.idf b/examples/magetab/two_color/test.idf
new file mode 100644
index 0000000..77f5431
--- /dev/null
+++ b/examples/magetab/two_color/test.idf
@@ -0,0 +1,49 @@
+"Investigation Title"	"University of Heidelberg H sapiens TK6"							
+"Experimental Design"	"genetic_modification_design"							
+"Experimental Factor Name"	"EF1"	"EF2"						
+"Experimental Factor Type"	"genetic_modification"	"compound"						
+"Experimental Factor Term Source REF"	"MO"							
+								
+"Person Last Name"	"Maier"	"Fleckenstein"	"Li"	"Laufs"	"Zeller"	"Freuhauf"	"Herskind"	"Wenz"
+"Person First Name"	"Patrick"	"Katharina"	"Li"	"Stephanie"	"Jens"	"Stephan"	"Carsten"	"Frederik"
+"Person Mid Initials"					"W"			
+"Person Email"	"patrick.maier at radonk.ma.uni-heidelberg.de"							
+"Person Phone"	491334787822							
+"Person Fax"	123
+"Person Address"	"Theodor-Kutzer-Ufer 1-3"							
+"Person Affiliation"	"Department of Radiation Oncology, University of Heidelberg"	"Department of Radiation Oncology, University of Heidelberg"	"University of Heidelberg, ZMF"	"DKFZ, Heidelberg"	"DKFZ, Heidelberg"	"University of Heidelberg, Department of Int Med V"	"Department of Radiation Oncology, University of Heidelberg"	"Department of Radiation Oncology, University of Heidelberg"
+"Person Roles"	"submitter;investigator"	"investigator"	"investigator"	"investigator"	"investigator"	"investigator"	"investigator"	"investigator"
+"Person Roles Term Source REF"	"MO"							
+								
+"Quality Control Type"	"biological_replicate"							
+"Quality Control Term Source REF"	"MO"							
+"Replicate Type"	"biological_replicate"							
+"Replicate Term Source REF"	"MO"							
+"Normalization Type"	"whatever"							
+"Normalization Term Source REF"	"MO"							
+"Date of Experiment"	"2005-01-04"							
+"Public Release Date"	"2005-04-01"							
+								
+"PubMed ID"	16953664							
+"Publication DOI"	"doi: some number here"
+"Publication Author List"	"Me, him and her"
+"Publication Title"	"What I did on my holidays"
+"Publication Status"	"Universally panned"
+"Publication Status Term Source REF"	"MO"
+
+"Experiment Description"	"Gene expression of TK6 cells transduced with an oncoretrovirus expressing MDR1 (TK6MDR1) was compared to untransduced TK6 cells and to TK6 cell transduced with an oncoretrovirus expressing the Neomycin resistance gene (TK6neo). Two biological replicates of each were generated and the expression profiles were determined using Affymetrix Human Genome U133 Plus2.0 GeneChip microarrays. Comparisons between the sample groups allow the identification of genes with exp [...]
+								
+"Protocol Name"	"GROWTHPRTCL10653"	"EXTPRTCL10654"	"TRANPRTCL10656"					
+"Protocol Type"	"grow"	"nucleic_acid_extraction"	"bioassay_data_transformation"					
+"Protocol Description"	"TK6 cells were grown in suspension cultures in RPMI 1640 medium supplemented with 10% horse serum (Invitrogen, Karlsruhe, Germany). The cells were routinely maintained at 37 C and 5% CO2."	"Approximately 10^6 cells were lysed in RLT buffer (Qiagen).Total RNA was extracted from the cell lysate using an RNeasy kit (Qiagen)."	"Mixed Model Normalization with SAS Micro Array Solutions (version 1.3)."					
+"Protocol Parameters"	"media"	"Extracted Product; Amplification"						
+"Protocol Term Source REF"	"MO"							
+"Protocol Hardware"		"MyHardware"
+"Protocol Software"		"MySoftware"
+"Protocol Contact"		"MyContact"
+
+"SDRF File"	"test_sdrf.txt"							
+								
+"Term Source Name"	"CTO"	"MO"	"ncbitax"	"ArrayExpress"	"NCI META"			
+"Term Source File"	"http://obo.sourceforge.net/cgi-bin/detail.cgi?cell"	"http://mged.sourceforge.net/ontologies/MGEDontology.php"	"http://www.ncbi.nlm.nih.gov/Taxonomy/taxonomyhome.html/"	"http://www.ebi.ac.uk/arrayexpress/"	"http://ncimeta.nci.nih.gov/indexMetaphrase.html"			
+"Term Source Version"		"1.3.0.1"						
diff --git a/examples/magetab/two_color/test_sdrf.txt b/examples/magetab/two_color/test_sdrf.txt
new file mode 100644
index 0000000..7a3939e
--- /dev/null
+++ b/examples/magetab/two_color/test_sdrf.txt
@@ -0,0 +1,6 @@
+Source Name	Provider	Characteristics[ OrganismPart ]	Characteristics[DiseaseState]	Term Source REF:test namespace	Term Accession Number	Material Type	Description	Comment[MyNVT]	Sample Name	Characteristics[Age]	Unit[TimeUnit]	Term Source REF	Material Type	Comment[sample comment]	Protocol REF	Performer	Parameter Value[Extracted Product]	Date	Comment[P_COMM]	Extract Name	Material Type	LabeledExtract Name	MaterialType	Term Source REF	Label	Term Source REF	Protocol REF	Term Source REF	Hybridi [...]
+my source	the guy in the next room	root	hemophilia	NCI META	CL:111111	organism_part	description_text	mycomment	my sample	6	hours	MO	cell	sample comment value	EXTPRTCL10654	the guy in the next room	total RNA	21/02/07	This did not happen. I was not here.	my extract	not_a_MO_term	my LE1	total_RNA	MO	Cy3	MO	P-XMPL-7	ArrayExpress	my hybridization	hyb conditions were suboptimal	A-TEST-1	My favourite array design	scanning protocol	my scan	imagefile1.TIFF	this was a great picture	Data1.txt		TRAN [...]
+my source	the guy in the next room	root	hemophilia	NCI META	CL:111111	organism_part	description_text	mycomment	my sample	6	hours	MO	cell	sample comment value	EXTPRTCL10654	the guy in the next room	total RNA	21/02/07	This did not happen. I was not here.	my extract	not_a_MO_term	my LE2	total_RNA	MO	Cy5	MO	P-XMPL-7	ArrayExpress	my hybridization	hyb conditions were suboptimal	A-TEST-1	My favourite array design	scanning protocol	my scan	imagefile1.TIFF	this was a great picture	Data2.txt	not a [...]
+sparse source 1			normal		blah blah ignore me										EXTPRTCL10654		polyA RNA					sparse LE Cy5			Cy5		P-XMPL-11	ArrayExpress	sparse hyb		A-TEST-1			sparse scan1	testing.jpg		Data3.txt		TRANPRTCL10656	norm 3		NormData3.txt	pained expression					
+sparse source 2			normal												EXTPRTCL10654		polyA RNA					sparse LE Cy3			Cy3		P-XMPL-11	ArrayExpress	sparse hyb		A-TEST-1			sparse scan2			Data4.txt		TRANPRTCL10656	norm 3		NormData3.txt	pregnant pause					
+sparse source 3			normal												EXTPRTCL10654		polyA RNA					sparse LE biotin			biotin		P-XMPL-11	ArrayExpress	sparse hyb b		A-TEST-1		scanning protocol	sparse scan3	imagefile2.TIFF	a bit blurry			TRANPRTCL10656	norm 4		NormData4.txt	preternatural calm					
diff --git a/examples/normalized_data/Data1.txt b/examples/normalized_data/Data1.txt
new file mode 100644
index 0000000..6d8b389
--- /dev/null
+++ b/examples/normalized_data/Data1.txt
@@ -0,0 +1,37 @@
+ATF	1																																	
+24	43																																	
+"Type=GenePix Results 1.2"																																		
+"DateTime=2003/01/23 11:27:59"																																		
+"Settings=E:\settings files\array.gps"																																		
+"GalFile="																																		
+"Scanner=GenePix 4000A"																																		
+"Comment="																																		
+"PixelSize=10"																																		
+"ImageName=635 nm	532 nm"																																	
+"FileName=\\Server\Data1.tif"																																		
+"PMTVolts=600	590"																																	
+"NormalizationFactor:RatioOfMedians=1.90064"																																		
+"NormalizationFactor:RatioOfMeans=1.90384"																																		
+"NormalizationFactor:MedianOfRatios=1.86554"																																		
+"NormalizationFactor:MeanOfRatios=1.23878"																																		
+"NormalizationFactor:RegressionRatio=2.45261"																																		
+"JpegImage=\\Server\Data1.jpg"																																		
+"RatioFormulation=W1/W2 (635 nm/532 nm)"																																		
+"Barcode="																																		
+"ImageOrigin=1240, 6320"																																		
+"JpegOrigin=1840, 7160"																																		
+"Creator=GenePix Pro 3.0.0.98"																																		
+"Temperature=1.41243"																																		
+"LaserPower=2.19092	0.863674"																																	
+"LaserOnTime=86000	85977"																																	
+"Block"	"Column"	"Row"	"Name"	"ID"	"X"	"Y"	"Dia."	"F635 Median"	"F635 Mean"	"F635 SD"	"B635 Median"	"B635 Mean"	"B635 SD"	"% > B635+1SD"	"% > B635+2SD"	"F635 % Sat."	"F532 Median"	"F532 Mean"	"F532 SD"	"B532 Median"	"B532 Mean"	"B532 SD"	"% > B532+1SD"	"% > B532+2SD"	"F532 % Sat."	"F Pixels"	"B Pixels"	"Sum of Medians"	"Sum of Means"	"F635 Median - B635"	"F532 Median - B532"	"F635 Mean - B635"	"F532 Mean - B532"	"Flags"
+1	1	1			2100	7300	90	240	240	54	40	44	21	100	100	0	502	497	98	81	86	32	100	100	0	52	376	621	616	200	421	200	416	0
+1	2	1			2410	7300	120	694	671	209	40	41	11	98	98	0	1406	1354	413	81	81	20	100	100	0	120	778	1979	1904	654	1325	631	1273	0
+1	3	1			2690	7320	120	501	531	267	41	41	10	99	99	0	998	1080	498	79	82	21	99	98	0	120	810	1379	1491	460	919	490	1001	0
+1	4	1			2980	7310	120	783	770	284	40	41	10	99	96	0	1034	1014	331	81	83	22	100	99	0	120	796	1696	1663	743	953	730	933	0
+1	5	1			3260	7320	130	457	434	219	40	41	10	100	99	0	998	938	493	81	83	21	100	98	0	120	930	1334	1251	417	917	394	857	0
+1	6	1			3570	7310	130	166	172	67	40	41	10	94	94	0	382	388	112	82	83	20	99	98	0	120	914	426	438	126	300	132	306	0
+1	7	1			3860	7320	100	63	65	17	40	41	10	76	55	0	182	188	53	79	80	20	100	96	0	80	552	126	134	23	103	25	109	0
+1	8	1			4130	7330	120	415	392	122	40	41	10	96	95	0	793	779	215	79	81	20	98	97	0	120	816	1089	1052	375	714	352	700	0
+1	9	1			4430	7320	120	153	155	61	39	41	10	97	94	0	374	382	125	80	82	20	99	99	0	120	796	408	418	114	294	116	302	0
+1	10	1			4730	7330	120	865	826	343	40	41	10	100	98	0	1011	941	389	79	81	21	99	98	0	120	810	1757	1648	825	932	786	862	0
diff --git a/examples/normalized_data/Data2.txt b/examples/normalized_data/Data2.txt
new file mode 100644
index 0000000..6975cfa
--- /dev/null
+++ b/examples/normalized_data/Data2.txt
@@ -0,0 +1,40 @@
+ATF	1																																	
+27	43																																	
+"Type=GenePix Results 1.4"																																		
+"DateTime=2003/05/19 15:39:10"																																		
+"Settings=E:\settings files\arrays.gps"																																		
+"GalFile="																																		
+"Scanner=GenePix 4000A [54807]"																																		
+"Comment="																																		
+"PixelSize=10"																																		
+"ImageName=635 nm	532 nm"																																	
+"FileName=\\Server\Data2.tif"																																		
+"PMTVolts=680	610"																																	
+"ScanPower=100	100"																																	
+"FocusPosition=0"																																		
+"NormalizationFactor:RatioOfMedians=0.93502"																																		
+"NormalizationFactor:RatioOfMeans=0.917873"																																		
+"NormalizationFactor:MedianOfRatios=0.933921"																																		
+"NormalizationFactor:MeanOfRatios=0.954675"																																		
+"NormalizationFactor:RegressionRatio=0.787143"																																		
+"JpegImage=\\Server\Data2.jpg"																																		
+"RatioFormulation=W1/W2 (635 nm/532 nm)"																																		
+"Barcode="																																		
+"ImageOrigin=560, 5360"																																		
+"JpegOrigin=1650, 6810"																																		
+"Creator=GenePix Pro 3.0.6.90"																																		
+"Temperature=41.3353"																																		
+"LaserPower=1.50975	1.13071"																																	
+"LaserOnTime=41930	44902"																																	
+"Supplier="																																		
+"Block"	"Column"	"Row"	"Name"	"ID"	"X"	"Y"	"Dia."	"F635 Median"	"F635 Mean"	"F635 SD"	"B635 Median"	"B635 Mean"	"B635 SD"	"% > B635+1SD"	"% > B635+2SD"	"F635 % Sat."	"F532 Median"	"F532 Mean"	"F532 SD"	"B532 Median"	"B532 Mean"	"B532 SD"	"% > B532+1SD"	"% > B532+2SD"	"F532 % Sat."	"F Pixels"	"B Pixels"	"Sum of Medians"	"Sum of Means"	"F635 Median - B635"	"F532 Median - B532"	"F635 Mean - B635"	"F532 Mean - B532"	"Flags"
+1	1	1	""	""	2040	7130	110	112	120	55	49	53	24	80	63	0	191	200	78	50	51	13	100	100	0	80	656	204	221	63	141	71	150	0
+1	2	1	""	""	2330	7120	120	412	435	197	51	55	26	95	95	0	731	843	375	50	51	11	99	99	0	120	820	1042	1177	361	681	384	793	0
+1	3	1	""	""	2610	7130	120	297	367	232	52	56	25	95	90	0	417	587	397	51	52	12	97	95	0	120	820	611	851	245	366	315	536	0
+1	4	1	""	""	2900	7120	120	214	297	230	50	54	25	92	88	0	272	412	327	51	52	12	94	93	0	120	820	385	608	164	221	247	361	0
+1	5	1	""	""	3200	7130	120	355	386	203	52	56	26	92	91	0	683	701	245	51	52	12	100	100	0	120	820	935	984	303	632	334	650	0
+1	6	1	""	""	3500	7110	120	134	135	56	52	55	26	82	71	0	213	209	61	51	52	14	95	93	0	120	810	244	241	82	162	83	158	0
+1	7	1	""	""	3770	7120	80	79	80	33	50	54	27	51	26	0	100	100	26	51	52	11	98	82	0	52	340	78	79	29	49	30	49	0
+1	8	1	""	""	4070	7120	120	332	339	152	55	59	27	92	90	0	609	613	227	52	52	12	95	95	0	120	820	834	845	277	557	284	561	0
+1	9	1	""	""	4370	7090	110	167	179	82	53	57	26	91	86	0	240	254	102	52	54	15	97	96	0	80	616	302	328	114	188	126	202	0
+1	10	1	""	""	4650	7110	130	241	291	200	54	57	25	90	85	0	371	522	320	51	52	12	99	99	0	120	930	507	708	187	320	237	471	0
diff --git a/examples/normalized_data/NormData1.txt b/examples/normalized_data/NormData1.txt
new file mode 100644
index 0000000..ade2275
--- /dev/null
+++ b/examples/normalized_data/NormData1.txt
@@ -0,0 +1,11 @@
+MetaColumn	MetaRow	Column	Row	Reporter Identifier	"Ratio of Medians"	"Ratio of Means"	"Median of Ratios"	"Mean of Ratios"	"Ratios SD"	"Rgn Ratio"	"Rgn R�"	"Log Ratio"
+1	1	1	1	A102340	0.475	0.481	0.494	0.491	0.129	0.413	0.731	-1.074
+1	1	2	1	A102341	0.494	0.496	0.5	0.599	0.409	0.433	0.777	-1.019
+1	1	3	1	A102342	0.501	0.49	0.493	0.641	1.28	0.46	0.837	-0.998
+1	1	4	1	A102343	0.78	0.782	0.819	1.372	3.252	0.675	0.722	-0.359
+1	1	5	1	A102344	0.455	0.46	0.45	0.6	0.632	0.382	0.751	-1.137
+1	1	6	1	A102345	0.42	0.431	0.473	0.696	2.121	0.362	0.589	-1.252
+1	1	7	1	A102346	0.223	0.229	0.226	0.313	0.289	0.16	0.283	-2.163
+1	1	8	1	A102347	0.525	0.503	0.522	0.741	1.71	0.445	0.754	-0.929
+1	1	9	1	A102348	0.388	0.384	0.368	0.443	0.369	0.351	0.721	-1.367
+1	1	10	1	A102349	0.885	0.912	0.923	1.477	3.031	0.751	0.698	-0.176
diff --git a/examples/normalized_data/NormData2.txt b/examples/normalized_data/NormData2.txt
new file mode 100644
index 0000000..52c7687
--- /dev/null
+++ b/examples/normalized_data/NormData2.txt
@@ -0,0 +1,11 @@
+Reporter Identifier	"Ratio of Medians"	"Ratio of Means"	"Median of Ratios"	"Mean of Ratios"	"Ratios SD"	"Rgn Ratio"	"Rgn R�"	"Log Ratio"
+A102340	0.447	0.473	0.502	0.428	2.467	0.472	0.332	-1.162
+A102341	0.53	0.484	0.477	0.503	3.009	0.418	0.611	-0.916
+A102342	0.669	0.588	0.592	0.604	3.17	0.511	0.592	-0.579
+A102343	0.742	0.684	0.755	0.675	3.312	0.576	0.412	-0.43
+A102344	0.479	0.514	0.506	0.502	2.251	0.507	0.593	-1.061
+A102345	0.506	0.525	0.546	0.488	3.047	0.532	0.398	-0.982
+A102346	0.592	0.612	1.025	0.705	2.93	1.819	0.071	-0.757
+A102347	0.497	0.506	0.543	0.522	3.195	0.482	0.645	-1.008
+A102348	0.606	0.624	0.669	0.614	2.841	0.577	0.414	-0.722
+A102349	0.584	0.503	0.457	0.508	2.361	0.481	0.581	-0.775
diff --git a/examples/normalized_data/normalized_data.png b/examples/normalized_data/normalized_data.png
new file mode 100644
index 0000000..bf1b7e1
Binary files /dev/null and b/examples/normalized_data/normalized_data.png differ
diff --git a/examples/normalized_data/normalized_data.txt b/examples/normalized_data/normalized_data.txt
new file mode 100644
index 0000000..bc7b765
--- /dev/null
+++ b/examples/normalized_data/normalized_data.txt
@@ -0,0 +1,40 @@
+# Simple example summary spreadsheet for tab2mage.pl script.																					
+# This file illustrates how to incorporate normalized data into the spreadsheet.																					
+# Lines starting with a '#' are treated as comments and are ignored.																					
+																					
+Experiment section																					
+domain	ebi.ac.uk																				
+accession	E-EXML-4																				
+quality_control	dye_swap_quality_control																				
+experiment_design_type	strain_or_line_design																				
+name	Transcriptome analysis of invasive verses non-invasive strains of budding yeast 																				
+description	An experiment was performed to...																				
+release_date	2004-08-30																				
+submission_date	2004-07-28																				
+submitter	John Falstaff																				
+# The publication details below are fictitious																					
+publication_title	Comparison of the transcriptomes of invasive verses non-invasive strains of budding yeast 																				
+authors	John Falstaff; Robin Goodfellow																				
+journal	Nature Genetics																				
+volume	12																				
+issue	4																				
+pages	123-456																				
+year	2004																				
+																					
+# Protocol text may be formatted using standard HTML tags																					
+Protocol section																					
+accession	text	name																			
+P-EXML-1	Cells were grown in YPD (1% yeast extract/2% peptone/2% glucose) to an OD600 of approximately 0.8	Yeast growth																			
+P-EXML-2	Your protocol text here	Yeast cell harvesting																			
+P-EXML-3	Your protocol text here	Cell lysis and RNA prep																			
+P-EXML-4	Your protocol text here	cDNA labeling																			
+P-EXML-5	Your protocol text here	Hybridization																			
+P-EXML-6	Your protocol text here	Scanning																			
+P-EXML-7	Your protocol text here	Log Ratio Normalization																			
+																					
+Hybridization section																					
+File[raw]	File[normalized]	NormalizationType	Protocol[normalization]	Array[accession]	Array[serial]	Protocol[grow]	Protocol[treatment]	Protocol[extraction]	Protocol[labeling]	Protocol[hybridization]	Protocol[scanning]	BioSource	BioSourceMaterial	SampleMaterial	ExtractMaterial	LabeledExtractMaterial	Dye	FactorValue[StrainOrLine]	BioMaterialCharacteristics[Organism]	BioMaterialCharacteristics[StrainOrLine]	BioMaterialCharacteristics[Sex]
+Data1.txt	NormData1.txt	log_ratio	P-EXML-7	A-EXML-1	244532	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	S288C	whole_organism	whole_organism	total_RNA	synthetic_DNA	Cy5	S288C	Saccharomyces cerevisiae	S288C	mating_type_a
+Data1.txt	NormData1.txt	log_ratio	P-EXML-7	A-EXML-1	244532	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	Sigma1278b	whole_organism	whole_organism	total_RNA	synthetic_DNA	Cy3	Sigma1278b	Saccharomyces cerevisiae	Sigma1278b	mating_type_a
+Data2.txt	NormData2.txt	log_ratio	P-EXML-7	A-EXML-1	244533	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	S288C	whole_organism	whole_organism	total_RNA	synthetic_DNA	Cy3	S288C	Saccharomyces cerevisiae	S288C	mating_type_a
+Data2.txt	NormData2.txt	log_ratio	P-EXML-7	A-EXML-1	244533	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	Sigma1278b	whole_organism	whole_organism	total_RNA	synthetic_DNA	Cy5	Sigma1278b	Saccharomyces cerevisiae	Sigma1278b	mating_type_a
diff --git a/examples/other_QTs.txt b/examples/other_QTs.txt
new file mode 100644
index 0000000..8f5ec43
--- /dev/null
+++ b/examples/other_QTs.txt
@@ -0,0 +1,27 @@
+# Example custom QT list for tab2mage and expt_check						
+#						
+# $Id: other_QTs.txt 619 2006-01-05 13:03:12Z tfrayner $						
+#						
+# Note: the information below is not fully curated, and is provided solely						
+# as an example of the QT file format. Use it at your own risk!						
+#						
+# Name	DataType	Scale	MAGE Subclass	isBackground	ConfidenceIndicator target QT	Channel	Description
+>>>ArrayVision[Imaging Research]						
+Ch2 Dens - Levels	float	linear_scale	SpecializedQuantitationType	0		Cy3
+Ch2 SD - Levels	float	linear_scale	Error	0	Ch2 Dens - Levels	Cy3
+Ch2 Area - mm2	float	linear_scale	SpecializedQuantitationType	1		Cy3
+Ch2 Bkgd	float	linear_scale	MeasuredSignal	1		Cy3
+Ch2 sDens	float	linear_scale	MeasuredSignal	0		Cy3
+Ch2 S/N	float	linear_scale	Ratio	0		Cy3
+Ch2 Flag	boolean	unscaled	PresentAbsent	0		Cy3
+Ch1 Dens - Levels	float	linear_scale	SpecializedQuantitationType	0		Cy5
+Ch1 SD - Levels	float	linear_scale	Error	0	Ch1 Dens - Levels	Cy5
+Ch1 Area - mm2	float	linear_scale	SpecializedQuantitationType	1		Cy5
+Ch1 Bkgd	float	linear_scale	MeasuredSignal	1		Cy5
+Ch1 sDens	float	linear_scale	MeasuredSignal	0		Cy5
+Ch1 S/N	float	linear_scale	Ratio	0		Cy5
+Ch1 Flag	boolean	unscaled	PresentAbsent	0		Cy5
+>>>Miscellaneous[ebi.ac.uk]						
+RMA	float	linear_scale	DerivedSignal	0		
+Normalized Intensity	float	linear_scale	DerivedSignal	0		
+Normalized Log Ratio	float	log_base_2	Ratio	0		
diff --git a/examples/parameters/Data1.txt b/examples/parameters/Data1.txt
new file mode 100644
index 0000000..157be04
--- /dev/null
+++ b/examples/parameters/Data1.txt
@@ -0,0 +1,37 @@
+ATF	1.0
+24	43    
+"Type=GenePix Results 1.2"
+"DateTime=2003/01/23 11:27:59"
+"Settings=E:\settings files\array.gps"
+"GalFile="
+"Scanner=GenePix 4000A"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm	532 nm"
+"FileName=\\Server\Data1.tif"
+"PMTVolts=600	590"
+"NormalizationFactor:RatioOfMedians=1.90064"
+"NormalizationFactor:RatioOfMeans=1.90384"
+"NormalizationFactor:MedianOfRatios=1.86554"
+"NormalizationFactor:MeanOfRatios=1.23878"
+"NormalizationFactor:RegressionRatio=2.45261"
+"JpegImage=\\Server\Data1.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=1240, 6320"
+"JpegOrigin=1840, 7160"
+"Creator=GenePix Pro 3.0.0.98"
+"Temperature=1.41243"
+"LaserPower=2.19092	0.863674"
+"LaserOnTime=86000	85977"
+"Block"	"Column"	"Row"	"Name"	"ID"	"X"	"Y"	"Dia."	"F635 Median"	"F635 Mean"	"F635 SD"	"B635 Median"	"B635 Mean"	"B635 SD"	"% > B635+1SD"	"% > B635+2SD"	"F635 % Sat."	"F532 Median"	"F532 Mean"	"F532 SD"	"B532 Median"	"B532 Mean"	"B532 SD"	"% > B532+1SD"	"% > B532+2SD"	"F532 % Sat."	"Ratio of Medians"	"Ratio of Means"	"Median of Ratios"	"Mean of Ratios"	"Ratios SD"	"Rgn Ratio"	"Rgn R�"	"F Pixels"	"B Pixels"	"Sum of Medians"	"Sum of Means"	"Log Ratio"	"F635 Median - B635"	"F532 Median - B53 [...]
+1	1	1			2100	7300	90	240	240	54	40	44	21	100	100	0	502	497	98	81	86	32	100	100	0	0.475	0.481	0.494	0.491	0.129	0.413	0.731	52	376	621	616	-1.074	200	421	200	416	0
+1	2	1			2410	7300	120	694	671	209	40	41	11	98	98	0	1406	1354	413	81	81	20	100	100	0	0.494	0.496	0.500	0.599	0.409	0.433	0.777	120	778	1979	1904	-1.019	654	1325	631	1273	0
+1	3	1			2690	7320	120	501	531	267	41	41	10	99	99	0	998	1080	498	79	82	21	99	98	0	0.501	0.490	0.493	0.641	1.280	0.460	0.837	120	810	1379	1491	-0.998	460	919	490	1001	0
+1	4	1			2980	7310	120	783	770	284	40	41	10	99	96	0	1034	1014	331	81	83	22	100	99	0	0.780	0.782	0.819	1.372	3.252	0.675	0.722	120	796	1696	1663	-0.359	743	953	730	933	0
+1	5	1			3260	7320	130	457	434	219	40	41	10	100	99	0	998	938	493	81	83	21	100	98	0	0.455	0.460	0.450	0.600	0.632	0.382	0.751	120	930	1334	1251	-1.137	417	917	394	857	0
+1	6	1			3570	7310	130	166	172	67	40	41	10	94	94	0	382	388	112	82	83	20	99	98	0	0.420	0.431	0.473	0.696	2.121	0.362	0.589	120	914	426	438	-1.252	126	300	132	306	0
+1	7	1			3860	7320	100	63	65	17	40	41	10	76	55	0	182	188	53	79	80	20	100	96	0	0.223	0.229	0.226	0.313	0.289	0.160	0.283	80	552	126	134	-2.163	23	103	25	109	0
+1	8	1			4130	7330	120	415	392	122	40	41	10	96	95	0	793	779	215	79	81	20	98	97	0	0.525	0.503	0.522	0.741	1.710	0.445	0.754	120	816	1089	1052	-0.929	375	714	352	700	0
+1	9	1			4430	7320	120	153	155	61	39	41	10	97	94	0	374	382	125	80	82	20	99	99	0	0.388	0.384	0.368	0.443	0.369	0.351	0.721	120	796	408	418	-1.367	114	294	116	302	0
+1	10	1			4730	7330	120	865	826	343	40	41	10	100	98	0	1011	941	389	79	81	21	99	98	0	0.885	0.912	0.923	1.477	3.031	0.751	0.698	120	810	1757	1648	-0.176	825	932	786	862	0
diff --git a/examples/parameters/Data2.txt b/examples/parameters/Data2.txt
new file mode 100644
index 0000000..f26132d
--- /dev/null
+++ b/examples/parameters/Data2.txt
@@ -0,0 +1,40 @@
+ATF	1.0
+27	43    
+"Type=GenePix Results 1.4"
+"DateTime=2003/05/19 15:39:10"
+"Settings=E:\settings files\arrays.gps"
+"GalFile="
+"Scanner=GenePix 4000A [54807]"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm	532 nm"
+"FileName=\\Server\Data2.tif"
+"PMTVolts=680	610"
+"ScanPower=100	100"
+"FocusPosition=0"
+"NormalizationFactor:RatioOfMedians=0.93502"
+"NormalizationFactor:RatioOfMeans=0.917873"
+"NormalizationFactor:MedianOfRatios=0.933921"
+"NormalizationFactor:MeanOfRatios=0.954675"
+"NormalizationFactor:RegressionRatio=0.787143"
+"JpegImage=\\Server\Data2.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=560, 5360"
+"JpegOrigin=1650, 6810"
+"Creator=GenePix Pro 3.0.6.90"
+"Temperature=41.3353"
+"LaserPower=1.50975	1.13071"
+"LaserOnTime=41930	44902"
+"Supplier="
+"Block"	"Column"	"Row"	"Name"	"ID"	"X"	"Y"	"Dia."	"F635 Median"	"F635 Mean"	"F635 SD"	"B635 Median"	"B635 Mean"	"B635 SD"	"% > B635+1SD"	"% > B635+2SD"	"F635 % Sat."	"F532 Median"	"F532 Mean"	"F532 SD"	"B532 Median"	"B532 Mean"	"B532 SD"	"% > B532+1SD"	"% > B532+2SD"	"F532 % Sat."	"Ratio of Medians"	"Ratio of Means"	"Median of Ratios"	"Mean of Ratios"	"Ratios SD"	"Rgn Ratio"	"Rgn R�"	"F Pixels"	"B Pixels"	"Sum of Medians"	"Sum of Means"	"Log Ratio"	"F635 Median - B635"	"F532 Median - B53 [...]
+1	1	1	""	""	2040	7130	110	112	120	55	49	53	24	80	63	0	191	200	78	50	51	13	100	100	0	0.447	0.473	0.502	0.428	2.467	0.472	0.332	80	656	204	221	-1.162	63	141	71	150	0
+1	2	1	""	""	2330	7120	120	412	435	197	51	55	26	95	95	0	731	843	375	50	51	11	99	99	0	0.530	0.484	0.477	0.503	3.009	0.418	0.611	120	820	1042	1177	-0.916	361	681	384	793	0
+1	3	1	""	""	2610	7130	120	297	367	232	52	56	25	95	90	0	417	587	397	51	52	12	97	95	0	0.669	0.588	0.592	0.604	3.170	0.511	0.592	120	820	611	851	-0.579	245	366	315	536	0
+1	4	1	""	""	2900	7120	120	214	297	230	50	54	25	92	88	0	272	412	327	51	52	12	94	93	0	0.742	0.684	0.755	0.675	3.312	0.576	0.412	120	820	385	608	-0.430	164	221	247	361	0
+1	5	1	""	""	3200	7130	120	355	386	203	52	56	26	92	91	0	683	701	245	51	52	12	100	100	0	0.479	0.514	0.506	0.502	2.251	0.507	0.593	120	820	935	984	-1.061	303	632	334	650	0
+1	6	1	""	""	3500	7110	120	134	135	56	52	55	26	82	71	0	213	209	61	51	52	14	95	93	0	0.506	0.525	0.546	0.488	3.047	0.532	0.398	120	810	244	241	-0.982	82	162	83	158	0
+1	7	1	""	""	3770	7120	80	79	80	33	50	54	27	51	26	0	100	100	26	51	52	11	98	82	0	0.592	0.612	1.025	0.705	2.930	1.819	0.071	52	340	78	79	-0.757	29	49	30	49	0
+1	8	1	""	""	4070	7120	120	332	339	152	55	59	27	92	90	0	609	613	227	52	52	12	95	95	0	0.497	0.506	0.543	0.522	3.195	0.482	0.645	120	820	834	845	-1.008	277	557	284	561	0
+1	9	1	""	""	4370	7090	110	167	179	82	53	57	26	91	86	0	240	254	102	52	54	15	97	96	0	0.606	0.624	0.669	0.614	2.841	0.577	0.414	80	616	302	328	-0.722	114	188	126	202	0
+1	10	1	""	""	4650	7110	130	241	291	200	54	57	25	90	85	0	371	522	320	51	52	12	99	99	0	0.584	0.503	0.457	0.508	2.361	0.481	0.581	120	930	507	708	-0.775	187	320	237	471	0
diff --git a/examples/parameters/parameters.png b/examples/parameters/parameters.png
new file mode 100644
index 0000000..dc7c4c4
Binary files /dev/null and b/examples/parameters/parameters.png differ
diff --git a/examples/parameters/parameters.txt b/examples/parameters/parameters.txt
new file mode 100644
index 0000000..5810839
--- /dev/null
+++ b/examples/parameters/parameters.txt
@@ -0,0 +1,39 @@
+# Simple example summary spreadsheet for tab2mage.pl script.																							
+# This file illustrates how channels should be combined when describing a two-color hybridization (e.g.  Cy3/Cy5).																							
+# Lines starting with a '#' are treated as comments and are ignored.																							
+																							
+Experiment section																							
+domain	ebi.ac.uk																						
+accession	E-EXML-6																						
+quality_control	dye_swap_quality_control																						
+experiment_design_type	strain_or_line_design																						
+name	Transcriptome analysis of invasive verses non-invasive strains of budding yeast 																						
+description	An experiment was performed to...																						
+release_date	2004-08-30																						
+submission_date	2004-07-28																						
+submitter	John Falstaff																						
+# The publication details below are fictitious																							
+publication_title	Comparison of the transcriptomes of invasive verses non-invasive strains of budding yeast 																						
+authors	John Falstaff; Robin Goodfellow																						
+journal	Nature Genetics																						
+volume	12																						
+issue	4																						
+pages	123-456																						
+year	2004																						
+																							
+# Protocol text may be formatted using standard HTML tags				# Note that parameter units should match those in the MAGE model.																			
+Protocol section																							
+accession	text	name	parameters																				
+P-EXML-1	Cells were grown in YPD (1% yeast extract/2% peptone/2% glucose) to an OD600 of approximately 0.8	Yeast growth	growth temperature (degree_C); pH																				
+P-EXML-2	Your protocol text here	Yeast cell harvesting	pellet weight (mg)																				
+P-EXML-3	Your protocol text here	Cell lysis and RNA prep																					
+P-EXML-4	Your protocol text here	cDNA labeling																					
+P-EXML-5	Your protocol text here	Hybridization	hyb temp (degree_C); hyb volume (uL)																				
+P-EXML-6	Your protocol text here	Scanning																					
+																							
+Hybridization section																							
+File[raw]	Array[accession]	Array[serial]	Protocol[grow]	Protocol[treatment]	Protocol[extraction]	Protocol[labeling]	Protocol[hybridization]	Protocol[scanning]	Parameter[growth temperature]	Parameter[pH]	Parameter[pellet weight]	Parameter[hyb temp]	Parameter[hyb volume]	BioSource	BioSourceMaterial	SampleMaterial	ExtractMaterial	LabeledExtractMaterial	Dye	FactorValue[StrainOrLine]	BioMaterialCharacteristics[Organism]	BioMaterialCharacteristics[StrainOrLine]	BioMaterialCharacteristics[Sex]
+Data1.txt	A-EXML-1	244532	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	30	6	10	56	50	S288C	whole_organism	whole_organism	total_RNA	synthetic_DNA	Cy5	S288C	Saccharomyces cerevisiae	S288C	mating_type_a
+Data1.txt	A-EXML-1	244532	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	31	6	12	56	50	Sigma1278b	whole_organism	whole_organism	total_RNA	synthetic_DNA	Cy3	Sigma1278b	Saccharomyces cerevisiae	Sigma1278b	mating_type_a
+Data2.txt	A-EXML-1	244533	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	30	6	10	55	50	S288C	whole_organism	whole_organism	total_RNA	synthetic_DNA	Cy3	S288C	Saccharomyces cerevisiae	S288C	mating_type_a
+Data2.txt	A-EXML-1	244533	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	31	6	12	55	50	Sigma1278b	whole_organism	whole_organism	total_RNA	synthetic_DNA	Cy5	Sigma1278b	Saccharomyces cerevisiae	Sigma1278b	mating_type_a
diff --git a/examples/real/E-MEXP-880.png b/examples/real/E-MEXP-880.png
new file mode 100644
index 0000000..13cb790
Binary files /dev/null and b/examples/real/E-MEXP-880.png differ
diff --git a/examples/real/E-MEXP-880.txt b/examples/real/E-MEXP-880.txt
new file mode 100644
index 0000000..78ebaba
--- /dev/null
+++ b/examples/real/E-MEXP-880.txt
@@ -0,0 +1,68 @@
+"Experiment section"																														
+"domain"	"ebi.ac.uk"																													
+"accession"	"E-MEXP-880"																													
+"name"	"BF-H.sapiens-HOXAmethylation"																													
+"description"	"To evaluate DNA methylation profile associated with breast cancer in 125 kB region of HOXA cluster we analyzed DNA from normal cancerous breast specimens and cell lines. Analysis was performed on the tiling array covering entire HOXA region with 500bp resolution. Immunoprecipitation with anti-methylcytosine antibody was for target preparation."																													
+"experiment_design_type"	"disease_state_design"																													
+"submitter"	"Petr Novak"																													
+"submitter_email"	"futscherlab at azcc.arizona.edu"																													
+"organization"	"University of Arizona"																													
+"address"	"1515 N Campbell Ave, Tucson, AZ, 85722, USA"																													
+"release_date"	"2006-09-30"																													
+"submission_date"	"2006-10-04"																													
+"publication_title"	"Epigenetic Inactivation of the HOXA Gene Cluster in Breast Cancer"																													
+																														
+"Protocol section"																														
+"accession"	"text"	"name"																												
+"BF_CGI_hyb"	"Cy3 and Cy5 labeled extracts were mixed and purified using Qiagen PCR purification kit according manufacturer's instruction.Hybridization Protocol:Slide Preparation Protocol:1. Remove all slides that you need from N2 storage and label accordingly. On the back side of the slide, scratch an outline of the corners of the array using a diamond-tipped scribe. The front is defined as the side the array is printed on and should have a label on it.Note: Only label with a pencil. Do [...]
+"HMEC"	"According manufacturer’s instructions (Clonetics, San Diego,CA) (Parameters: start time = 2, stop time = 8, time unit = weeks, min temperature = 37, max temperature = C, temperature unit = MEGM)"	"HMEC"																												
+"CCL-231-BT549"	"According Manufacturer's instruction (American Type Culture Collection, Rockville, MD) (Parameters: time unit = seconds, min temperature = 37, max temperature = C, temperature unit = RPMI)"	"CCL-231-BT549"																												
+"BREAST_TISSUE"	"Flash frozen specimens derived from normal or cancerous breast tissue were obtained from patients who underwent surgery for breast cancer, either lumpectomy or mastectomy, at the University Medical Center in Tucson, AZ., from 2003-2004.  All patients signed surgical and clinical research consents for tissue collection in accordance with the University of Arizona Institutional Review Board and HIPAA regulations (Parameters: time unit = seconds, temperature unit = C)"	"BRE [...]
+"MeCIP_Cy3"	"Random priming reactions in the presence of 3ul of Cy3-dUTP (Amersham Pharmacia) with the use of a BioPrime DNA labeling kit (Invitrogen) according manufacturer's protocol with one modification. Reaction time was prolonget to 16hrs (instead of 2) (Parameters: Amount of nucleic acid labeled = 200, Label used = Cy3, Amplification = none, Mass unit = Nano gram)"	"MeCIP_Cy3"																												
+"BF_CGI_Scan"	"Parameters of scanning and image analysis stored in raw data files.PMT values were set to obtain approximately same range of intensities in both channelsEach grinding was visually examined and anomalous spots were flagged for exclussion (Parameters: Scanning hardware = GenePix 4000B [Axon Instruments], Scanning software = GenePix Pro [Axon Instruments])"	"BF_CGI_Scan"																												
+"BF-MeCIP"	"DNA was isolated according tissue Protocol (QIAamp DNA Mini Kit-www.qiagen.com). Using anti-MethylCytosine antibody(Aviva System Biology, cat.no. AMM99021), methylated fraction of DNA was enriched by immunoprecipitation. Input- sonicated DNA serves as reference:Day 1First, 5ug of genomic DNA was resuspended in 50ul of SDS lysis buffer(1% SDS, 10mM EDTA, 50mM Tris-HCl, pH 8.1) + 450ul ChIP dilution buffer (0.01% SDS, 1.1% Triton X-100, 1.2mM EDTA, 16.7mM Tris-HCl, pH 8.1, 167m [...]
+"MeCIP_Cy5"	"Random priming reactions in the presence of 3ul of Cy3-dUTP (Amersham Pharmacia) with the use of a BioPrime DNA labeling kit (Invitrogen) according manufacturer's protocol with one modification. Reaction time was prolonget to 16hrs (instead of 2hrs) (Parameters: Amount of nucleic acid labeled = 200, Mass unit = Nano gram, Label used = Cy5, Amplification = none)"	"MeCIP_Cy5"																												
+"BF-HOXA_transf"	"The data from scanned microarray images were extracted using GenePix software. Median signal intensity of each spot was used for further analysis. Obtained data were were normalized in Excel. Only features corresponding HOXA tiling array were used in data processing but not features of CGI array. To exclude low quality spot data, the only spots with median of pixel intensity 1.5 higher than median of its local backgrounds where used for further analysis. Two step normal [...]
+																														
+"Hybridization section"																														
+"BioSource"	"BioMaterialCharacteristics[Organism]"	"BioMaterialCharacteristics[BioSourceProvider]"	"BioMaterialCharacteristics[BioSourceType]"	"BioMaterialCharacteristics[DevelopmentalStage]"	"BioMaterialCharacteristics[OrganismPart]"	"BioMaterialCharacteristics[Sex]"	"BioMaterialCharacteristics[Individual]"	"BioMaterialCharacteristics[DiseaseState]"	"BioMaterialCharacteristics[TargetedCellType]"	"BioMaterialCharacteristics[CellLine]"	"BioMaterialCharacteristics[ClinicalHistory]"	"Protoc [...]
+"1139T"	"Homo sapiens"	"University Medical Center, Tucson,AZ"	"frozen_sample"	"adult"	"mammary gland"	"female"	1139	"Lymph node metastasis"	"Epithelial"	1139	"Lymph node metastasis"	"BREAST_TISSUE"	"BF-MeCIP"	"E_1139T"	"genomic_DNA"	"L_1139T IP"	"molecular_mixture"	"MeCIP_Cy3"	"L_1139T"	"Cy3"	"BF_CGI_hyb"	"H_1139T_1139T_1"	"A-MEXP-568"	"BF_CGI_Scan"	"1139T.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Lymph node metastasis"	1139	"molecular_mixture"
+"1139T"	"Homo sapiens"	"University Medical Center, Tucson,AZ"	"frozen_sample"	"adult"	"mammary gland"	"female"	1139	"Lymph node metastasis"	"Epithelial"	1139	"Lymph node metastasis"	"BREAST_TISSUE"	"BF-MeCIP"	"E_1139T"	"genomic_DNA"	"L_1139T sonicated input DNA"	"genomic_DNA"	"MeCIP_Cy5"	"L_1139T_1"	"Cy5"	"BF_CGI_hyb"	"H_1139T_1139T_1"	"A-MEXP-568"	"BF_CGI_Scan"	"1139T.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Lymph node metastasis"	1139	"genomic_DNA"
+"120T"	"Homo sapiens"	"University Medical Center, Tucson,AZ"	"frozen_sample"	"adult"	"mammary gland"	"female"	120	"Infiltrating ductal carcinoma"	"Epithelial"	120	"Infiltrating ductal carcinoma"	"BREAST_TISSUE"	"BF-MeCIP"	"E_120T"	"genomic_DNA"	"L_120T IP"	"molecular_mixture"	"MeCIP_Cy3"	"L_120T"	"Cy3"	"BF_CGI_hyb"	"H_120T_120T_1"	"A-MEXP-568"	"BF_CGI_Scan"	"120T.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Infiltrating ductal carcinoma"	120	"molecular_mixture"
+"120T"	"Homo sapiens"	"University Medical Center, Tucson,AZ"	"frozen_sample"	"adult"	"mammary gland"	"female"	120	"Infiltrating ductal carcinoma"	"Epithelial"	120	"Infiltrating ductal carcinoma"	"BREAST_TISSUE"	"BF-MeCIP"	"E_120T"	"genomic_DNA"	"L_120 sonicated input DNA"	"genomic_DNA"	"MeCIP_Cy5"	"L_120T_1"	"Cy5"	"BF_CGI_hyb"	"H_120T_120T_1"	"A-MEXP-568"	"BF_CGI_Scan"	"120T.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Infiltrating ductal carcinoma"	120	"genomic_DNA"
+"2845T"	"Homo sapiens"	"University Medical Center, Tucson,AZ"	"frozen_sample"	"adult"	"mammary gland"	"female"	2845	"Infiltrating ductal carcinoma"	"Epithelial"	2845	"Infiltrating ductal carcinoma"	"BREAST_TISSUE"	"BF-MeCIP"	"E_2845T"	"genomic_DNA"	"L_2845T IP"	"molecular_mixture"	"MeCIP_Cy3"	"L_2845T"	"Cy3"	"BF_CGI_hyb"	"H_2845T_2845T_1"	"A-MEXP-568"	"BF_CGI_Scan"	"2845T.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Infiltrating ductal carcinoma"	2845	"molecular_mixture"
+"2845T"	"Homo sapiens"	"University Medical Center, Tucson,AZ"	"frozen_sample"	"adult"	"mammary gland"	"female"	2845	"Infiltrating ductal carcinoma"	"Epithelial"	2845	"Infiltrating ductal carcinoma"	"BREAST_TISSUE"	"BF-MeCIP"	"E_2845T"	"genomic_DNA"	"L_2845T sonicated input DNA"	"genomic_DNA"	"MeCIP_Cy5"	"L_2845T_1"	"Cy5"	"BF_CGI_hyb"	"H_2845T_2845T_1"	"A-MEXP-568"	"BF_CGI_Scan"	"2845T.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Infiltrating ductal carcinoma"	2845	"genomic_DNA"
+"5256T"	"Homo sapiens"	"University Medical Center, Tucson,AZ"	"frozen_sample"	"adult"	"mammary gland"	"female"	5256	"Infiltrating ductal carcinoma"	"Epithelial"	5256	"Infiltrating ductal carcinoma"	"BREAST_TISSUE"	"BF-MeCIP"	"E_5256"	"genomic_DNA"	"L_5256T IP"	"molecular_mixture"	"MeCIP_Cy3"	"L_5256T"	"Cy3"	"BF_CGI_hyb"	"H_5256T_5256T_1"	"A-MEXP-568"	"BF_CGI_Scan"	"5256T.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Infiltrating ductal carcinoma"	5256	"molecular_mixture"
+"5256T"	"Homo sapiens"	"University Medical Center, Tucson,AZ"	"frozen_sample"	"adult"	"mammary gland"	"female"	5256	"Infiltrating ductal carcinoma"	"Epithelial"	5256	"Infiltrating ductal carcinoma"	"BREAST_TISSUE"	"BF-MeCIP"	"E_5256"	"genomic_DNA"	"L_5256T sonicated input DNA"	"genomic_DNA"	"MeCIP_Cy5"	"L_5256T_1"	"Cy5"	"BF_CGI_hyb"	"H_5256T_5256T_1"	"A-MEXP-568"	"BF_CGI_Scan"	"5256T.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Infiltrating ductal carcinoma"	5256	"genomic_DNA"
+"5343N"	"Homo sapiens"	"University Medical Center, Tucson,AZ"	"frozen_sample"	"adult"	"mammary gland"	"female"	5343	"Normal"	"Epithelial"	5343		"BREAST_TISSUE"	"BF-MeCIP"	"E_5343N"	"genomic_DNA"	"L_5343N IP"	"molecular_mixture"	"MeCIP_Cy3"	"L_5343N"	"Cy3"	"BF_CGI_hyb"	"H_5343N_5343N_1"	"A-MEXP-568"	"BF_CGI_Scan"	"5343N.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"normal"	5343	"molecular_mixture"
+"5343N"	"Homo sapiens"	"University Medical Center, Tucson,AZ"	"frozen_sample"	"adult"	"mammary gland"	"female"	5343	"Normal"	"Epithelial"	5343		"BREAST_TISSUE"	"BF-MeCIP"	"E_5343N"	"genomic_DNA"	"L_5343N sonicated input DNA"	"genomic_DNA"	"MeCIP_Cy5"	"L_5343N_1"	"Cy5"	"BF_CGI_hyb"	"H_5343N_5343N_1"	"A-MEXP-568"	"BF_CGI_Scan"	"5343N.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"normal"	5343	"genomic_DNA"
+"5799T"	"Homo sapiens"	"University Medical Center, Tucson,AZ"	"frozen_sample"	"adult"	"mammary gland"	"female"	5799	"Infiltrating ductal carcinoma"	"Epithelial"	5799	"Infiltrating ductal carcinoma"	"BREAST_TISSUE"	"BF-MeCIP"	"E_5799T"	"genomic_DNA"	"L_5799T IP"	"molecular_mixture"	"MeCIP_Cy3"	"L_5799T"	"Cy3"	"BF_CGI_hyb"	"H_5799T_5799T_1"	"A-MEXP-568"	"BF_CGI_Scan"	"5799T.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Infiltrating ductal carcinoma"	5799	"molecular_mixture"
+"5799T"	"Homo sapiens"	"University Medical Center, Tucson,AZ"	"frozen_sample"	"adult"	"mammary gland"	"female"	5799	"Infiltrating ductal carcinoma"	"Epithelial"	5799	"Infiltrating ductal carcinoma"	"BREAST_TISSUE"	"BF-MeCIP"	"E_5799T"	"genomic_DNA"	"L_5799T sonicated input DNA"	"genomic_DNA"	"MeCIP_Cy5"	"L_5799T_1"	"Cy5"	"BF_CGI_hyb"	"H_5799T_5799T_1"	"A-MEXP-568"	"BF_CGI_Scan"	"5799T.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Infiltrating ductal carcinoma"	5799	"genomic_DNA"
+"6245T"	"Homo sapiens"	"University Medical Center, Tucson,AZ"	"frozen_sample"	"adult"	"mammary gland"	"female"	6245	"Infiltrating ductal carcinoma"	"Epithelial"	6245	"Infiltrating ductal carcinoma"	"BREAST_TISSUE"	"BF-MeCIP"	"E_6245T"	"genomic_DNA"	"L_6245T IP"	"molecular_mixture"	"MeCIP_Cy3"	"L_6245T"	"Cy3"	"BF_CGI_hyb"	"H_6245T_6245T_1"	"A-MEXP-568"	"BF_CGI_Scan"	"6245T.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Infiltrating ductal carcinoma"	6245	"molecular_mixture"
+"6245T"	"Homo sapiens"	"University Medical Center, Tucson,AZ"	"frozen_sample"	"adult"	"mammary gland"	"female"	6245	"Infiltrating ductal carcinoma"	"Epithelial"	6245	"Infiltrating ductal carcinoma"	"BREAST_TISSUE"	"BF-MeCIP"	"E_6245T"	"genomic_DNA"	"L_6245T sonicated input DNA"	"genomic_DNA"	"MeCIP_Cy5"	"L_6245T_1"	"Cy5"	"BF_CGI_hyb"	"H_6245T_6245T_1"	"A-MEXP-568"	"BF_CGI_Scan"	"6245T.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Infiltrating ductal carcinoma"	6245	"genomic_DNA"
+"6333N"	"Homo sapiens"	"University Medical Center, Tucson,AZ"	"frozen_sample"	"adult"	"mammary gland"	"female"	6333	"Normal"	"Epithelial"	6333		"BREAST_TISSUE"	"BF-MeCIP"	"E_6333N"	"genomic_DNA"	"L_6333N IP"	"molecular_mixture"	"MeCIP_Cy3"	"L_6333N"	"Cy3"	"BF_CGI_hyb"	"H_6333N_6333N_1"	"A-MEXP-568"	"BF_CGI_Scan"	"6333N.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"normal"	6333	"molecular_mixture"
+"6333N"	"Homo sapiens"	"University Medical Center, Tucson,AZ"	"frozen_sample"	"adult"	"mammary gland"	"female"	6333	"Normal"	"Epithelial"	6333		"BREAST_TISSUE"	"BF-MeCIP"	"E_6333N"	"genomic_DNA"	"L_6333N sonicated input DNA"	"genomic_DNA"	"MeCIP_Cy5"	"L_6333N_1"	"Cy5"	"BF_CGI_hyb"	"H_6333N_6333N_1"	"A-MEXP-568"	"BF_CGI_Scan"	"6333N.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"normal"	6333	"genomic_DNA"
+"7732N"	"Homo sapiens"	"University Medical Center, Tucson,AZ"	"frozen_sample"	"adult"	"mammary gland"	"female"	7732	"Normal"	"Epithelial"	7732		"BREAST_TISSUE"	"BF-MeCIP"	"E_7732N"	"genomic_DNA"	"L_7732N IP"	"molecular_mixture"	"MeCIP_Cy3"	"L_7732N"	"Cy3"	"BF_CGI_hyb"	"H_7732N_7732N_1"	"A-MEXP-568"	"BF_CGI_Scan"	"7732N.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"normal"	7732	"molecular_mixture"
+"7732N"	"Homo sapiens"	"University Medical Center, Tucson,AZ"	"frozen_sample"	"adult"	"mammary gland"	"female"	7732	"Normal"	"Epithelial"	7732		"BREAST_TISSUE"	"BF-MeCIP"	"E_7732N"	"genomic_DNA"	"L_7732N sonicated input DNA"	"genomic_DNA"	"MeCIP_Cy5"	"L_7732N_1"	"Cy5"	"BF_CGI_hyb"	"H_7732N_7732N_1"	"A-MEXP-568"	"BF_CGI_Scan"	"7732N.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"normal"	7732	"genomic_DNA"
+"7732T"	"Homo sapiens"	"University Medical Center, Tucson,AZ"	"frozen_sample"	"adult"	"mammary gland"	"female"	7732	"Infiltrating ductal carcinoma"	"Epithelial"	7732	"Infiltrating ductal carcinoma"	"BREAST_TISSUE"	"BF-MeCIP"	"E_7732T"	"genomic_DNA"	"L_7732T IP"	"molecular_mixture"	"MeCIP_Cy3"	"L_7732T"	"Cy3"	"BF_CGI_hyb"	"H_7732T_7732T_1"	"A-MEXP-568"	"BF_CGI_Scan"	"7732T.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Infiltrating ductal carcinoma"	7732	"molecular_mixture"
+"7732T"	"Homo sapiens"	"University Medical Center, Tucson,AZ"	"frozen_sample"	"adult"	"mammary gland"	"female"	7732	"Infiltrating ductal carcinoma"	"Epithelial"	7732	"Infiltrating ductal carcinoma"	"BREAST_TISSUE"	"BF-MeCIP"	"E_7732T"	"genomic_DNA"	"L_7732T sonicated input DNA"	"genomic_DNA"	"MeCIP_Cy5"	"L_7732T_1"	"Cy5"	"BF_CGI_hyb"	"H_7732T_7732T_1"	"A-MEXP-568"	"BF_CGI_Scan"	"7732T.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Infiltrating ductal carcinoma"	7732	"genomic_DNA"
+"7768T"	"Homo sapiens"	"University Medical Center, Tucson,AZ"	"frozen_sample"	"adult"	"mammary gland"	"female"	7768	"Infiltrating ductal carcinoma"	"Epithelial"	7768	"Infiltrating ductal carcinoma"	"BREAST_TISSUE"	"BF-MeCIP"	"E_7768T"	"genomic_DNA"	"L_7768T IP"	"molecular_mixture"	"MeCIP_Cy3"	"L_7768T"	"Cy3"	"BF_CGI_hyb"	"H_7768T_7768T_1"	"A-MEXP-568"	"BF_CGI_Scan"	"7768T.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Infiltrating ductal carcinoma"	7768	"molecular_mixture"
+"7768T"	"Homo sapiens"	"University Medical Center, Tucson,AZ"	"frozen_sample"	"adult"	"mammary gland"	"female"	7768	"Infiltrating ductal carcinoma"	"Epithelial"	7768	"Infiltrating ductal carcinoma"	"BREAST_TISSUE"	"BF-MeCIP"	"E_7768T"	"genomic_DNA"	"L_7768T sonicated input DNA"	"genomic_DNA"	"MeCIP_Cy5"	"L_7768T_1"	"Cy5"	"BF_CGI_hyb"	"H_7768T_7768T_1"	"A-MEXP-568"	"BF_CGI_Scan"	"7768T.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Infiltrating ductal carcinoma"	7768	"genomic_DNA"
+"7788T"	"Homo sapiens"	"University Medical Center, Tucson,AZ"	"frozen_sample"	"adult"	"mammary gland"	"female"	7788	"Infiltrating lobular carcinoma"	"Epithelial"	7788	"Infiltrating lobular carcinoma"	"BREAST_TISSUE"	"BF-MeCIP"	"E_7788T"	"genomic_DNA"	"L_7788T IP"	"molecular_mixture"	"MeCIP_Cy3"	"L_7788T"	"Cy3"	"BF_CGI_hyb"	"H_7788T_7788T_1"	"A-MEXP-568"	"BF_CGI_Scan"	"7788T.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Infiltrating lobular carcinoma"	7788	"molecular_mixture"
+"7788T"	"Homo sapiens"	"University Medical Center, Tucson,AZ"	"frozen_sample"	"adult"	"mammary gland"	"female"	7788	"Infiltrating lobular carcinoma"	"Epithelial"	7788	"Infiltrating lobular carcinoma"	"BREAST_TISSUE"	"BF-MeCIP"	"E_7788T"	"genomic_DNA"	"L_7788T sonicated input DNA"	"genomic_DNA"	"MeCIP_Cy5"	"L_7788T_1"	"Cy5"	"BF_CGI_hyb"	"H_7788T_7788T_1"	"A-MEXP-568"	"BF_CGI_Scan"	"7788T.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Infiltrating lobular carcinoma"	7788	"genomic_DNA"
+"9663T"	"Homo sapiens"	"University Medical Center, Tucson,AZ"	"frozen_sample"	"adult"	"mammary gland"	"female"	9663	"Lymph node metastasis"	"Epithelial"	9663	"Lymph node metastasis"	"BREAST_TISSUE"	"BF-MeCIP"	"E_9663T"	"genomic_DNA"	"L_9663T IP"	"molecular_mixture"	"MeCIP_Cy3"	"L_9663T"	"Cy3"	"BF_CGI_hyb"	"H_9663T_9663T_1"	"A-MEXP-568"	"BF_CGI_Scan"	"9663T.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Lymph node metastasis"	9663	"molecular_mixture"
+"9663T"	"Homo sapiens"	"University Medical Center, Tucson,AZ"	"frozen_sample"	"adult"	"mammary gland"	"female"	9663	"Lymph node metastasis"	"Epithelial"	9663	"Lymph node metastasis"	"BREAST_TISSUE"	"BF-MeCIP"	"E_9663T"	"genomic_DNA"	"L_9663T sonicated input DNA"	"genomic_DNA"	"MeCIP_Cy5"	"L_9663T_1"	"Cy5"	"BF_CGI_hyb"	"H_9663T_9663T_1"	"A-MEXP-568"	"BF_CGI_Scan"	"9663T.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Lymph node metastasis"	9663	"genomic_DNA"
+"BT 549-1"	"Homo sapiens"	"American Type Culture Collection (Rockville, MD)"		"adult"	"mammary gland"	"female"	"BT 549"	"Breast Cancer Cell line"	"Epithelial"	"BT 549"	"Breast Cancer Cell line"	"CCL-231-BT549"	"BF-MeCIP"	"E_BT 549-1"	"genomic_DNA"	"L_BT 549-1 IP"	"molecular_mixture"	"MeCIP_Cy3"	"L_BT 549-1"	"Cy3"	"BF_CGI_hyb"	"H_BT 549-1_BT 549-1_1"	"A-MEXP-568"	"BF_CGI_Scan"	"BT549-1.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Breast Cancer Cell line"	"BT 549"	"molecular_mixture"
+"BT 549-1"	"Homo sapiens"	"American Type Culture Collection (Rockville, MD)"		"adult"	"mammary gland"	"female"	"BT 549"	"Breast Cancer Cell line"	"Epithelial"	"BT 549"	"Breast Cancer Cell line"	"CCL-231-BT549"	"BF-MeCIP"	"E_BT 549-1"	"genomic_DNA"	"L_BT 549-1 sonicated input DNA"	"genomic_DNA"	"MeCIP_Cy5"	"L_BT 549-1_1"	"Cy5"	"BF_CGI_hyb"	"H_BT 549-1_BT 549-1_1"	"A-MEXP-568"	"BF_CGI_Scan"	"BT549-1.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Breast Cancer Cell line"	"BT 549"	"genomic_DNA"
+"BT 549-2"	"Homo sapiens"	"American Type Culture Collection (Rockville, MD)"		"adult"	"mammary gland"	"female"	"BT 549"	"Breast Cancer Cell line"	"Epithelial"	"BT 549"	"Breast Cancer Cell line"	"CCL-231-BT549"	"BF-MeCIP"	"E_BT 549-2"	"genomic_DNA"	"L_BT 549-2 IP"	"molecular_mixture"	"MeCIP_Cy3"	"L_BT 549-2"	"Cy3"	"BF_CGI_hyb"	"H_BT 549-2_BT 549-2_1"	"A-MEXP-568"	"BF_CGI_Scan"	"BT549-2.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Breast Cancer Cell line"	"BT 549"	"molecular_mixture"
+"BT 549-2"	"Homo sapiens"	"American Type Culture Collection (Rockville, MD)"		"adult"	"mammary gland"	"female"	"BT 549"	"Breast Cancer Cell line"	"Epithelial"	"BT 549"	"Breast Cancer Cell line"	"CCL-231-BT549"	"BF-MeCIP"	"E_BT 549-2"	"genomic_DNA"	"L_BT 549-2 sonicated input DNA"	"genomic_DNA"	"MeCIP_Cy5"	"L_BT 549-2_1"	"Cy5"	"BF_CGI_hyb"	"H_BT 549-2_BT 549-2_1"	"A-MEXP-568"	"BF_CGI_Scan"	"BT549-2.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Breast Cancer Cell line"	"BT 549"	"genomic_DNA"
+"HMEC1"	"Homo sapiens"	"Clonetics, San Diego, CA"		"adult"	"mammary gland"	"female"	"HMEC"	"Normal"	"Epithelial"	"HMEC"	"Human Mammary Epithelial Cell"	"HMEC"	"BF-MeCIP"	"E_HMEC1"	"genomic_DNA"	"L_HMEC1 IP"	"molecular_mixture"	"MeCIP_Cy3"	"L_HMEC1"	"Cy3"	"BF_CGI_hyb"	"H_HMEC1_HMEC1_1"	"A-MEXP-568"	"BF_CGI_Scan"	"HMEC1.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"normal"	"HMEC"	"molecular_mixture"
+"HMEC1"	"Homo sapiens"	"Clonetics, San Diego, CA"		"adult"	"mammary gland"	"female"	"HMEC"	"Normal"	"Epithelial"	"HMEC"	"Human Mammary Epithelial Cell"	"HMEC"	"BF-MeCIP"	"E_HMEC1"	"genomic_DNA"	"L_HMEC1 sonicated input DNA"	"genomic_DNA"	"MeCIP_Cy5"	"L_HMEC1_1"	"Cy5"	"BF_CGI_hyb"	"H_HMEC1_HMEC1_1"	"A-MEXP-568"	"BF_CGI_Scan"	"HMEC1.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"normal"	"HMEC"	"genomic_DNA"
+"HMEC2"	"Homo sapiens"	"Clonetics, San Diego, CA"		"adult"	"mammary gland"	"female"	"HMEC"	"Normal"	"Epithelial"	"HMEC"	"Human Mammary Epithelial Cell"	"HMEC"	"BF-MeCIP"	"E_HMEC2"	"genomic_DNA"	"L_HMEC2 IP"	"molecular_mixture"	"MeCIP_Cy3"	"L_HMEC2"	"Cy3"	"BF_CGI_hyb"	"H_HMEC2_HMEC2_1"	"A-MEXP-568"	"BF_CGI_Scan"	"HMEC2.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"normal"	"HMEC"	"molecular_mixture"
+"HMEC2"	"Homo sapiens"	"Clonetics, San Diego, CA"		"adult"	"mammary gland"	"female"	"HMEC"	"Normal"	"Epithelial"	"HMEC"	"Human Mammary Epithelial Cell"	"HMEC"	"BF-MeCIP"	"E_HMEC2"	"genomic_DNA"	"L_HMEC2 sonicated input DNA"	"genomic_DNA"	"MeCIP_Cy5"	"L_HMEC2_1"	"Cy5"	"BF_CGI_hyb"	"H_HMEC2_HMEC2_1"	"A-MEXP-568"	"BF_CGI_Scan"	"HMEC2.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"normal"	"HMEC"	"genomic_DNA"
+"HMEC3"	"Homo sapiens"	"Clonetics, San Diego, CA"		"adult"	"mammary gland"	"female"	"HMEC"	"Normal"	"Epithelial"	"HMEC"	"Human Mammary Epithelial Cell"	"HMEC"	"BF-MeCIP"	"E_HMEC3"	"genomic_DNA"	"L_HMEC3 IP"	"molecular_mixture"	"MeCIP_Cy3"	"L_HMEC3"	"Cy3"	"BF_CGI_hyb"	"H_HMEC3_HMEC3_1"	"A-MEXP-568"	"BF_CGI_Scan"	"HMEC3.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"normal"	"HMEC"	"molecular_mixture"
+"HMEC3"	"Homo sapiens"	"Clonetics, San Diego, CA"		"adult"	"mammary gland"	"female"	"HMEC"	"Normal"	"Epithelial"	"HMEC"	"Human Mammary Epithelial Cell"	"HMEC"	"BF-MeCIP"	"E_HMEC3"	"genomic_DNA"	"L_HMEC3 sonicated input DNA"	"genomic_DNA"	"MeCIP_Cy5"	"L_HMEC3_1"	"Cy5"	"BF_CGI_hyb"	"H_HMEC3_HMEC3_1"	"A-MEXP-568"	"BF_CGI_Scan"	"HMEC3.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"normal"	"HMEC"	"genomic_DNA"
+"MB-MDA-231-1"	"Homo sapiens"	"American Type Culture Collection (Rockville, MD)"		"adult"	"mammary gland"	"female"	"MB-MDA-231"	"Breast Cancer Cell line"	"Epithelial"	"MB-MDA-231"	"Breast Cancer Cell line"	"CCL-231-BT549"	"BF-MeCIP"	"E_MB-MDA-231-1"	"genomic_DNA"	"L_MB-MDA-231-1 IP"	"molecular_mixture"	"MeCIP_Cy3"	"L_MB-MDA-231-1"	"Cy3"	"BF_CGI_hyb"	"H_MB-MDA-231-1_MB-MDA-231-1_1"	"A-MEXP-568"	"BF_CGI_Scan"	"MDA-MB-231-1.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Breast Cancer Cell  [...]
+"MB-MDA-231-1"	"Homo sapiens"	"American Type Culture Collection (Rockville, MD)"		"adult"	"mammary gland"	"female"	"MB-MDA-231"	"Breast Cancer Cell line"	"Epithelial"	"MB-MDA-231"	"Breast Cancer Cell line"	"CCL-231-BT549"	"BF-MeCIP"	"E_MB-MDA-231-1"	"genomic_DNA"	"L_MB-MDA-231-1 sonicated input DNA"	"genomic_DNA"	"MeCIP_Cy5"	"L_MB-MDA-231-1_1"	"Cy5"	"BF_CGI_hyb"	"H_MB-MDA-231-1_MB-MDA-231-1_1"	"A-MEXP-568"	"BF_CGI_Scan"	"MDA-MB-231-1.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Breast [...]
+"MB-MDA-231-2"	"Homo sapiens"	"American Type Culture Collection (Rockville, MD)"		"adult"	"mammary gland"	"female"	"MB-MDA-231"	"Breast Cancer Cell line"	"Epithelial"	"MB-MDA-231"	"Breast Cancer Cell line"	"CCL-231-BT549"	"BF-MeCIP"	"E_MB-MDA-231-2"	"genomic_DNA"	"L_MB-MDA-231-2 IP"	"molecular_mixture"	"MeCIP_Cy3"	"L_MB-MDA-231-2"	"Cy3"	"BF_CGI_hyb"	"H_MB-MDA-231-2_MB-MDA-231-2_1"	"A-MEXP-568"	"BF_CGI_Scan"	"MDA-MB-231-2.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Breast Cancer Cell  [...]
+"MB-MDA-231-2"	"Homo sapiens"	"American Type Culture Collection (Rockville, MD)"		"adult"	"mammary gland"	"female"	"MB-MDA-231"	"Breast Cancer Cell line"	"Epithelial"	"MB-MDA-231"	"Breast Cancer Cell line"	"CCL-231-BT549"	"BF-MeCIP"	"E_MB-MDA-231-2"	"genomic_DNA"	"L_MB-MDA-231-2 sonicated input DNA"	"genomic_DNA"	"MeCIP_Cy5"	"L_MB-MDA-231-2_1"	"Cy5"	"BF_CGI_hyb"	"H_MB-MDA-231-2_MB-MDA-231-2_1"	"A-MEXP-568"	"BF_CGI_Scan"	"MDA-MB-231-2.gpr"	"BF-HOXA_transf"	"combined_data_HOXA.txt"	"Breast [...]
diff --git a/examples/real/E-TABM-102.png b/examples/real/E-TABM-102.png
new file mode 100644
index 0000000..20479a1
Binary files /dev/null and b/examples/real/E-TABM-102.png differ
diff --git a/examples/real/E-TABM-102.txt b/examples/real/E-TABM-102.txt
new file mode 100755
index 0000000..b9df9fd
--- /dev/null
+++ b/examples/real/E-TABM-102.txt
@@ -0,0 +1,56 @@
+Experiment section	
+domain	ebi.ac.uk
+accession	E-TABM-102
+quality_control	biological_replicate
+experiment_design_type	"compound_treatment_design,genetic_modification_design,time_series_design"
+name	"Transcription profiling of wild-type and ATF3 -/-mouse bone marrow macrophages stimulated with lipopolysaccharide after 1, 2, 4, 8 and 24 hours"
+description	Time course data from lipopolysaccharide-stimulated wild-type and ATF3 -/- mouse bone marrow macrophages were collected to investigate the transcriptional network in Toll-like recepter 4-activated macrophages.
+release_date	2006-05-30
+submission_date	2006-05-30
+submitter	Bruz Marzolf
+organization	Institute for Systems Biology
+publication_title	Systems biology approaches identify ATF3 as a negative regulator of Toll-like receptor 4
+authors	Mark Gilchrist; Vesteinn Thorsson; Bin Li; Alistair G. Rust; Martin Korb; Kathleen Kennedy; Tsonwin Hai; Hamid Bolouri and Alan Aderem
+journal	Nature
+volume	441
+issue	2006 May 11
+pages	173-178																				
+year	2006																				
+
+Protocol section																					
+accession	text	name	type																		
+P-TABM-gilchrist1	"Flush femurs from C57BL/6 mice (Jackson Laboratories) with complete RPMI (RPMI 1640 supplemented with 10% FCS, 2mM L-glutamine, 100 IU/mL penicillin and 100ug/mL streptomycin, all from Cellgro, Mediatech, except the FCS which is from from Hyclone). Plate bone marrow cells on non-tissue culture treated plastic in complete RPMI supplemented with recombinant human M-CSF (rhM-CSF) at 50 ng/mL (Chiron). On day 3, wash the cells two times with complete RPMI and then grow for [...]
+P-TABM-gilchrist2	"On day 7, stimulate the cells with 10 ng/mL lipopolysaccharide (S. Minnesota, List) for 1, 2, 4, 8 and 24 hours without changing the media."	Bone marrow macrophage stimulation	treatment																		
+
+Hybridization section																					
+File[raw]	File[normalized]	File[exp]	File[cdf]	Array[accession]	Array[serial]	Protocol[grow]	Protocol[treatment]	Protocol[extraction]	Protocol[labeling]	BioSource	BioSourceMaterial	BioMaterialCharacteristics[Organism]	BioMaterialCharacteristics[StrainOrLine]	BiomaterialCharacteristics[Genotype]	BioMaterialCharacteristics[Sex]	BioMaterialCharacteristics[CellType]	BioMaterialCharacteristics[GeneticModification]	FactorValue[Compound]	FactorValue[Dose(ng/mL)]	FactorValue[Time(h)]	FactorValue [...]
+20040603_03_LPS1-0.CEL	20040603_03_LPS1-0.CHP	20040603_03_LPS1-0.EXP	Mouse430_2.cdf	A-AFFY-45	4001319	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	LPS 0 hr Mouse 1	cell	Mus musculus	C57BL/6	wild_type	mixed_sex	macrophage		none	0	0	wild_type
+20040621_01_LPS2-0.CEL	20040621_01_LPS2-0.CHP	20040621_01_LPS2-0.EXP	Mouse430_2.cdf	A-AFFY-45	4001319	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	LPS 0 hr Mouse 2	cell	Mus musculus	C57BL/6	wild_type	mixed_sex	macrophage		none	0	0	wild_type
+20040622_01_LPS3-0.CEL	20040622_01_LPS3-0.CHP	20040622_01_LPS3-0.EXP	Mouse430_2.cdf	A-AFFY-45	4001319	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	LPS 0 hr Mouse 3	cell	Mus musculus	C57BL/6	wild_type	mixed_sex	macrophage		none	0	0	wild_type
+20040609_03_LPS1-60.CEL	20040609_03_LPS1-60.CHP	20040609_03_LPS1-60.EXP	Mouse430_2.cdf	A-AFFY-45	4001319	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	LPS 1 hr Mouse 1	cell	Mus musculus	C57BL/6	wild_type	mixed_sex	macrophage		lipopolysaccharide	10	1	wild_type
+20040621_04_LPS2-60.CEL	20040621_04_LPS2-60.CHP	20040621_04_LPS2-60.EXP	Mouse430_2.cdf	A-AFFY-45	4001319	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	LPS 1 hr Mouse 2	cell	Mus musculus	C57BL/6	wild_type	mixed_sex	macrophage		lipopolysaccharide	10	1	wild_type
+20040622_04_LPS3-60.CEL	20040622_04_LPS3-60.CHP	20040622_04_LPS3-60.EXP	Mouse430_2.cdf	A-AFFY-45	4001789	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	LPS 1 hr Mouse 3	cell	Mus musculus	C57BL/6	wild_type	mixed_sex	macrophage		lipopolysaccharide	10	1	wild_type
+20040603_04_LPS1-120.CEL	20040603_04_LPS1-120.CHP	20040603_04_LPS1-120.EXP	Mouse430_2.cdf	A-AFFY-45	4001319	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	LPS 2 hr Mouse 1	cell	Mus musculus	C57BL/6	wild_type	mixed_sex	macrophage		lipopolysaccharide	10	2	wild_type
+20040621_06_LPS2-120.CEL	20040621_06_LPS2-120.CHP	20040621_06_LPS2-120.EXP	Mouse430_2.cdf	A-AFFY-45	4001319	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	LPS 2 hr Mouse 2	cell	Mus musculus	C57BL/6	wild_type	mixed_sex	macrophage		lipopolysaccharide	10	2	wild_type
+20040622_06_LPS3-120.CEL	20040622_06_LPS3-120.CHP	20040622_06_LPS3-120.EXP	Mouse430_2.cdf	A-AFFY-45	4001789	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	LPS 2 hr Mouse 3	cell	Mus musculus	C57BL/6	wild_type	mixed_sex	macrophage		lipopolysaccharide	10	2	wild_type
+20050310_03_C57_A-240.CEL	20050310_03_C57_A-240.CHP	20050310_03_C57_A-240.EXP	Mouse430_2.cdf	A-AFFY-45	4005732	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	LPS 4 hr Mouse 1	cell	Mus musculus	C57BL/6	wild_type	mixed_sex	macrophage		lipopolysaccharide	10	4	wild_type
+20050505_09_C57_LPS_B_240.CEL	20050505_09_C57_LPS_B_240.CHP	20050505_09_C57_LPS_B_240.EXP	Mouse430_2.cdf	A-AFFY-45	4006289	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	LPS 4 hr Mouse 2	cell	Mus musculus	C57BL/6	wild_type	mixed_sex	macrophage		lipopolysaccharide	10	4	wild_type
+20050623_03_C57_LPS_C_240min.CEL	20050623_03_C57_LPS_C_240min.CHP	20050623_03_C57_LPS_C_240min.EXP	Mouse430_2.cdf	A-AFFY-45	4005732	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	LPS 4 hr Mouse 3	cell	Mus musculus	C57BL/6	wild_type	mixed_sex	macrophage		lipopolysaccharide	10	4	wild_type
+20050622_09_LPS_A1_8_hr.CEL	20050622_09_LPS_A1_8_hr.CHP	20050622_09_LPS_A1_8_hr.EXP	Mouse430_2.cdf	A-AFFY-45	4009043	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	LPS 8 hr Mouse 1	cell	Mus musculus	C57BL/6	wild_type	mixed_sex	macrophage		lipopolysaccharide	10	8	wild_type
+20050622_11_LPS_B1_8_hr.CEL	20050622_11_LPS_B1_8_hr.CHP	20050622_11_LPS_B1_8_hr.EXP	Mouse430_2.cdf	A-AFFY-45	4005732	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	LPS 8 hr Mouse 2	cell	Mus musculus	C57BL/6	wild_type	mixed_sex	macrophage		lipopolysaccharide	10	8	wild_type
+20050623_01_LPS_C1_8hr.CEL	20050623_01_LPS_C1_8hr.CHP	20050623_01_LPS_C1_8hr.EXP	Mouse430_2.cdf	A-AFFY-45	4006263	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	LPS 8 hr Mouse 3	cell	Mus musculus	C57BL/6	wild_type	mixed_sex	macrophage		lipopolysaccharide	10	8	wild_type
+20050622_10_LPS_A1_24_hr.CEL	20050622_10_LPS_A1_24_hr.CHP	20050622_10_LPS_A1_24_hr.EXP	Mouse430_2.cdf	A-AFFY-45	4009043	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	LPS 24 hr Mouse 1	cell	Mus musculus	C57BL/6	wild_type	mixed_sex	macrophage		lipopolysaccharide	10	24	wild_type
+20050622_12_LPS_B1_24_hr.CEL	20050622_12_LPS_B1_24_hr.CHP	20050622_12_LPS_B1_24_hr.EXP	Mouse430_2.cdf	A-AFFY-45	4005732	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	LPS 24 hr Mouse 2	cell	Mus musculus	C57BL/6	wild_type	mixed_sex	macrophage		lipopolysaccharide	10	24	wild_type
+20050623_04_LPS_C2_24hr.CEL	20050623_04_LPS_C2_24hr.CHP	20050623_04_LPS_C2_24hr.EXP	Mouse430_2.cdf	A-AFFY-45	4005732	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	LPS 24 hr Mouse 3	cell	Mus musculus	C57BL/6	wild_type	mixed_sex	macrophage		lipopolysaccharide	10	24	wild_type
+20050310_04_ATF3--_A-0.CEL	20050310_04_ATF3--_A-0.CHP	20050310_04_ATF3--_A-0.EXP	Mouse430_2.cdf	A-AFFY-45	4005732	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	ATF3 -/- LPS 0 hr Mouse 1	cell	Mus musculus	ATF3 -/- C57BL/6	ATF3 -/-	mixed_sex	macrophage	gene_knock_out	none	0	0	ATF3-/-
+20050331_08_ATF3--_B-0.CEL	20050331_08_ATF3--_B-0.CHP	20050331_08_ATF3--_B-0.EXP	Mouse430_2.cdf	A-AFFY-45	4006290	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	ATF3 -/- LPS 0 hr Mouse 2	cell	Mus musculus	ATF3 -/- C57BL/6	ATF3 -/-	mixed_sex	macrophage	gene_knock_out	none	0	0	ATF3-/-
+20050504_09_ATF3_LPS_E-0.CEL	20050504_09_ATF3_LPS_E-0.CHP	20050504_09_ATF3_LPS_E-0.EXP	Mouse430_2.cdf	A-AFFY-45	4006289	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	ATF3 -/- LPS 0 hr Mouse 3	cell	Mus musculus	ATF3 -/- C57BL/6	ATF3 -/-	mixed_sex	macrophage	gene_knock_out	none	0	0	ATF3-/-
+20050310_05_ATF3--_A-60.CEL	20050310_05_ATF3--_A-60.CHP	20050310_05_ATF3--_A-60.EXP	Mouse430_2.cdf	A-AFFY-45	4005732	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	ATF3 -/- LPS 1 hr Mouse 1	cell	Mus musculus	ATF3 -/- C57BL/6	ATF3 -/-	mixed_sex	macrophage	gene_knock_out	lipopolysaccharide	10	1	ATF3-/-
+20050411_04_ATF3--_B-60.CEL	20050411_04_ATF3--_B-60.CHP	20050411_04_ATF3--_B-60.EXP	Mouse430_2.cdf	A-AFFY-45	4006290	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	ATF3 -/- LPS 1 hr Mouse 2	cell	Mus musculus	ATF3 -/- C57BL/6	ATF3 -/-	mixed_sex	macrophage	gene_knock_out	lipopolysaccharide	10	1	ATF3-/-
+20050504_10_ATF3_LPS_E-60.CEL	20050504_10_ATF3_LPS_E-60.CHP	20050504_10_ATF3_LPS_E-60.EXP	Mouse430_2.cdf	A-AFFY-45	4006289	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	ATF3 -/- LPS 1 hr Mouse 3	cell	Mus musculus	ATF3 -/- C57BL/6	ATF3 -/-	mixed_sex	macrophage	gene_knock_out	lipopolysaccharide	10	1	ATF3-/-
+20050310_06_ATF3--_A-120.CEL	20050310_06_ATF3--_A-120.CHP	20050310_06_ATF3--_A-120.EXP	Mouse430_2.cdf	A-AFFY-45	4005732	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	ATF3 -/- LPS 2 hr Mouse 1	cell	Mus musculus	ATF3 -/- C57BL/6	ATF3 -/-	mixed_sex	macrophage	gene_knock_out	lipopolysaccharide	10	2	ATF3-/-
+20050411_05_ATF3--_B-120.CEL	20050411_05_ATF3--_B-120.CHP	20050411_05_ATF3--_B-120.EXP	Mouse430_2.cdf	A-AFFY-45	4006290	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	ATF3 -/- LPS 2 hr Mouse 2	cell	Mus musculus	ATF3 -/- C57BL/6	ATF3 -/-	mixed_sex	macrophage	gene_knock_out	lipopolysaccharide	10	2	ATF3-/-
+20050504_11_ATF3_LPS_E-120.CEL	20050504_11_ATF3_LPS_E-120.CHP	20050504_11_ATF3_LPS_E-120.EXP	Mouse430_2.cdf	A-AFFY-45	4006289	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	ATF3 -/- LPS 2 hr Mouse 3	cell	Mus musculus	ATF3 -/- C57BL/6	ATF3 -/-	mixed_sex	macrophage	gene_knock_out	lipopolysaccharide	10	2	ATF3-/-
+20050310_07_ATF3--_A-240.CEL	20050310_07_ATF3--_A-240.CHP	20050310_07_ATF3--_A-240.EXP	Mouse430_2.cdf	A-AFFY-45	4005732	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	ATF3 -/- LPS 4 hr Mouse 1	cell	Mus musculus	ATF3 -/- C57BL/6	ATF3 -/-	mixed_sex	macrophage	gene_knock_out	lipopolysaccharide	10	4	ATF3-/-
+20050623_02_ATF3_B_240min.CEL	20050623_02_ATF3_B_240min.CHP	20050623_02_ATF3_B_240min.EXP	Mouse430_2.cdf	A-AFFY-45	4006263	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	ATF3 -/- LPS 4 hr Mouse 2	cell	Mus musculus	ATF3 -/- C57BL/6	ATF3 -/-	mixed_sex	macrophage	gene_knock_out	lipopolysaccharide	10	4	ATF3-/-
+20050504_12_ATF3_LPS_E-240.CEL	20050504_12_ATF3_LPS_E-240.CHP	20050504_12_ATF3_LPS_E-240.EXP	Mouse430_2.cdf	A-AFFY-45	4006289	P-TABM-gilchrist1	P-TABM-gilchrist2	P-AFFY-1	P-AFFY-2	ATF3 -/- LPS 4 hr Mouse 3	cell	Mus musculus	ATF3 -/- C57BL/6	ATF3 -/-	mixed_sex	macrophage	gene_knock_out	lipopolysaccharide	10	4	ATF3-/-
diff --git a/examples/real/E-TABM-134.png b/examples/real/E-TABM-134.png
new file mode 100644
index 0000000..afd833c
Binary files /dev/null and b/examples/real/E-TABM-134.png differ
diff --git a/examples/real/E-TABM-134.txt b/examples/real/E-TABM-134.txt
new file mode 100755
index 0000000..0e1f15d
--- /dev/null
+++ b/examples/real/E-TABM-134.txt
@@ -0,0 +1,57 @@
+Experiment Section	
+domain	ebi.ac.uk
+accession	E-TABM-134
+quality_control	
+experiment_design_type	"genotyping_design, disease_state_design, optimization_design"
+name	WGA-LCM and Genomewide Survey of Lung Cancer
+description	A series of experiments were performed to validate whole genome amplification of genomic DNA from laser captured microdissectates and the technique was then applied to serial samples of developing lung cancers to identify early events.  10K Affymetrix SNP arrays were used.
+release_date	2006-10-01
+submission_date	2006-08-11
+submitter	Helen Bird
+submitter_email	H.E.Bird at warwick.ac.uk
+organization	Warwick University
+publication_title	Validation of a method for processing laser captures microdissected samples for analysis using high density single nucleotide polymorphism arrays & Genomewide loss of heterozygosity in small cell and non-small cell lung cancer and precursor bronchial epithelium
+authors	Bird H; Snead DRJ; Hey Y; Brown PE; Davis SA; James S; Knowles MA; Hurst CD; Blanks A; Pepper SD & Snead DRJ; Brown PE; James S; Davis SA; Suortamo S; Pandey S; Blanks A; Grammatopoulos D; Vohra H; Bird H
+journal	in submission to Journal of Pathology
+volume	
+issue																													
+pages																													
+year																													
+
+Protocol Section																													
+accession	text	name	parameters																										
+P-EXML-2	"Resected tumour, tissue of bronchial origin and  lymph node were snap frozen in liquid nitrogen. Frozen sections were cut and stained with haematoxylin and eosin."	Patient Sample Processing																											
+P-EXML-3	"LCM was performed using the Nikon Sl� cut instrument (Nikon UK Ltd.) on frozen sections which had been stored at -80�C. Target lesions were visualised directly, excised and captured on to the adhesive cap of the LCM tubes (Nikon UK Ltd.). Captured cells were stored at -80�C prior to WGA and microarray analysis."	Laser Capture Microdissection																											
+P-EXML-4	"For cell lines: DNA was extracted from the bladder tumour-derived cell line, J82, and from the patient-matched normal cell line (J82EBV, immortalised normal lymphoblasts). Aliquots of J82 tumour cell line DNA and of J82EBV normal cell line DNA were adjusted to either 250ng/5�L (for non-WGA controls), 3ng/3�L or 1ng/3�L (for WGA) to start the next step of the protocol. <br><br>For patient tissue: For non-WGA controls, complete sections were taken and DNA extracted using the DNea [...]
+P-EXML-6	"Protocol was followed exactly according to manufacturer's instructions (Affymetrix, High Wycombe, UK), starting with 250ng DNA in all cases."	"Digestion, Ligation, PCR, Purification, Labeling, Fragmentation"																											
+P-EXML-8	"80�L hybridisation cocktail was hybridisaed overnight at 48�C according to manufacturer's instructions (Affymetrix, High Wycombe, UK). Mapping10K_Xba131 arrays were processed with DNAARRAY_WS2 on Fluidics 400"	Washing & Staining_131																											
+P-EXML-9	"80�L hybridisation cocktail was hybridisaed overnight at 48�C according to manufacturer's instructions (Affymetrix, High Wycombe, UK). Mapping10K_Xba142 arrays were processed with Mini_Mapping10Kv1_450 on Fluidics 450"	Washing & Staining_142																											
+P-EXML-10	"CEL files were analysed in GDAS using the same default settings for Xba131 and Xba142 arrays: HCRBound = 0.070000, CallZoneBound=0.800000, DSBound = 0.080000, GenderAttributeName = Gender, MaleAttributeValue = M, FemaleAttributeValue = F"	Data Processing																											
+
+Hybridization Section																													
+File[raw]	File[normalized]	File[cdf]	Array[accession]	Array[serial]	BioSource	BioMaterialCharacteristics[DiseaseStaging]	FactorValue[DiseaseStaging]	BioMaterialCharacteristics[Individual]	FactorValue[Individual]	BioMaterialCharacteristics[Organism]	BioSourceMaterial	BioMaterialCharacteristics[OrganismPart]	BioMaterialCharacteristics[DiseaseState]	FactorValue[DiseaseState]	BioMaterialCharacteristics[TumourGrading]	BioMaterialCharacteristics[Sex]	BioMaterialCharacteristics[Age]	BioMaterial [...]
+nJ82_P.CEL	nJ82_P.CHP	Mapping10K_Xba131.CDF	A-AFFY-64	3003935	nJ82_P		not applicable	patient J82	patient J82	Homo sapiens	cell_line		tumour cell line	tumour cell line								P-EXML-4	250ng DNA	none	none	PICR	P-EXML-6	P-EXML-8	P-EXML-10
+nJ82ebv_P.CEL	nJ82ebv_P.CHP	Mapping10K_Xba131.CDF	A-AFFY-64	3003935	nJ82ebv_P		not applicable	patient J82	patient J82	Homo sapiens	cell_line		immortalised cell line	immortalised cell line								P-EXML-4	250ng DNA	none	none	PICR	P-EXML-6	P-EXML-8	P-EXML-10
+aJ82_1_W.CEL	aJ82_1_W.CHP	Mapping10K_Xba131.CDF	A-AFFY-64	3002019	aJ82_1_W		not applicable	patient J82	patient J82	Homo sapiens	cell_line		tumour cell line	tumour cell line								P-EXML-4	1ng DNA	WGA	WGA	Warwick	P-EXML-6	P-EXML-8	P-EXML-10
+aJ82ebv_1_W.CEL	aJ82ebv_1_W.CHP	Mapping10K_Xba131.CDF	A-AFFY-64	3002019	aJ82ebv_1_W		not applicable	patient J82	patient J82	Homo sapiens	cell_line		immortalised cell line	immortalised cell line								P-EXML-4	1ng DNA	WGA	WGA	Warwick	P-EXML-6	P-EXML-8	P-EXML-10
+aJ82_3_W.CEL	aJ82_3_W.CHP	Mapping10K_Xba131.CDF	A-AFFY-64	3002019	aJ82_3_W		not applicable	patient J82	patient J82	Homo sapiens	cell_line		tumour cell line	tumour cell line								P-EXML-4	3ng DNA	WGA	WGA	Warwick	P-EXML-6	P-EXML-8	P-EXML-10
+nJ82_W.CEL	nJ82_W.CHP	Mapping10K_Xba131.CDF	A-AFFY-64	3002019	nJ82_W		not applicable	patient J82	patient J82	Homo sapiens	cell_line		tumour cell line	tumour cell line								P-EXML-4	250ng DNA	none	none	Warwick	P-EXML-6	P-EXML-8	P-EXML-10
+0904_MK18_10K_90J82.CEL	0904_MK18_10K_90J82.CHP	Mapping10K_Xba131.CDF	A-AFFY-64	3003935	0904_MK18_10K_90J82		not applicable	patient J82	patient J82	Homo sapiens	cell_line		mixed source cell line	mixed source cell line								P-EXML-4	250ng DNA	none	none	PICR	P-EXML-6	P-EXML-8	P-EXML-10
+0904_MK19_10K_80J82.CEL	0904_MK19_10K_80J82.CHP	Mapping10K_Xba131.CDF	A-AFFY-64	3003935	0904_MK19_10K_80J82		not applicable	patient J82	patient J82	Homo sapiens	cell_line		mixed source cell line	mixed source cell line								P-EXML-4	250ng DNA	none	none	PICR	P-EXML-6	P-EXML-8	P-EXML-10
+0904_MK20_10K_70J82.CEL	0904_MK20_10K_70J82.CHP	Mapping10K_Xba131.CDF	A-AFFY-64	3003935	0904_MK20_10K_70J82		not applicable	patient J82	patient J82	Homo sapiens	cell_line		mixed source cell line	mixed source cell line								P-EXML-4	250ng DNA	none	none	PICR	P-EXML-6	P-EXML-8	P-EXML-10
+Case1_aLN.CEL	Case1_aLN.CHP	Mapping10K_Xba131.CDF	A-AFFY-64	3003935	Case1_aLN	aLN	aLN	case 1	case 1	Homo sapiens	organism_part	lymph node	squamous cell carcinoma	squamous cell carcinoma	"T2, N0, stage 1b"	male	65	years	smoking history: 50 pack years	P-EXML-2	P-EXML-3	P-EXML-4	1035 cells	WGA	WGA	Warwick	P-EXML-6	P-EXML-8	P-EXML-10
+Case1_aHYP.CEL	Case1_aHYP.CHP	Mapping10K_Xba131.CDF	A-AFFY-64	3002019	Case1_aHYP	aHYP	aHYP	case 1	case 1	Homo sapiens	organism_part	lung	squamous cell carcinoma	squamous cell carcinoma	"T2, N0, stage 1b"	male	65	years	smoking history: 50 pack years	P-EXML-2	P-EXML-3	P-EXML-4	816 cells	WGA	WGA	Warwick	P-EXML-6	P-EXML-8	P-EXML-10
+Case1_aTUM.CEL	Case1_aTUM.CHP	Mapping10K_Xba131.CDF	A-AFFY-64	3002019	Case1_aTUM	aTUM	aTUM	case 1	case 1	Homo sapiens	organism_part	lung	squamous cell carcinoma	squamous cell carcinoma	"T2, N0, stage 1b"	male	65	years	smoking history: 50 pack years	P-EXML-2	P-EXML-3	P-EXML-4	1136 cells	WGA	WGA	Warwick	P-EXML-6	P-EXML-8	P-EXML-10
+Case2_aAVM.CEL	Case2_aAVM.CHP	Mapping10K_Xba131.CDF	A-AFFY-64	3002019	Case2_aAVM	aAVM	aAVM	case 2	case 2	Homo sapiens	organism_part	macrophage	large cell neuroendocrine carcinoma	large cell neuroendocrine carcinoma	"T2, N0, M0, stage 1b"	female	70	years	smoking history: 30 pack years	P-EXML-2	P-EXML-3	P-EXML-4	558 cells	WGA	WGA	Warwick	P-EXML-6	P-EXML-8	P-EXML-10
+Case2_aBE.CEL	Case2_aBE.CHP	Mapping10K_Xba131.CDF	A-AFFY-64	3002019	Case2_aBE	aBE	aBE	case 2	case 2	Homo sapiens	organism_part	lung	large cell neuroendocrine carcinoma	large cell neuroendocrine carcinoma	"T2, N0, M0, stage 1b"	female	70	years	smoking history: 30 pack years	P-EXML-2	P-EXML-3	P-EXML-4	nk	WGA	WGA	Warwick	P-EXML-6	P-EXML-8	P-EXML-10
+Case2_aOE.CEL	Case2_aOE.CHP	Mapping10K_Xba131.CDF	A-AFFY-64	3002019	Case2_aOE	aOE	aOE	case 2	case 2	Homo sapiens	organism_part	lung	large cell neuroendocrine carcinoma	large cell neuroendocrine carcinoma	"T2, N0, M0, stage 1b"	female	70	years	smoking history: 30 pack years	P-EXML-2	P-EXML-3	P-EXML-4	419 cells	WGA	WGA	Warwick	P-EXML-6	P-EXML-8	P-EXML-10
+Case2_aTUM1.CEL	Case2_aTUM1.CHP	Mapping10K_Xba131.CDF	A-AFFY-64	3002019	Case2_aTUM1	aTUM1	aTUM1	case 2	case 2	Homo sapiens	organism_part	lung	large cell neuroendocrine carcinoma	large cell neuroendocrine carcinoma	"T2, N0, M0, stage 1b"	female	70	years	smoking history: 30 pack years	P-EXML-2	P-EXML-3	P-EXML-4	446 cells	WGA	WGA	Warwick	P-EXML-6	P-EXML-8	P-EXML-10
+Case2_aTUM2.CEL	Case2_aTUM2.CHP	Mapping10K_Xba142.CDF	A-AFFY-65	4008473	Case2_aTUM2	aTUM2	aTUM2	case 2	case 2	Homo sapiens	organism_part	lung	large cell neuroendocrine carcinoma	large cell neuroendocrine carcinoma	"T2, N0, M0, stage 1b"	female	70	years	smoking history: 30 pack years	P-EXML-2	P-EXML-3	P-EXML-4	498 cells	WGA	WGA	Warwick	P-EXML-6	P-EXML-9	P-EXML-10
+Case3_nLN.CEL	Case3_nLN.CHP	Mapping10K_Xba131.CDF	A-AFFY-64	3002019	Case3_nLN	nLN	nLN	case 3	case 3	Homo sapiens	organism_part	lymph node	small cell lung cancer	small cell lung cancer	"T1, N2, M0, stage 3"	female	47	years	smoking history: 30 pack years	P-EXML-2		P-EXML-4	250ng DNA	none	none	Warwick	P-EXML-6	P-EXML-8	P-EXML-10
+Case3_aLN.CEL	Case3_aLN.CHP	Mapping10K_Xba131.CDF	A-AFFY-64	3002019	Case3_aLN	aLN	aLN	case 3	case 3	Homo sapiens	organism_part	lymph node	small cell lung cancer	small cell lung cancer	"T1, N2, M0, stage 3"	female	47	years	smoking history: 30 pack years	P-EXML-2	P-EXML-3	P-EXML-4	545 cells	WGA	WGA	Warwick	P-EXML-6	P-EXML-8	P-EXML-10
+Case3_aBE.CEL	Case3_aBE.CHP	Mapping10K_Xba131.CDF	A-AFFY-64	3002019	Case3_aBE	aBE	aBE	case 3	case 3	Homo sapiens	organism_part	lung	small cell lung cancer	small cell lung cancer	"T1, N2, M0, stage 3"	female	47	years	smoking history: 30 pack years	P-EXML-2	P-EXML-3	P-EXML-4	843 cells	WGA	WGA	Warwick	P-EXML-6	P-EXML-8	P-EXML-10
+Case3_aOE.CEL	Case3_aOE.CHP	Mapping10K_Xba142.CDF	A-AFFY-65	4008473	Case3_aOE	aOE	aOE	case 3	case 3	Homo sapiens	organism_part	lung	small cell lung cancer	small cell lung cancer	"T1, N2, M0, stage 3"	female	47	years	smoking history: 30 pack years	P-EXML-2	P-EXML-3	P-EXML-4	200 cells	WGA	WGA	Warwick	P-EXML-6	P-EXML-9	P-EXML-10
+Case3_nTB.CEL	Case3_nTB.CHP	Mapping10K_Xba131.CDF	A-AFFY-64	3002019	Case3_nTB	nTB	nTB	case 3	case 3	Homo sapiens	organism_part	lung	small cell lung cancer	small cell lung cancer	"T1, N2, M0, stage 3"	female	47	years	smoking history: 30 pack years	P-EXML-2		P-EXML-4	250ng DNA	none	none	Warwick	P-EXML-6	P-EXML-8	P-EXML-10
+Case3_aTUM1.CEL	Case3_aTUM1.CHP	Mapping10K_Xba131.CDF	A-AFFY-64	3002019	Case3_aTUM1	aTUM1	aTUM1	case 3	case 3	Homo sapiens	organism_part	lung	small cell lung cancer	small cell lung cancer	"T1, N2, M0, stage 3"	female	47	years	smoking history: 30 pack years	P-EXML-2	P-EXML-3	P-EXML-4	1300 cells	WGA	WGA	Warwick	P-EXML-6	P-EXML-8	P-EXML-10
+Case3_aTUM2.CEL	Case3_aTUM2.CHP	Mapping10K_Xba131.CDF	A-AFFY-64	3002019	Case3_aTUM2	aTUM2	aTUM2	case 3	case 3	Homo sapiens	organism_part	lung	small cell lung cancer	small cell lung cancer	"T1, N2, M0, stage 3"	female	47	years	smoking history: 30 pack years	P-EXML-2	P-EXML-3	P-EXML-4	997 cells	WGA	WGA	Warwick	P-EXML-6	P-EXML-8	P-EXML-10
+Case3_aTUM3.CEL	Case3_aTUM3.CHP	Mapping10K_Xba131.CDF	A-AFFY-64	3002019	Case3_aTUM3	aTUM3	aTUM3	case 3	case 3	Homo sapiens	organism_part	lung	small cell lung cancer	small cell lung cancer	"T1, N2, M0, stage 3"	female	47	years	smoking history: 30 pack years	P-EXML-2	P-EXML-3	P-EXML-4	350 cells	WGA	WGA	Warwick	P-EXML-6	P-EXML-8	P-EXML-10
diff --git a/examples/real/E-TABM-136.png b/examples/real/E-TABM-136.png
new file mode 100644
index 0000000..f26cd2f
Binary files /dev/null and b/examples/real/E-TABM-136.png differ
diff --git a/examples/real/E-TABM-136.txt b/examples/real/E-TABM-136.txt
new file mode 100755
index 0000000..bdbb9be
--- /dev/null
+++ b/examples/real/E-TABM-136.txt
@@ -0,0 +1,106 @@
+Experiment section	
+domain	ebi.ac.uk
+accession	E-TABM-136
+quality_control	biological_replicate_design
+experiment_design_type	organism_part_comparison_design;species_comparison_design;
+name	Functionality of Intergenic Transcription: An Evolutionary Comparison
+description	"Although a large proportion of human transcription occurs outside the boundaries of known genes, the functional significance of this transcription remains unknown. We have compared the expression patterns of known genes as well as intergenic transcripts within the ENCODE regions between humans and chimpanzees in brain, heart, testis and lymphoblastoid cell lines. We find that intergenic transcripts show patterns of tissue-specific conservation of their expression which are c [...]
+ArrayExpressDisplayName	"Transcription profiling of human and chimpanzee heart, brain, testis and lymphblastoid cell lines to study functionality of intergenic transcription."
+release_date	2006-09-30
+submission_date	2006-08-18
+submitter	Philipp Khaitovich
+submitter_email	khaitovich at eva.mpg.de
+publication_title	Functionality of Intergenic Transcription: An Evolutionary Comparison
+authors	Philipp Khaitovich; Janet Kelso; Henriette Franz; Johann Visagie; Thomas Giger; Sabrina Joerchel; Ekkehard Petzold; Richard E. Green; Michael Lachmann; Svante P��bo
+journal	PLoS Genetics
+volume	
+issue																	
+pages																	
+year	2006																
+
+Protocol section																	
+accession	type	text	name														
+P-TABM-47	labeling	Following Affymetrix Whole Transcript (WT) Sense Target Labeling Assay protocol. Starting amount of material: 2 ug of total RNA  per sample	Whole Transcript (WT) Sense Target Labeling Assay 														
+
+Hybridization section																	
+File[raw]	File[exp]	Array[serial]	Array[accession]	Protocol[extraction]	Protocol[labeling]	Protocol[hybridization]	Protocol[scanning]	BioSource	BioSourceMaterial	BioMaterialCharacteristics[Organism]	BioMaterialCharacteristics[Sex]	BioMaterialCharacteristics[Age(years)]	BioMaterialCharacteristics[OrganismPart]	BioMaterialCharacteristics[OrganismStatus]	FactorValue[Organism]	FactorValue[OrganismPart]	FactorValue[lymphoblastoid cellLine]
+CB1F.CEL	CB1F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c01	organism_part	Pan troglodytes	male	7	"brain, prefrontal cortex"	postmortem	Pan troglodytes	brain	
+CB2F.CEL	CB2F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c02	organism_part	Pan troglodytes	male	12	"brain, prefrontal cortex"	postmortem	Pan troglodytes	brain	
+CB3F.CEL	CB3F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c03	organism_part	Pan troglodytes	male	12	"brain, prefrontal cortex"	postmortem	Pan troglodytes	brain	
+CB4F.CEL	CB4F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c04	organism_part	Pan troglodytes	male	12	"brain, prefrontal cortex"	postmortem	Pan troglodytes	brain	
+CB5F.CEL	CB5F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c05	organism_part	Pan troglodytes	male	>40	"brain, prefrontal cortex"	postmortem	Pan troglodytes	brain	
+CC1F.CEL	CC1F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c06	organism_part	Pan troglodytes	female	19	lymphoblastoid cell line	postmortem	Pan troglodytes		lymphoblastoid cell line
+CC2F.CEL	CC2F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c07	organism_part	Pan troglodytes	female	20	lymphoblastoid cell line	postmortem	Pan troglodytes		lymphoblastoid cell line
+CC3F.CEL	CC3F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c08	organism_part	Pan troglodytes	female	32	lymphoblastoid cell line	postmortem	Pan troglodytes		lymphoblastoid cell line
+CC4F.CEL	CC4F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c09	organism_part	Pan troglodytes	female	12	lymphoblastoid cell line	postmortem	Pan troglodytes		lymphoblastoid cell line
+CC5F.CEL	CC5F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c10	organism_part	Pan troglodytes	female	37	lymphoblastoid cell line	postmortem	Pan troglodytes		lymphoblastoid cell line
+CH1F.CEL	CH1F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c01	organism_part	Pan troglodytes	male	7	heart muscle	postmortem	Pan troglodytes	heart	
+CH2F.CEL	CH2F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c11	organism_part	Pan troglodytes	male	34	heart muscle	postmortem	Pan troglodytes	heart	
+CH3F.CEL	CH3F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c03	organism_part	Pan troglodytes	male	12	heart muscle	postmortem	Pan troglodytes	heart	
+CH4F.CEL	CH4F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c12	organism_part	Pan troglodytes	male	26	heart muscle	postmortem	Pan troglodytes	heart	
+CH5F.CEL	CH5F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c02	organism_part	Pan troglodytes	male	12	heart muscle	postmortem	Pan troglodytes	heart	
+CT1F.CEL	CT1F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c13	organism_part	Pan troglodytes	male	20	testis	postmortem	Pan troglodytes	testis	
+CT2F.CEL	CT2F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c02	organism_part	Pan troglodytes	male	12	testis	postmortem	Pan troglodytes	testis	
+CT3F.CEL	CT3F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c03	organism_part	Pan troglodytes	male	12	testis	postmortem	Pan troglodytes	testis	
+CT4F.CEL	CT4F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c04	organism_part	Pan troglodytes	male	12	testis	postmortem	Pan troglodytes	testis	
+CT5F.CEL	CT5F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c05	organism_part	Pan troglodytes	male	>40	testis	postmortem	Pan troglodytes	testis	
+HB1F.CEL	HB1F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h01	organism_part	Homo sapiens	male	45	"brain, prefrontal cortex"	postmortem	Homo sapiens	brain	
+HB2F.CEL	HB2F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h02	organism_part	Homo sapiens	male	45	"brain, prefrontal cortex"	postmortem	Homo sapiens	brain	
+HB3F.CEL	HB3F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h03	organism_part	Homo sapiens	male	48	"brain, prefrontal cortex"	postmortem	Homo sapiens	brain	
+HB4F.CEL	HB4F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h04	organism_part	Homo sapiens	male	56	"brain, prefrontal cortex"	postmortem	Homo sapiens	brain	
+HB5F.CEL	HB5F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h05	organism_part	Homo sapiens	male	67	"brain, prefrontal cortex"	postmortem	Homo sapiens	brain	
+HC1F.CEL	HC1F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h06	organism_part	Homo sapiens	female	13	lymphoblastoid cell line	postmortem	Homo sapiens		lymphoblastoid cell line
+HC2F.CEL	HC2F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h07	organism_part	Homo sapiens	female	12	lymphoblastoid cell line	postmortem	Homo sapiens		lymphoblastoid cell line
+HC3F.CEL	HC3F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h08	organism_part	Homo sapiens	female	20	lymphoblastoid cell line	postmortem	Homo sapiens		lymphoblastoid cell line
+HC4F.CEL	HC4F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h09	organism_part	Homo sapiens	female	22	lymphoblastoid cell line	postmortem	Homo sapiens		lymphoblastoid cell line
+HC5F.CEL	HC5F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h10	organism_part	Homo sapiens	female	32	lymphoblastoid cell line	postmortem	Homo sapiens		lymphoblastoid cell line
+HH1F.CEL	HH1F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h11	organism_part	Homo sapiens	male	71	heart muscle	postmortem	Homo sapiens	heart	
+HH2F.CEL	HH2F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h12	organism_part	Homo sapiens	male	74	heart muscle	postmortem	Homo sapiens	heart	
+HH3F.CEL	HH3F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h13	organism_part	Homo sapiens	male	57	heart muscle	postmortem	Homo sapiens	heart	
+HH4F.CEL	HH4F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h14	organism_part	Homo sapiens	male	77	heart muscle	postmortem	Homo sapiens	heart	
+HH5F.CEL	HH5F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h15	organism_part	Homo sapiens	male	51	heart muscle	postmortem	Homo sapiens		lymphoblastoid cell line
+HT1F.CEL	HT1F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h16	organism_part	Homo sapiens	male	27	testis	postmortem	Homo sapiens	testis	
+HT2F.CEL	HT2F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h17	organism_part	Homo sapiens	male	32	testis	postmortem	Homo sapiens	testis	
+HT3F.CEL	HT3F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h18	organism_part	Homo sapiens	male	25	testis	postmortem	Homo sapiens	testis	
+HT4F.CEL	HT4F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h19	organism_part	Homo sapiens	male	41	testis	postmortem	Homo sapiens	testis	
+HT5F.CEL	HT5F.EXP	4011140	A-AFFY-61	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h20	organism_part	Homo sapiens	male	27	testis	postmortem	Homo sapiens	testis	
+CB1R.CEL	CB1R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c01	organism_part	Pan troglodytes	male	7	"brain, prefrontal cortex"	postmortem	Pan troglodytes	brain	
+CB2R.CEL	CB2R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c02	organism_part	Pan troglodytes	male	12	"brain, prefrontal cortex"	postmortem	Pan troglodytes	brain	
+CB3R.CEL	CB3R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c03	organism_part	Pan troglodytes	male	12	"brain, prefrontal cortex"	postmortem	Pan troglodytes	brain	
+CB4R.CEL	CB4R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c04	organism_part	Pan troglodytes	male	12	"brain, prefrontal cortex"	postmortem	Pan troglodytes	brain	
+CB5R.CEL	CB5R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c05	organism_part	Pan troglodytes	male	>40	"brain, prefrontal cortex"	postmortem	Pan troglodytes	brain	
+CC1R.CEL	CC1R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c06	organism_part	Pan troglodytes	female	19	lymphoblastoid cell line	postmortem	Pan troglodytes		lymphoblastoid cell line
+CC2R.CEL	CC2R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c07	organism_part	Pan troglodytes	female	20	lymphoblastoid cell line	postmortem	Pan troglodytes		lymphoblastoid cell line
+CC3R.CEL	CC3R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c08	organism_part	Pan troglodytes	female	32	lymphoblastoid cell line	postmortem	Pan troglodytes		lymphoblastoid cell line
+CC4R.CEL	CC4R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c09	organism_part	Pan troglodytes	female	12	lymphoblastoid cell line	postmortem	Pan troglodytes		lymphoblastoid cell line
+CC5R.CEL	CC5R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c10	organism_part	Pan troglodytes	female	37	lymphoblastoid cell line	postmortem	Pan troglodytes		lymphoblastoid cell line
+CH1R.CEL	CH1R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c01	organism_part	Pan troglodytes	male	7	heart muscle	postmortem	Pan troglodytes	heart	
+CH2R.CEL	CH2R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c11	organism_part	Pan troglodytes	male	34	heart muscle	postmortem	Pan troglodytes	heart	
+CH3R.CEL	CH3R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c03	organism_part	Pan troglodytes	male	12	heart muscle	postmortem	Pan troglodytes	heart	
+CH4R.CEL	CH4R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c12	organism_part	Pan troglodytes	male	26	heart muscle	postmortem	Pan troglodytes	heart	
+CH5R.CEL	CH5R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c02	organism_part	Pan troglodytes	male	12	heart muscle	postmortem	Pan troglodytes	heart	
+CT1R.CEL	CT1R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c13	organism_part	Pan troglodytes	male	20	testis	postmortem	Pan troglodytes	testis	
+CT2R.CEL	CT2R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c02	organism_part	Pan troglodytes	male	12	testis	postmortem	Pan troglodytes	testis	
+CT3R.CEL	CT3R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c03	organism_part	Pan troglodytes	male	12	testis	postmortem	Pan troglodytes	testis	
+CT4R.CEL	CT4R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c04	organism_part	Pan troglodytes	male	12	testis	postmortem	Pan troglodytes	testis	
+CT5R.CEL	CT5R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	c05	organism_part	Pan troglodytes	male	>40	testis	postmortem	Pan troglodytes	testis	
+HB1R.CEL	HB1R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h01	organism_part	Homo sapiens	male	45	"brain, prefrontal cortex"	postmortem	Homo sapiens	brain	
+HB2R.CEL	HB2R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h02	organism_part	Homo sapiens	male	45	"brain, prefrontal cortex"	postmortem	Homo sapiens	brain	
+HB3R.CEL	HB3R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h03	organism_part	Homo sapiens	male	48	"brain, prefrontal cortex"	postmortem	Homo sapiens	brain	
+HB4R.CEL	HB4R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h04	organism_part	Homo sapiens	male	56	"brain, prefrontal cortex"	postmortem	Homo sapiens	brain	
+HB5R.CEL	HB5R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h05	organism_part	Homo sapiens	male	67	"brain, prefrontal cortex"	postmortem	Homo sapiens	brain	
+HC1R.CEL	HC1R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h06	organism_part	Homo sapiens	female	13	lymphoblastoid cell line	postmortem	Homo sapiens		lymphoblastoid cell line
+HC2R.CEL	HC2R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h07	organism_part	Homo sapiens	female	12	lymphoblastoid cell line	postmortem	Homo sapiens		lymphoblastoid cell line
+HC3R.CEL	HC3R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h08	organism_part	Homo sapiens	female	20	lymphoblastoid cell line	postmortem	Homo sapiens		lymphoblastoid cell line
+HC4R.CEL	HC4R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h09	organism_part	Homo sapiens	female	22	lymphoblastoid cell line	postmortem	Homo sapiens		lymphoblastoid cell line
+HC5R.CEL	HC5R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h10	organism_part	Homo sapiens	female	32	lymphoblastoid cell line	postmortem	Homo sapiens		lymphoblastoid cell line
+HH1R.CEL	HH1R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h11	organism_part	Homo sapiens	male	71	heart muscle	postmortem	Homo sapiens	heart
+HH2R.CEL	HH2R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h12	organism_part	Homo sapiens	male	74	heart muscle	postmortem	Homo sapiens	heart
+HH3R.CEL	HH3R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h13	organism_part	Homo sapiens	male	57	heart muscle	postmortem	Homo sapiens	heart
+HH4R.CEL	HH4R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h14	organism_part	Homo sapiens	male	77	heart muscle	postmortem	Homo sapiens	heart
+HH5R.CEL	HH5R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h15	organism_part	Homo sapiens	male	51	heart muscle	postmortem	Homo sapiens	heart
+HT1R.CEL	HT1R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h16	organism_part	Homo sapiens	male	27	testis	postmortem	Homo sapiens	testis
+HT2R.CEL	HT2R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h17	organism_part	Homo sapiens	male	32	testis	postmortem	Homo sapiens	testis
+HT3R.CEL	HT3R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h18	organism_part	Homo sapiens	male	25	testis	postmortem	Homo sapiens	testis
+HT4R.CEL	HT4R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h19	organism_part	Homo sapiens	male	41	testis	postmortem	Homo sapiens	testis
+HT5R.CEL	HT5R.EXP	4011140	A-AFFY-67	P-TABM-44	P-TABM-47	Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV	P-TABM-46	h20	organism_part	Homo sapiens	male	27	testis	postmortem	Homo sapiens	testis
diff --git a/examples/real/E-TABM-140.png b/examples/real/E-TABM-140.png
new file mode 100644
index 0000000..620d18c
Binary files /dev/null and b/examples/real/E-TABM-140.png differ
diff --git a/examples/real/E-TABM-140.txt b/examples/real/E-TABM-140.txt
new file mode 100755
index 0000000..a94d554
--- /dev/null
+++ b/examples/real/E-TABM-140.txt
@@ -0,0 +1,108 @@
+Experiment section	
+domain	ebi.ac.uk
+accession	E-TABM-140
+quality_control	biological_replicate
+experiment_design_type	binding_site_identification_design
+name	Chromatin immunoprecipitation microarray (ChIP-chip) study using human erythroleukemia cell line K-562 and H3K4me2 (ab7766); H3K4me3 (ab8580); H3ac (06-599); H4ac (06-866): Histone H2B(ab1790) and Histone H3 (ab1791) antibodies (ENCODE).
+description	"ENCODE ChIP-chip study using human myelogenous leukemia cell line K-562 and anti histone H3K4me2 (Abcam; ab7766); H3K4me3 (Abcam; ab8580); H3ac (Upstate; 06-599); H4ac (Upstate; 06-866); Histone H2B (Abcam: ab1790) and Histone H3 (Abcam: ab1791) antibodies. Each antibody experiment was conducted in three biological replicates, with two technical replicates performed for each biological replicate"
+ae_display_name	Chromatin immunoprecipitation of Histone H3K4me2; H3K4me3; H3K9/14ac; H4K5/8/12/16ac; H2B and H3
+release_date	2006-09-01
+submission_date	2006-08-18
+submitter	Shane Dillon
+submitter_email	scd at sanger.ac.uk
+investigator	David Vetrie
+investigator_email	vt1 at sanger.ac.uk
+organization	The Wellcome Trust Sanger Institute
+address	Wellcome Trust Genome Campus;Hinxton; Cambridge; CB10 1SA; UK
+publication_title	The Landscape of Activating Histone Modifications across 1% of the Human Genome.	
+authors	Christoph M. Koch; Robert M. Andrews; Paul Flicek; Shane C. Dillon; Ula Kara�z; Gayle K. Clelland; Sarah Wilcox; David M. Beare; Joanna C. Fowler; Phillippe Couttet; Keith D. James; Gregory C. Lefebvre; Alexander W. Bruce; Oliver M. Dovey; Peter D. Ellis; Pawandeep Dhami; Cordelia F. Langford; Zhiping Weng; Ewan Birney; Nigel P. Carter; David Vetrie; Ian Dunham	
+journal	Genome Res	
+volume		
+issue		
+pages		
+year		
+
+Protocol section		
+accession	name	text
+GROW_K-562	K-562 CULTURE CONDITIONS	Cells were grown in DMEM supplemented with 10% FBS and 1% Penicillin/Streptomycin to a total number of ~ 3x10^8 cells. Dilute cell density to 3 x10^5/ml every two to three days. Split and feed 16 hours before cross-linking the cells.
+CROSSLINK_K-562_1	K-562_CHROMATIN PREPARATION_1	"Day 1 Cross linking cells and chromatin extraction/preparation<br>1. Grow or harvest 1x10^8 cells. Collect the cells by centrifuging at 1200 rpm for 5-8 minutes.<br>2. Resuspend the cells in pre-warmed 50 ml serum free media in a glass flask. Add 0.5 ml of formaldehyde solution in fume hood (37%; final concentration 0.37%) to cross link DNA-protein interactions. Incubate at room temperature with gentle agitation for 10 minutes.<br>3. Add 3 [...]
+CROSSLINK_K-562_2	K-562_CHROMATIN PREPARATION_2	"Same as K-562_CHROMATIN PREPARATION_1, except cells were crosslinked with 1.35 ml formaldehyde(final concentration =1%) and 3.425 ml of glycine added to quench crosslinking. "
+CHROMATIN_IMMUNOPRECIPITATION	CHROMATINIMMUNOPRECIPITATION and DNA RECOVERY	"Immunoprecipitation Day One<br>Control conditions<br><br>Normal rabbit IgG: 675 ul chromatin + 675 ul 1:4 NLB:IPDB buffer + 10 ul Rabbit IgG (Upstate)<br>Input 270 ul of chromatin store at -20�C until step 5 next day.<br><br>Immunoprecipitation conditions<br><br>Histone H3 tri methyl K4 (Abcam, ab8895):  675 ul chromatin + 675 ul 1:4 NLB:IPDB buffer + 10 ug Histone H3 trimethyl K4 antibody<br>Histone H3 di methy [...]
+HYBRIDISATION_TECAN	Preparation of Hybridisation mixture	"Tecan Encode array hybridisation (gridsize: 2x6 cm)<br><br><br>Equipment and reagents<br><br>Hybridisation buffer: (50 % formamide; 5 % dextran sulphate; 0.1 % Tween 20; 2x SSC; 10 mM Tris pH 7.4)<br>* 3 M NaAc pH 5.2<br>* human Cot1 DNA (Invitrogen, order. No. 15279011, or Roche, order. No. 1581074, test the quality prior to use)<br>* Herring sperm DNA (Sigma, order. No. D7290)<br>* 100 % Ethanol, 80 % Ethanol<br>* Yeast tRNA (In [...]
+SCANARRAYEXPRESS-2	Scanning of Slides and Analysis	Microarrays were scanned using a ScanArray 4000 confocal laser-based scanner (Perkin Elmer). Mean spot intensities from images were quantified using ScanArray Express (Perkin Elmer) with background subtraction. Spots affected by dust were manually flagged as �not found� and subsequently excluded from the analysis. Wiggle tracks were generated combining data of all replicates to the median using an R script (see also ftp://ftp.sanger.ac.u [...]
+TRANSFORMATION	transformation	"The Data of all replicates of a ChIP experiment were combined to the median using an R script (see also ftp://ftp.sanger.ac.uk/pub/encode/). It takes the ""Ch2 N Ratio of Means"" values over all technical replicates, where ""Ch2 N Ratio of Means"" equals the ratio of the enriched and non-enriched fractions (both background corrected and global normalised) and combines the values to the median. Tracks of this median data can be visualised using the UCSC geno [...]
+
+Hybridization section																											
+File[raw]	File[transformed]	Array[accession]	Array[serial]	Protocol[grow]	Protocol[treatment]	Protocol[extraction]	Protocol[labeling]	Protocol[hybridization]	Protocol[scanning]	Protocol[TRANSFORMATION]	BioSource	Sample	Extract	LabeledExtract	Hybridization	BioSourceMaterial	SampleMaterial	ExtractMaterial	LabeledExtractMaterial	Dye	BioMaterialCharacteristics[Organism]	BioMaterialCharacteristics[BioSourceType]	BioMaterialCharacteristics[StrainOrLine]	BioMaterialCharacteristics[CellType]	Bio [...]
+1106-16_raw.txt	FGEDM_complete.txt	A-MEXP-215	1106-16	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP1	K-562_BIOREP1_techrep1	"IP of K-562_BIOREP1 with anti H3K4me2 (Abcam, ab7766)"	K-562 BIOREP1_techrep1 ChIP H3K4me2 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisi [...]
+1106-16_raw.txt	FGEDM_complete.txt	A-MEXP-215	1106-16	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP1	K-562_BIOREP1_techrep1	Input control DNA of K-562_BIOREP1	K-562 BIOREP1_techrep1 ChIP H3K4me2 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1106-17_raw.txt	FGEDM_complete.txt	A-MEXP-215	1106-17	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP1	K-562_BIOREP1_techrep2	"IP of K-562_BIOREP1 with anti H3K4me2 (Abcam, ab7766)"	K-562 BIOREP1_techrep2 ChIP H3K4me2 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisi [...]
+1106-17_raw.txt	FGEDM_complete.txt	A-MEXP-215	1106-17	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP1	K-562_BIOREP1_techrep2	Input control DNA of K-562_BIOREP1	K-562 BIOREP1_techrep2 ChIP H3K4me2 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1140-6_raw.txt	FGEDM_complete.txt	A-MEXP-215	1140-6	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP2	K-562_BIOREP2_techrep1	"IP of K-562_BIOREP2 with anti H3K4me2 (Abcam, ab7766)"	K-562 BIOREP2_techrep1 ChIP H3K4me2 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	 [...]
+1140-6_raw.txt	FGEDM_complete.txt	A-MEXP-215	1140-6	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP2	K-562_BIOREP2_techrep1	Input control DNA of K-562_BIOREP2	K-562 BIOREP2_techrep1 ChIP H3K4me2 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1140-7_raw.txt	FGEDM_complete.txt	A-MEXP-215	1140-7	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP2	K-562_BIOREP2_techrep2	"IP of K-562_BIOREP2 with anti H3K4me2 (Abcam, ab7766)"	K-562 BIOREP2_techrep2 ChIP H3K4me2 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	 [...]
+1140-7_raw.txt	FGEDM_complete.txt	A-MEXP-215	1140-7	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP2	K-562_BIOREP2_techrep2	Input control DNA of K-562_BIOREP2	K-562 BIOREP2_techrep2 ChIP H3K4me2 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1140-12_raw.txt	FGEDM_complete.txt	A-MEXP-215	1140-12	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP3	K-562_BIOREP3_techrep1	"IP of K-562_BIOREP3 with anti H3K4me2 (Abcam, ab7766)"	K-562 BIOREP3_techrep1 ChIP H3K4me2 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisi [...]
+1140-12_raw.txt	FGEDM_complete.txt	A-MEXP-215	1140-12	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP3	K-562_BIOREP3_techrep1	Input control DNA of K-562_BIOREP3	K-562 BIOREP3_techrep1 ChIP H3K4me2 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1140-13_raw.txt	FGEDM_complete.txt	A-MEXP-215	1140-13	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP3	K-562_BIOREP3_techrep2	"IP of K-562_BIOREP3 with anti H3K4me2 (Abcam, ab7766)"	K-562 BIOREP3_techrep2 ChIP H3K4me2 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisi [...]
+1140-13_raw.txt	FGEDM_complete.txt	A-MEXP-215	1140-13	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP3	K-562_BIOREP3_techrep2	Input control DNA of K-562_BIOREP3	K-562 BIOREP3_techrep2 ChIP H3K4me2 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1023-16_raw.txt	FGEDM_complete.txt	A-MEXP-156	1023-16	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP1	K-562_BIOREP1_techrep1	"IP of K-562_BIOREP1 with anti H3K4me3 (Abcam, ab8895)"	K-562 BIOREP1_techrep1 ChIP H3K4me3 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisi [...]
+1023-16_raw.txt	FGEDM_complete.txt	A-MEXP-156	1023-16	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP1	K-562_BIOREP1_techrep1	Input control DNA of K-562_BIOREP1	K-562 BIOREP1_techrep1 ChIP H3K4me3 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1023-17_raw.txt	FGEDM_complete.txt	A-MEXP-156	1023-17	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP1	K-562_BIOREP1_techrep2	"IP of K-562_BIOREP1 with anti H3K4me3 (Abcam, ab8895)"	K-562 BIOREP1_techrep2 ChIP H3K4me3 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisi [...]
+1023-17_raw.txt	FGEDM_complete.txt	A-MEXP-156	1023-17	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP1	K-562_BIOREP1_techrep2	Input control DNA of K-562_BIOREP1	K-562 BIOREP1_techrep2 ChIP H3K4me3 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1106-10_raw.txt	FGEDM_complete.txt	A-MEXP-215	1106-10	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP2	K-562_BIOREP2_techrep1	"IP of K-562_BIOREP2 with anti H3K4me3 (Abcam, ab8895)"	K-562 BIOREP2_techrep1 ChIP H3K4me3 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisi [...]
+1106-10_raw.txt	FGEDM_complete.txt	A-MEXP-215	1106-10	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP2	K-562_BIOREP2_techrep1	Input control DNA of K-562_BIOREP2	K-562 BIOREP2_techrep1 ChIP H3K4me3 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1106-11_raw.txt	FGEDM_complete.txt	A-MEXP-215	1106-11	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP2	K-562_BIOREP2_techrep2	"IP of K-562_BIOREP2 with anti H3K4me3 (Abcam, ab8895)"	K-562 BIOREP2_techrep2 ChIP H3K4me3 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisi [...]
+1106-11_raw.txt	FGEDM_complete.txt	A-MEXP-215	1106-11	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP2	K-562_BIOREP2_techrep2	Input control DNA of K-562_BIOREP2	K-562 BIOREP2_techrep2 ChIP H3K4me3 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1163-8_raw.txt	FGEDM_complete.txt	A-MEXP-215	1163-8	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP3	K-562_BIOREP3_techrep1	"IP of K-562_BIOREP3 with anti H3K4me3 (Abcam, ab8895)"	K-562 BIOREP3_techrep1 ChIP H3K4me3 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	 [...]
+1163-8_raw.txt	FGEDM_complete.txt	A-MEXP-215	1163-8	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP3	K-562_BIOREP3_techrep1	Input control DNA of K-562_BIOREP3	K-562 BIOREP3_techrep1 ChIP H3K4me3 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1163-9_raw.txt	FGEDM_complete.txt	A-MEXP-215	1163-9	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP3	K-562_BIOREP3_techrep2	"IP of K-562_BIOREP3 with anti H3K4me3 (Abcam, ab8895)"	K-562 BIOREP3_techrep2 ChIP H3K4me3 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	 [...]
+1163-9_raw.txt	FGEDM_complete.txt	A-MEXP-215	1163-9	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP3	K-562_BIOREP3_techrep2	Input control DNA of K-562_BIOREP3	K-562 BIOREP3_techrep2 ChIP H3K4me3 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1023-13_raw.txt	FGEDM_complete.txt	A-MEXP-156	1023-13	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP1	K-562_BIOREP1_techrep1	"IP of K-562_BIOREP1 with anti H3ac (Upstate, 06-599)"	K-562 BIOREP1_techrep1 ChIP H3ac hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	fe [...]
+1023-13_raw.txt	FGEDM_complete.txt	A-MEXP-156	1023-13	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP1	K-562_BIOREP1_techrep1	Input control DNA of K-562_BIOREP1	K-562 BIOREP1_techrep1 ChIP H3ac hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1023-18_raw.txt	FGEDM_complete.txt	A-MEXP-156	1023-18	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP1	K-562_BIOREP1_techrep2	"IP of K-562_BIOREP1 with anti H3ac (Upstate, 06-599)"	K-562 BIOREP1_techrep2 ChIP H3ac hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	fe [...]
+1023-18_raw.txt	FGEDM_complete.txt	A-MEXP-156	1023-18	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP1	K-562_BIOREP1_techrep2	Input control DNA of K-562_BIOREP1	K-562 BIOREP1_techrep2 ChIP H3ac hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1106-2_raw.txt	FGEDM_complete.txt	A-MEXP-215	1106-2	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP2	K-562_BIOREP2_techrep1	"IP of K-562_BIOREP2 with anti H3ac (Upstate, 06-599)"	K-562 BIOREP2_techrep1 ChIP H3ac hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	fema [...]
+1106-2_raw.txt	FGEDM_complete.txt	A-MEXP-215	1106-2	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP2	K-562_BIOREP2_techrep1	Input control DNA of K-562_BIOREP2	K-562 BIOREP2_techrep1 ChIP H3ac hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1106-3_raw.txt	FGEDM_complete.txt	A-MEXP-215	1106-3	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP2	K-562_BIOREP2_techrep2	"IP of K-562_BIOREP2 with anti H3ac (Upstate, 06-599)"	K-562 BIOREP2_techrep2 ChIP H3ac hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	fema [...]
+1106-3_raw.txt	FGEDM_complete.txt	A-MEXP-215	1106-3	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP2	K-562_BIOREP2_techrep2	Input control DNA of K-562_BIOREP2	K-562 BIOREP2_techrep2 ChIP H3ac hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1140-2_raw.txt	FGEDM_complete.txt	A-MEXP-215	1140-2	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP3	K-562_BIOREP3_techrep1	"IP of K-562_BIOREP3 with anti H3ac (Upstate, 06-599)"	K-562 BIOREP3_techrep1 ChIP H3ac hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	fema [...]
+1140-2_raw.txt	FGEDM_complete.txt	A-MEXP-215	1140-2	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP3	K-562_BIOREP3_techrep1	Input control DNA of K-562_BIOREP3	K-562 BIOREP3_techrep1 ChIP H3ac hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1140-3_raw.txt	FGEDM_complete.txt	A-MEXP-215	1140-3	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP3	K-562_BIOREP3_techrep2	"IP of K-562_BIOREP3 with anti H3ac (Upstate, 06-599)"	K-562 BIOREP3_techrep2 ChIP H3ac hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	fema [...]
+1140-3_raw.txt	FGEDM_complete.txt	A-MEXP-215	1140-3	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP3	K-562_BIOREP3_techrep2	Input control DNA of K-562_BIOREP3	K-562 BIOREP3_techrep2 ChIP H3ac hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1043-6_raw.txt	FGEDM_complete.txt	A-MEXP-156	1043-6	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP1	K-562_BIOREP1_techrep1	"IP of K-562_BIOREP1 with anti H4ac (Upstate, 06-866)"	K-562 BIOREP1_techrep1 ChIP H4ac hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	fema [...]
+1043-6_raw.txt	FGEDM_complete.txt	A-MEXP-156	1043-6	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP1	K-562_BIOREP1_techrep1	Input control DNA of K-562_BIOREP1	K-562 BIOREP1_techrep1 ChIP H4ac hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1043-7_raw.txt	FGEDM_complete.txt	A-MEXP-156	1043-7	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP1	K-562_BIOREP1_techrep2	"IP of K-562_BIOREP1 with anti H4ac (Upstate, 06-866)"	K-562 BIOREP1_techrep2 ChIP H4ac hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	fema [...]
+1043-7_raw.txt	FGEDM_complete.txt	A-MEXP-156	1043-7	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP1	K-562_BIOREP1_techrep2	Input control DNA of K-562_BIOREP1	K-562 BIOREP1_techrep2 ChIP H4ac hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1106-4_raw.txt	FGEDM_complete.txt	A-MEXP-215	1106-4	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP2	K-562_BIOREP2_techrep1	"IP of K-562_BIOREP2 with anti H4ac (Upstate, 06-866)"	K-562 BIOREP2_techrep1 ChIP H4ac hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	fema [...]
+1106-4_raw.txt	FGEDM_complete.txt	A-MEXP-215	1106-4	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP2	K-562_BIOREP2_techrep1	Input control DNA of K-562_BIOREP2	K-562 BIOREP2_techrep1 ChIP H4ac hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1106-5_raw.txt	FGEDM_complete.txt	A-MEXP-215	1106-5	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP2	K-562_BIOREP2_techrep2	"IP of K-562_BIOREP2 with anti H4ac (Upstate, 06-866)"	K-562 BIOREP2_techrep2 ChIP H4ac hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	fema [...]
+1106-5_raw.txt	FGEDM_complete.txt	A-MEXP-215	1106-5	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP2	K-562_BIOREP2_techrep2	Input control DNA of K-562_BIOREP2	K-562 BIOREP2_techrep2 ChIP H4ac hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1140-20_raw.txt	FGEDM_complete.txt	A-MEXP-215	1140-20	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP3	K-562_BIOREP3_techrep1	"IP of K-562_BIOREP3 with anti H4ac (Upstate, 06-866)"	K-562 BIOREP3_techrep1 ChIP H4ac hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	fe [...]
+1140-20_raw.txt	FGEDM_complete.txt	A-MEXP-215	1140-20	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP3	K-562_BIOREP3_techrep1	Input control DNA of K-562_BIOREP3	K-562 BIOREP3_techrep1 ChIP H4ac hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1163-2_raw.txt	FGEDM_complete.txt	A-MEXP-215	1163-2	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP3	K-562_BIOREP3_techrep2	"IP of K-562_BIOREP3 with anti H4ac (Upstate, 06-866)"	K-562 BIOREP3_techrep2 ChIP H4ac hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	fema [...]
+1163-2_raw.txt	FGEDM_complete.txt	A-MEXP-215	1163-2	GROW_K-562	CROSSLINK_K-562_1	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP3	K-562_BIOREP3_techrep2	Input control DNA of K-562_BIOREP3	K-562 BIOREP3_techrep2 ChIP H4ac hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1604-84_raw.txt	FGEDM_complete.txt	A-MEXP-215	1604-84	GROW_K-562	CROSSLINK_K-562_2	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP1a	K-562_BIOREP1a_techrep1a	"IP of K-562_BIOREP1a with anti H2B (Abcam, ab1790)"	K-562 BIOREP1a_techrep1a ChIP H2B hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	 [...]
+1604-84_raw.txt	FGEDM_complete.txt	A-MEXP-215	1604-84	GROW_K-562	CROSSLINK_K-562_2	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP1a	K-562_BIOREP1a_techrep1a	Input control DNA of K-562_BIOREP1a	K-562 BIOREP1a_techrep1a ChIP H2B hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1604-89_raw.txt	FGEDM_complete.txt	A-MEXP-215	1604-89	GROW_K-562	CROSSLINK_K-562_2	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP1a	K-562_BIOREP1a_techrep2a	"IP of K-562_BIOREP1a with anti H2B (Abcam, ab1790)"	K-562 BIOREP1a_techrep2a ChIP H2B hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	 [...]
+1604-89_raw.txt	FGEDM_complete.txt	A-MEXP-215	1604-89	GROW_K-562	CROSSLINK_K-562_2	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP1a	K-562_BIOREP1a_techrep2a	Input control DNA of K-562_BIOREP1a	K-562 BIOREP1a_techrep2a ChIP H2B hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1604-97_raw.txt	FGEDM_complete.txt	A-MEXP-215	1604-97	GROW_K-562	CROSSLINK_K-562_2	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP2a	K-562_BIOREP2a_techrep1a	"IP of K-562_BIOREP2a with anti H2B (Abcam, ab1790)"	K-562 BIOREP2a_techrep1a ChIP H2B hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	 [...]
+1604-97_raw.txt	FGEDM_complete.txt	A-MEXP-215	1604-97	GROW_K-562	CROSSLINK_K-562_2	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP2a	K-562_BIOREP2a_techrep1a	Input control DNA of K-562_BIOREP2a	K-562 BIOREP2a_techrep1a ChIP H2B hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1604-98_raw.txt	FGEDM_complete.txt	A-MEXP-215	1604-98	GROW_K-562	CROSSLINK_K-562_2	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP2a	K-562_BIOREP2a_techrep2a	"IP of K-562_BIOREP2a with anti H2B (Abcam, ab1790)"	K-562 BIOREP2a_techrep2a ChIP H2B hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	 [...]
+1604-98_raw.txt	FGEDM_complete.txt	A-MEXP-215	1604-98	GROW_K-562	CROSSLINK_K-562_2	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP2a	K-562_BIOREP2a_techrep2a	Input control DNA of K-562_BIOREP2a	K-562 BIOREP2a_techrep2a ChIP H2B hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1604-88_raw.txt	FGEDM_complete.txt	A-MEXP-215	1604-88	GROW_K-562	CROSSLINK_K-562_2	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP3a	K-562_BIOREP3a_techrep1a	"IP of K-562_BIOREP3a with anti H2B (Abcam, ab1790)"	K-562 BIOREP3a_techrep1a ChIP H2B hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	 [...]
+1604-88_raw.txt	FGEDM_complete.txt	A-MEXP-215	1604-88	GROW_K-562	CROSSLINK_K-562_2	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP3a	K-562_BIOREP3a_techrep1a	Input control DNA of K-562_BIOREP3a	K-562 BIOREP3a_techrep1a ChIP H2B hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1604-99_raw.txt	FGEDM_complete.txt	A-MEXP-215	1604-99	GROW_K-562	CROSSLINK_K-562_2	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP3a	K-562_BIOREP3a_techrep2a	"IP of K-562_BIOREP3a with anti H2B (Abcam, ab1790)"	K-562 BIOREP3a_techrep2a ChIP H2B hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	 [...]
+1604-99_raw.txt	FGEDM_complete.txt	A-MEXP-215	1604-99	GROW_K-562	CROSSLINK_K-562_2	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP3a	K-562_BIOREP3a_techrep2a	Input control DNA of K-562_BIOREP3a	K-562 BIOREP3a_techrep2a ChIP H2B hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1604-85_raw.txt	FGEDM_complete.txt	A-MEXP-215	1604-85	GROW_K-562	CROSSLINK_K-562_2	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP1a	K-562_BIOREP1a_techrep1a	"IP of K-562_BIOREP1a with anti H3 (Upstate, ab1791)"	K-562 BIOREP1a_techrep1a ChIP H3 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	 [...]
+1604-85_raw.txt	FGEDM_complete.txt	A-MEXP-215	1604-85	GROW_K-562	CROSSLINK_K-562_2	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP1a	K-562_BIOREP1a_techrep1a	Input control DNA of K-562_BIOREP1a	K-562 BIOREP1a_techrep1a ChIP H3 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1604-91_raw.txt	FGEDM_complete.txt	A-MEXP-215	1604-91	GROW_K-562	CROSSLINK_K-562_2	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP1a	K-562_BIOREP1a_techrep2a	"IP of K-562_BIOREP1a with anti H3 (Upstate, ab1791)"	K-562 BIOREP1a_techrep2a ChIP H3 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	 [...]
+1604-91_raw.txt	FGEDM_complete.txt	A-MEXP-215	1604-91	GROW_K-562	CROSSLINK_K-562_2	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP1a	K-562_BIOREP1a_techrep2a	Input control DNA of K-562_BIOREP1a	K-562 BIOREP1a_techrep2a ChIP H3 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1604-95_raw.txt	FGEDM_complete.txt	A-MEXP-215	1604-95	GROW_K-562	CROSSLINK_K-562_2	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP2a	K-562_BIOREP2a_techrep1a	"IP of K-562_BIOREP2a with anti H3 (Upstate, ab1791)"	K-562 BIOREP2a_techrep1a ChIP H3 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	 [...]
+1604-95_raw.txt	FGEDM_complete.txt	A-MEXP-215	1604-95	GROW_K-562	CROSSLINK_K-562_2	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP2a	K-562_BIOREP2a_techrep1a	Input control DNA of K-562_BIOREP2a	K-562 BIOREP2a_techrep1a ChIP H3 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1604-96_raw.txt	FGEDM_complete.txt	A-MEXP-215	1604-96	GROW_K-562	CROSSLINK_K-562_2	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP2a	K-562_BIOREP2a_techrep2a	"IP of K-562_BIOREP2a with anti H3 (Upstate, ab1791)"	K-562 BIOREP2a_techrep2a ChIP H3 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	 [...]
+1604-96_raw.txt	FGEDM_complete.txt	A-MEXP-215	1604-96	GROW_K-562	CROSSLINK_K-562_2	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP2a	K-562_BIOREP2a_techrep2a	Input control DNA of K-562_BIOREP2a	K-562 BIOREP2a_techrep2a ChIP H3 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1604-90_raw.txt	FGEDM_complete.txt	A-MEXP-215	1604-90	GROW_K-562	CROSSLINK_K-562_2	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP3a	K-562_BIOREP3a_techrep1a	"IP of K-562_BIOREP3a with anti H3 (Upstate, ab1791)"	K-562 BIOREP3a_techrep1a ChIP H3 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	 [...]
+1604-90_raw.txt	FGEDM_complete.txt	A-MEXP-215	1604-90	GROW_K-562	CROSSLINK_K-562_2	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP3a	K-562_BIOREP3a_techrep1a	Input control DNA of K-562_BIOREP3a	K-562 BIOREP3a_techrep1a ChIP H3 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
+1604-94_raw.txt	FGEDM_complete.txt	A-MEXP-215	1604-94	GROW_K-562	CROSSLINK_K-562_2	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7423	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP3a	K-562_BIOREP3a_techrep2a	"IP of K-562_BIOREP3a with anti H3 (Upstate, ab1791)"	K-562 BIOREP3a_techrep2a ChIP H3 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy3	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	 [...]
+1604-94_raw.txt	FGEDM_complete.txt	A-MEXP-215	1604-94	GROW_K-562	CROSSLINK_K-562_2	CHROMATIN_IMMUNOPRECIPITATION	P-MEXP-7422	HYBRIDISATION_TECAN	SCANARRAYEXPRESS-2	TRANSFORMATION	K-562	K-562_BIOREP3a	K-562_BIOREP3a_techrep2a	Input control DNA of K-562_BIOREP3a	K-562 BIOREP3a_techrep2a ChIP H3 hyb	cell	genomic_DNA	genomic_DNA	synthetic_DNA	Cy5	Homo sapiens	frozen_sample	K-562	human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis	female	K-562	
diff --git a/examples/real/E-TABM-16.png b/examples/real/E-TABM-16.png
new file mode 100644
index 0000000..059001b
Binary files /dev/null and b/examples/real/E-TABM-16.png differ
diff --git a/examples/real/E-TABM-16.txt b/examples/real/E-TABM-16.txt
new file mode 100755
index 0000000..03d2b2c
--- /dev/null
+++ b/examples/real/E-TABM-16.txt
@@ -0,0 +1,166 @@
+Experiment section   	
+domain	ebi.ac.uk
+accession	E-TABM-16
+experiment_design_type	quality_control_testing_design
+name	FDA-CDER MTS RNA reagent cross platform test
+description	"This experiment is a test of a reagent that can provide a basis for performance assessments on Affymetrix RAE230A,  CodeLink UniSet Rat I, and Agilent G4130A rat oligonucleotide arrays.   The reagent is a set of two mixed tissue RNA samples formulated to contain different ratios of RNA for each of four rat tissues (brain, liver, kidney, and testes).   The analytes in this assay bind to a subset of gene probes that were matched across platforms to the same UniGene cluster or  [...]
+release_date	2005-11-23
+submission_date	2005-06-01
+submitter	Scott Pine
+organization	FDA-CDER
+publication_title	
+authors	
+journal	unknown
+volume	
+issue	
+pages	
+year				
+
+Protocol section				
+accession	type	text	name	parameters
+6377	grow	Rodents received certified rodent diet #5002C ad lib and drinking water purified by reverse osmosis.   The animals were acclimated for 6 days prior to euthanasia.   The animals were on a 12 hr light/dark cycle and euthanasia was performed consistently within 4-6  hr after the start of the light cycle. Each batch was made up of 8 animals from the same shipment. 	MTS Batches1-3	
+6380	nucleic_acid_extraction	"After euthanasia in a slow charged CO2 chamber, the rats were immediately decapitated to allow for rapid access to the brain.  Four organs were quickly removed in the following order:  brain, liver, kidneys, and testes.  The whole brain, including brain stem but excluding pituitary gland, was collected.  Tissues were quickly dissected into 0.5 cm sections while submersed in RNAlater (Ambion, Austin, TX) in sterile petri plates and placed into 50 mL tubes con [...]
+6396	pool	"Total RNA was quantitated by ultraviolet/visible wavelength spectrophotometry.  For each tissue, equal amounts of RNA were pooled from each of the 8 animals in the same shipment to create tissue shipment specific pools.  2 mg each of two mixtures (Mix1 and Mix2) were prepared for each shipment from the pooled same-tissue RNAs.    "	MTS Tissue pools	
+6396-mix1	pool	"Mix1 consisted of 200 ug testis RNA,  600 ug liver RNA, 800 ug brain RNA, and 400 ug kidney RNA. 50 ug aliquots of each mixture were frozen at -70C."	MTS Mix1	
+6396-mix2	pool	"Mix2 consisted of 800 ug testis RNA,  400 ug liver RNA, 400 ug brain RNA, and 400 ug kidney RNA.  50 ug aliquots of each mixture were frozen at -70C."	MTS Mix2	
+6407	image_aquisition	GeneChips were scanned on a GeneArray 2500 scanner using MicroArray Suite v. 5.0.0.032 and default scanner settings.	MTS Affymetrix - GeneArray Scanner	
+6409	image_aquisition	GeneChips were scanned on a GeneChip 3000 scanner using default scanner settings.	MTS Affymetrix - GeneChip Scanner	
+6486	labeling	"1st strand cDNA synthesis: <br><br>For each sample, add<br><br>2 ul T7-(dT)24 primer @ 50 uM (Affymetrix PN 900375) to          <br><br>5 ug (up to 8 ul) total RNA.                     <br><br>Incubate @ 70C, 10 minutes, quick spin, quench on ice.<br><br><br><br>Make the following cocktail for 5 reactions:<br><br>20 ul     5X 1st Strand cDNA Buffer (Invitrogen PN 18064014), <br><br>10 ul     0.1 M DTT, and<br><br> 5 ul     10 mM dNTP mix (Invitrogen PN 18427013).           [...]
+6489	labeling	"1st strand cDNA synthesis: <br><br><br><br>For each sample, add 2 ul T7-(dT)24 primer @ 50 uM (Affymetrix PN 900375) to 5 ug (up to 8 ul) total RNA. Incubate @ 70C, 10 minutes, quick spin, quench on ice.<br><br><br><br>Make the following cocktail for 5 reactions using Invitrogen SuperScript Double-Stranded cDNA Synthesis Kit (PN 11917-010):  20 ul 5X 1st Strand cDNA Buffer, 10 ul 0.1 M DTT, and  5 ul 10 mM dNTP mix.  Add 7 ul to each reaction.  Incubate @ 45 C for 2 minute [...]
+6491	hybridization	"Hybridization cocktail for single array:  15 ug fragmented labeled cRNA, 5 uL Control Oligonucleotide B2 (component in kit; Affymetrix P/N 900299), 15 uL 20x Eukaryotic Hybridization Controls (component in kit; Affymetrix P/N 900299), 3 uL Herring Sperm DNA (10 mg/mL) (Promega P/N D1811), 3 uL Acetylated BSA (50 mg/mL) (Invitrogen P/N 15561-020),  2x Hybridization Buffer (200 mM MES, 2 M [Na+], 40 mM EDTA, 0.02 % Tween 20), and H2O to final volume of 300 uL. <br><br>< [...]
+6552	bioassay_data_transformation	"Microarray Suite v. 5.0.0.032 software (Affymetrix, Inc.) was used to calculate signal values using default settings (Parameters: Gamma 1L, 0.0045; Gamma 1H, 0.0045; Gamma 2L, 0.006; Gamma 2H, 0.006; Perturbation, 1.1; alpha1, 0.05; alpha2, 0.065; tau, 0.015).  All data was global scaled to a target intensity of 500.  "	MTS Affymetrix MAS5	
+6553	bioassay_data_transformation	"Probe set signal values were calculated from CEL files with GeneChip Operating Software v. 1.2.0.003 using the Probe Logarithmic Intensity Error Estimation (PLIER, Affymetrix, Inc.) algorithm using default settings (Probe selection, PM-MM; Background method, none; Optimization, full; Combine replicates, no; Quantile normalization, yes; GM control, 0.15, Attenuation, 0.005, Probe penalty, 0.001, Concentration penalty, 1e-006; Background percent, 0.05; al [...]
+6564	bioassay_data_transformation	"MAS5: Microarray Suite v. 5.0.0.032 software (Affymetrix, Inc.) was used to calculate signal values using default settings (Parameters: Gamma 1L, 0.0045; Gamma 1H, 0.0045; Gamma 2L, 0.006; Gamma 2H, 0.006; Perturbation, 1.1; alpha1, 0.05; alpha2, 0.065; tau, 0.015).  All data was global scaled to a target intensity of 500.<br><br>PLIER: Probe set signal values were calculated from CEL files with GeneChip Operating Software v. 1.2.0.003 using the Probe L [...]
+6672	nucleic_acid_extraction	"Total RNAs isolated from rat brain (Cat.# 7912), kidney (Cat.# 7926), liver (Cat.# 7910), and testis (Cat.# 7934) were obtained from Ambion (Austin, TX).  Total RNAs isolated from rat brain (Cat.# 737001), kidney (Cat.# 737007), liver (Cat.# 737009), and testis (Cat.# 737023) were obtained from Statagene (La Jolla, CA)."	MTS Commercial RNA
+6673	grow	Commercial source.	MTS Batches4-5
+6841	pool	MTS were prepared from commercial RNAs in proportions based upon the RNA concentrations provided by the supplier. 	MTS Mix1 Mix2 (commercial source)
+6841-mix1	pool	"Mix1 consisted of 5 ug testis RNA, 15 ug liver RNA, 20 ug brain RNA, and 10 ug kidney RNA.  50 ug aliquots of each mixture were frozen at -70C."	MTS Mix1 (commercial source)
+6841-mix2	pool	"Mix2 consisted of 20 ug testis RNA, 10 ug liver RNA, 10 ug brain RNA, and 10 ug kidney RNA. 50 ug aliquots of each mixture were frozen at -70C."	MTS Mix2 (commercial source)
+7671	labeling	"Target labeling of 2 ug total RNA per reaction was performed essentially as described in the CodeLink Expression Bioarrays Automated Target Preparation addendum using the Qiagen BioRobot 9604.  CDNA synthesis, cRNA preparation, and cRNA purification were processed in a 96-well format using automated Qiagen Biorobot 9604 procedures.  CDNA synthesis were performed using reagents in the CodeLink cDNA Synthesis Kit (PN 320003) supplemented with 10 mM biotin-11-UTP and 10 mM bi [...]
+7672	labeling	Target labeling of 2 ug total RNA per reaction was performed as specified in the manufacturers� protocol in the manual  �Codelink Gene Expression System:  Manual Labelled cRNA Target Preparation.�  CDNA synthesis and in vitro transcription reactions were performed using reagents in the CodeLink Expression Assay Reagent Kit (PN 320012) and 10 mM biotin-11-UTP (PerkinElmer NEL543 or 999-543).  Purification of double-stranded cDNA was performed using a QIAquick PCR purificatio [...]
+8188	hybridization	"Hybridization and detection was performed as specified in the instruction manual ""Codelink Gene Expression System:  Single-Assay Bioarray Hybridization and Detection.""  Reagents for fragmentation and hybridization were from the CodeLink Expression Assay Reagent Kit (PN 320012).  After fragmentation, 10 ug cRNA was hybridized in a total volume of 260 ul to each CodeLink array for 20 hr at 37C on an orbital shaker.  <br><br>Post-hybridization, the arrays were washed i [...]
+8189	hybridization	"Hybridization and detection was performed as specified in the instruction manual ""Codelink Gene Expression System:  Single-Assay Bioarray Hybridization and Detection"" and the user guide CodeLink Expression Bioarrays Alexa Fluor 647-Streptavidin Detection Addendum.""  Reagents for fragmentation and hybridization were from the CodeLink Expression Assay Reagent Kit (PN 320012).  After fragmentation, 10 ug cRNA was hybridized in a total volume of 260 ul to each CodeLink [...]
+8190	image_aquisition	"Scanning was performed using an Axon 4000B scanner at 10 micron scan resolution, using settings defined in the user manual.  Feature extraction was performed using CodeLink Expression Analysis software v2.2.25.   Spot intensity was calculated as the average of the pixel signals in the area of interest minus the scaled 2-pixel median background.  The intensities were then median normalized by calculating the median of all spots, minus the control probes and blanks,  [...]
+8309	image_aquisition	"Scanning was performed using an Axon 4000B scanner at 10 micron scan resolution, using settings defined in the user manual.  Feature extraction was performed using CodeLink Expression Analysis software v2.3.2.   Spot intensity was calculated as the average of the pixel signals in the area of interest minus the scaled 2-pixel median background.  The intensities were then median normalized by calculating the median of all spots, minus the control probes and blanks, a [...]
+8532	labeling	5 ug of total RNA was labeled using the protocol described in Hughes et al. Nature Biotechnol 19: 342-347.	MTS Agilent 1 (Cy3)
+8534	labeling	5 ug of total RNA was labeled using the protocol described in Hughes et al. Nature Biotechnol 19: 342-347.	MTS Agilent 1 (Cy5)
+8535	labeling	100 ng of total RNA was labeled using the Agilent Low Input RNA Fluorescent Linear Amplification Kit (P/N 5184-3523).	MTS Agilent 2 (Cy3)
+8536	labeling	100 ng of total RNA was labeled using the Agilent Low Input RNA Fluorescent Linear Amplification Kit (P/N 5184-3523).	MTS Agilent 2 (Cy5)
+8537	labeling	500 ng of total RNA was labeled using the Agilent Low Input RNA Fluorescent Linear Amplification Kit (P/N 5184-3523).	MTS Agilent 3 (Cy3)																												
+8538	labeling	500 ng of total RNA was labeled using the Agilent Low Input RNA Fluorescent Linear Amplification Kit (P/N 5184-3523).	MTS Agilent 3 (Cy5)																												
+8573	hybridization	5 ug of cRNA was hybridized to Agilent G4130A arrays using the protocol described in Hughes et al. Nature Biotechnol 19: 342-347.	MTS Agilent 1																												
+8588	hybridization	0.75 ug labeled cRNA was hybridized per dye channel to Agilent G4130A arrays using Agilent microarray hybridization chambers (G2534A).    The arrays were hybridized for 17 hr at 60C at rotor setting 4 in Agilent recommended oven (G2505-80081).  Microarrays were washed and dried as specified in the user manual (Agilent 60-mer Oligo Microarray Processing Protocol).	MTS Agilent 2																												
+8589	hybridization	0.75 ug labeled cRNA was hybridized per dye channel to Agilent G4130A arrays using Agilent microarray hybridization chambers (G2534A).    The arrays were hybridized for 17 hr at 60C at rotor setting 5 in Agilent recommended oven (G2505-80081).  Microarrays were washed and dried as specified in the user manual (Agilent 60-mer Oligo Microarray Processing Protocol).	MTS Agilent 3																												
+8590	image_aquisition	"Agilent arrays were scanned using the Agilent DNA microarray scanner (G2565BA) using all default settings.  TIFF images were processed with Agilent Feature Extraction software version A.7.4.47 (a prerelease version which is algorithmically identical to v 7.5.1) using default settings.  Adjustment for local variations in background signal was performed using a spatial detrending algorithm.  To normalize and correct for dye bias, a combined method of linear scaling i [...]
+8749	bioassay_data_transformation	Mix1 signals were normalized to Mix2 using the 10% trimmed mean signal of the kidney-selective probe subset.   The ratio of Mix1 to Mix2 was calculated using the kidney normalized data. <br><br>	MTS Kidney-Selective Probe Normalization																												
+8764	bioassay_data_transformation	"To normalize and correct for dye bias, a combined method of linear scaling in each channel followed by LOWESS curve fitting (�Linear&LOWESS� option in Agilent Feature Extraction Software version A.7.5) was used.  Mix1 and Mix2 signals were calculated from the average of dye-swap replicates.  This step was followed by normalization of Mix1 to Mix2 using the 10% trimmed mean signal of the kidney-selective probe subset.  Features flagged as outliers by the [...]
+8946	bioassay_data_transformation	"Gene summary values were calculated from CEL files using the Probe Logarithmic Intensity Error Estimation (PLIER) algorithm (http://www.affymetrix.com/support/developer/adn_news/and_news_1004.affx).  An affinity model for the PLIER analysis was constructed from single-tissue hybridizations.  RNA from each of 4 different tissues was pooled across 8 animals from a single shipment (n=3) and hybridized to Affymetrix RAE230A arrays (n=12).  PLIER signal calc [...]
+8947	bioassay_data_transformation	(null)	TRANPRTCL8947																												
+
+
+Hybridization section																															
+TSAMPLE_SYSUID	BioSource	BioSourceMaterial	BioMaterialCharacteristics[Organism]	BioMaterialCharacteristics[BioSourceProvider]	BioMaterialCharacteristics[Age]	BioMaterialCharacteristics[Sex]	BioMaterialCharacteristics[StrainOrLine]	Protocol[grow]	Protocol[treatment]	FactorValue[Platform]	Sample	Extract	Protocol[?]	Protocol[extraction]	LabeledExtract	dye	Protocol[labeling]	Hybridization	FactorValue[Individual]	FactorValue[Mix]	FactorValue[Operator]	Protocol[hybridization]	Protocol[scanning [...]
+173435	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Affymetrix	MTS Batch1	MTS Batch1 Mix1	6396	6396-mix1	MTS Site1 Batch1 Mix1	biotin	6486	MTS Site1 Batch1 Mix1	Batch1	Mix1	Site1	6491	6407	6553	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3002245	Site1_Batch1_Mix1.CEL	Site1_Batch1_Mix1.EXP	Site1_Batch1_Mix1_PLIER	Site1_Batch1_Mix1_PLIER.CHP
+173436	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Affymetrix	MTS Batch1	MTS Batch1 Mix2	6396	6396-mix2	MTS Site1 Batch1 Mix2	biotin	6486	MTS Site1 Batch1 Mix2	Batch1	Mix2	Site1	6491	6407	6553	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3002245	Site1_Batch1_Mix2.CEL	Site1_Batch1_Mix2.EXP	Site1_Batch1_Mix2_PLIER	Site1_Batch1_Mix2_PLIER.CHP
+173437	MTS Batch2	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Affymetrix	MTS Batch2	MTS Batch2 Mix1	6396	6396-mix1	MTS Site1 Batch2 Mix1	biotin	6486	MTS Site1 Batch2 Mix1	Batch2	Mix1	Site1	6491	6407	6553	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3002245	Site1_Batch2_Mix1.CEL	Site1_Batch2_Mix1.EXP	Site1_Batch2_Mix1_PLIER	Site1_Batch2_Mix1_PLIER.CHP
+173438	MTS Batch2	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Affymetrix	MTS Batch2	MTS Batch2 Mix2	6396	6396-mix2	MTS Site1 Batch2 Mix2	biotin	6486	MTS Site1 Batch2 Mix2	Batch2	Mix2	Site1	6491	6407	6553	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3002245	Site1_Batch2_Mix2.CEL	Site1_Batch2_Mix2.EXP	Site1_Batch2_Mix2_PLIER	Site1_Batch2_Mix2_PLIER.CHP
+173439	MTS Batch3	whole_organism	Rattus norvegicus	"Harlan, Frederick, MD"	6-7 weeks	male	Sprague-Dawley	6377	6380	Affymetrix	MTS Batch3	MTS Batch3 Mix1	6396	6396-mix1	MTS Site1 Batch3 Mix1	biotin	6486	MTS Site1 Batch3 Mix1	Batch3	Mix1	Site1	6491	6407	6553	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3002245	Site1_Batch3_Mix1.CEL	Site1_Batch3_Mix1.EXP	Site1_Batch3_Mix1_PLIER	Site1_Batch3_Mix1_PLIER.CHP
+173440	MTS Batch3	whole_organism	Rattus norvegicus	"Harlan, Frederick, MD"	6-7 weeks	male	Sprague-Dawley	6377	6380	Affymetrix	MTS Batch3	MTS Batch3 Mix2	6396	6396-mix2	MTS Site1 Batch3 Mix2	biotin	6486	MTS Site1 Batch3 Mix2	Batch3	Mix2	Site1	6491	6407	6553	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3002245	Site1_Batch3_Mix2.CEL	Site1_Batch3_Mix2.EXP	Site1_Batch3_Mix2_PLIER	Site1_Batch3_Mix2_PLIER.CHP
+173435	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Affymetrix	MTS Batch1	MTS Batch1 Mix1	6396	6396-mix1	MTS Site2 Batch1 Mix1	biotin	6489	MTS Site2 Batch1 Mix1	Batch1	Mix1	Site2	6491	6407	6553	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3005334	Site2_Batch1_Mix1.CEL	Site2_Batch1_Mix1.EXP	Site2_Batch1_Mix1_PLIER	Site2_Batch1_Mix1_PLIER.CHP
+173436	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Affymetrix	MTS Batch1	MTS Batch1 Mix2	6396	6396-mix2	MTS Site2 Batch1 Mix2	biotin	6489	MTS Site2 Batch1 Mix2	Batch1	Mix2	Site2	6491	6407	6553	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3005334	Site2_Batch1_Mix2.CEL	Site2_Batch1_Mix2.EXP	Site2_Batch1_Mix2_PLIER	Site2_Batch1_Mix2_PLIER.CHP
+173437	MTS Batch2	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Affymetrix	MTS Batch2	MTS Batch2 Mix1	6396	6396-mix1	MTS Site2 Batch2 Mix1	biotin	6489	MTS Site2 Batch2 Mix1	Batch2	Mix1	Site2	6491	6407	6553	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3005334	Site2_Batch2_Mix1.CEL	Site2_Batch2_Mix1.EXP	Site2_Batch2_Mix1_PLIER	Site2_Batch2_Mix1_PLIER.CHP
+173438	MTS Batch2	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Affymetrix	MTS Batch2	MTS Batch2 Mix2	6396	6396-mix2	MTS Site2 Batch2 Mix2	biotin	6489	MTS Site2 Batch2 Mix2	Batch2	Mix2	Site2	6491	6407	6553	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3005334	Site2_Batch2_Mix2.CEL	Site2_Batch2_Mix2.EXP	Site2_Batch2_Mix2_PLIER	Site2_Batch2_Mix2_PLIER.CHP
+173439	MTS Batch3	whole_organism	Rattus norvegicus	"Harlan, Frederick, MD"	6-7 weeks	male	Sprague-Dawley	6377	6380	Affymetrix	MTS Batch3	MTS Batch3 Mix1	6396	6396-mix1	MTS Site2 Batch3 Mix1	biotin	6489	MTS Site2 Batch3 Mix1	Batch3	Mix1	Site2	6491	6407	6553	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3005334	Site2_Batch3_Mix1.CEL	Site2_Batch3_Mix1.EXP	Site2_Batch3_Mix1_PLIER	Site2_Batch3_Mix1_PLIER.CHP
+173440	MTS Batch3	whole_organism	Rattus norvegicus	"Harlan, Frederick, MD"	6-7 weeks	male	Sprague-Dawley	6377	6380	Affymetrix	MTS Batch3	MTS Batch3 Mix2	6396	6396-mix2	MTS Site2 Batch3 Mix2	biotin	6489	MTS Site2 Batch3 Mix2	Batch3	Mix2	Site2	6491	6407	6553	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3005334	Site2_Batch3_Mix2.CEL	Site2_Batch3_Mix2.EXP	Site2_Batch3_Mix2_PLIER	Site2_Batch3_Mix2_PLIER.CHP
+173435	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Affymetrix	MTS Batch1	MTS Batch1 Mix1	6396	6396-mix1	MTS Site3 Batch1 Mix1	biotin	6486	MTS Site3 Batch1 Mix1	Batch1	Mix1	Site3	6491	6409	6553	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3005331	Site3_Batch1_Mix1.CEL	Site3_Batch1_Mix1.EXP	Site3_Batch1_Mix1_PLIER	Site3_Batch1_Mix1_PLIER.CHP
+173436	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Affymetrix	MTS Batch1	MTS Batch1 Mix2	6396	6396-mix2	MTS Site3 Batch1 Mix2	biotin	6486	MTS Site3 Batch1 Mix2	Batch1	Mix2	Site3	6491	6409	6553	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3005331	Site3_Batch1_Mix2.CEL	Site3_Batch1_Mix2.EXP	Site3_Batch1_Mix2_PLIER	Site3_Batch1_Mix2_PLIER.CHP
+173437	MTS Batch2	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Affymetrix	MTS Batch2	MTS Batch2 Mix1	6396	6396-mix1	MTS Site3 Batch2 Mix1	biotin	6486	MTS Site3 Batch2 Mix1	Batch2	Mix1	Site3	6491	6409	6553	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3005331	Site3_Batch2_Mix1.CEL	Site3_Batch2_Mix1.EXP	Site3_Batch2_Mix1_PLIER	Site3_Batch2_Mix1_PLIER.CHP
+173438	MTS Batch2	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Affymetrix	MTS Batch2	MTS Batch2 Mix2	6396	6396-mix2	MTS Site3 Batch2 Mix2	biotin	6486	MTS Site3 Batch2 Mix2	Batch2	Mix2	Site3	6491	6409	6553	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3005331	Site3_Batch2_Mix2.CEL	Site3_Batch2_Mix2.EXP	Site3_Batch2_Mix2_PLIER	Site3_Batch2_Mix2_PLIER.CHP
+173439	MTS Batch3	whole_organism	Rattus norvegicus	"Harlan, Frederick, MD"	6-7 weeks	male	Sprague-Dawley	6377	6380	Affymetrix	MTS Batch3	MTS Batch3 Mix1	6396	6396-mix1	MTS Site3 Batch3 Mix1	biotin	6486	MTS Site3 Batch3 Mix1	Batch3	Mix1	Site3	6491	6409	6553	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3005331	Site3_Batch3_Mix1.CEL	Site3_Batch3_Mix1.EXP	Site3_Batch3_Mix1_PLIER	Site3_Batch3_Mix1_PLIER.CHP
+173440	MTS Batch3	whole_organism	Rattus norvegicus	"Harlan, Frederick, MD"	6-7 weeks	male	Sprague-Dawley	6377	6380	Affymetrix	MTS Batch3	MTS Batch3 Mix2	6396	6396-mix2	MTS Site3 Batch3 Mix2	biotin	6486	MTS Site3 Batch3 Mix2	Batch3	Mix2	Site3	6491	6409	6553	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3005331	Site3_Batch3_Mix2.CEL	Site3_Batch3_Mix2.EXP	Site3_Batch3_Mix2_PLIER	Site3_Batch3_Mix2_PLIER.CHP
+173435	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Affymetrix	MTS Batch1	MTS Batch1 Mix1	6396	6396-mix1	MTS Site1 Batch1 Mix1 1YR	biotin	6486	MTS Site1 Batch1 Mix1 1YR	Batch1	Mix1	Site1	6491	6407	6553	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3002256	Site1_Batch1_Mix1_1YR.CEL	Site1_Batch1_Mix1_1YR.EXP	Site1_Batch1_Mix1_1YR_PLIER	Site1_Batch1_Mix1_1YR_PLIER.CHP
+173436	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Affymetrix	MTS Batch1	MTS Batch1 Mix2	6396	6396-mix2	MTS Site1 Batch1 Mix2 1YR	biotin	6486	MTS Site1 Batch1 Mix2 1YR	Batch1	Mix2	Site1	6491	6407	6553	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3002256	Site1_Batch1_Mix2_1YR.CEL	Site1_Batch1_Mix2_1YR.EXP	Site1_Batch1_Mix2_1YR_PLIER	Site1_Batch1_Mix2_1YR_PLIER.CHP
+173795	MTS Batch4	total_RNA	Rattus norvegicus	"Ambion (Austin, TX)"		unknown_sex	Sprague-Dawley	6673	6672	Affymetrix	MTS Batch4	MTS Batch4 Mix1	6841	6841-mix1	MTS Site1 Batch4 Mix1	biotin	6486	MTS Site1 Batch4 Mix1	Batch4	Mix1	Site1	6491	6407	6553	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3002254	Site1_Batch4_Mix1.CEL	Site1_Batch4_Mix1.EXP	Site1_Batch4_Mix1_PLIER	Site1_Batch4_Mix1_PLIER.CHP
+173796	MTS Batch4	total_RNA	Rattus norvegicus	"Ambion (Austin, TX)"		unknown_sex	Sprague-Dawley	6673	6672	Affymetrix	MTS Batch4	MTS Batch4 Mix2	6841	6841-mix2	MTS Site1 Batch4 Mix2	biotin	6486	MTS Site1 Batch4 Mix2	Batch4	Mix2	Site1	6491	6407	6553	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3002254	Site1_Batch4_Mix2.CEL	Site1_Batch4_Mix2.EXP	Site1_Batch4_Mix2_PLIER	Site1_Batch4_Mix2_PLIER.CHP
+173797	MTS Batch5	total_RNA	Rattus norvegicus	"Stratagene (La Jolla, CA)"		unknown_sex	Sprague-Dawley	6673	6672	Affymetrix	MTS Batch5	MTS Batch5 Mix1	6841	6841-mix1	MTS Site1 Batch5 Mix1	biotin	6486	MTS Site1 Batch5 Mix1	Batch5	Mix1	Site1	6491	6407	6553	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3002256	Site1_Batch5_Mix1.CEL	Site1_Batch5_Mix1.EXP	Site1_Batch5_Mix1_PLIER	Site1_Batch5_Mix1_PLIER.CHP
+173798	MTS Batch5	total_RNA	Rattus norvegicus	"Stratagene (La Jolla, CA)"		unknown_sex	Sprague-Dawley	6673	6672	Affymetrix	MTS Batch5	MTS Batch5 Mix2	6841	6841-mix2	MTS Site1 Batch5 Mix2	biotin	6486	MTS Site1 Batch5 Mix2	Batch5	Mix2	Site1	6491	6407	6553	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3002256	Site1_Batch5_Mix2.CEL	Site1_Batch5_Mix2.EXP	Site1_Batch5_Mix2_PLIER	Site1_Batch5_Mix2_PLIER.CHP
+175308	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Codelink	MTS Batch1	MTS Batch1 Mix1	6396	6396-mix1	MTS Site4 Batch1 Mix1	biotin	7672	MTS Site4 Batch1 Mix1	Batch1	Mix1	Site4	8188	8309		A-MEXP-124		T00231505	Site4_Batch1_Mix1_Data.txt			
+175309	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Codelink	MTS Batch1	MTS Batch1 Mix2	6396	6396-mix2	MTS Site4 Batch1 Mix2	biotin	7672	MTS Site4 Batch1 Mix2	Batch1	Mix2	Site4	8188	8309		A-MEXP-124		T00231509	Site4_Batch1_Mix2_Data.txt			
+175310	MTS Batch2	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Codelink	MTS Batch2	MTS Batch2 Mix1	6396	6396-mix1	MTS Site4 Batch2 Mix1	biotin	7672	MTS Site4 Batch2 Mix1	Batch2	Mix1	Site4	8188	8309		A-MEXP-124		T00231506	Site4_Batch2_Mix1_Data.txt			
+175311	MTS Batch2	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Codelink	MTS Batch2	MTS Batch2 Mix2	6396	6396-mix2	MTS Site4 Batch2 Mix2	biotin	7672	MTS Site4 Batch2 Mix2	Batch2	Mix2	Site4	8188	8309		A-MEXP-124		T00231504	Site4_Batch2_Mix2_Data.txt			
+175312	MTS Batch3	whole_organism	Rattus norvegicus	"Harlan, Frederick, MD"	6-7 weeks	male	Sprague-Dawley	6377	6380	Codelink	MTS Batch3	MTS Batch3 Mix1	6396	6396-mix1	MTS Site4 Batch3 Mix1	biotin	7672	MTS Site4 Batch3 Mix1	Batch3	Mix1	Site4	8188	8309		A-MEXP-124		T00231511	Site4_Batch3_Mix1_Data.txt			
+175313	MTS Batch3	whole_organism	Rattus norvegicus	"Harlan, Frederick, MD"	6-7 weeks	male	Sprague-Dawley	6377	6380	Codelink	MTS Batch3	MTS Batch3 Mix2	6396	6396-mix2	MTS Site4 Batch3 Mix2	biotin	7672	MTS Site4 Batch3 Mix2	Batch3	Mix2	Site4	8188	8309		A-MEXP-124		T00231510	Site4_Batch3_Mix2_Data.txt			
+175308	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Codelink	MTS Batch1	MTS Batch1 Mix1	6396	6396-mix1	MTS Site5 Batch1 Mix1	biotin	7671	MTS Site5 Batch1 Mix1	Batch1	Mix1	Site5	8189	8190		A-MEXP-124		T00221468	Site5_Batch1_Mix1_Data.txt			
+175309	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Codelink	MTS Batch1	MTS Batch1 Mix2	6396	6396-mix2	MTS Site5 Batch1 Mix2	biotin	7671	MTS Site5 Batch1 Mix2	Batch1	Mix2	Site5	8189	8190		A-MEXP-124		T00221469	Site5_Batch1_Mix2_Data.txt			
+175310	MTS Batch2	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Codelink	MTS Batch2	MTS Batch2 Mix1	6396	6396-mix1	MTS Site5 Batch2 Mix1	biotin	7671	MTS Site5 Batch2 Mix1	Batch2	Mix1	Site5	8189	8190		A-MEXP-124		T00221470	Site5_Batch2_Mix1_Data.txt			
+175311	MTS Batch2	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Codelink	MTS Batch2	MTS Batch2 Mix2	6396	6396-mix2	MTS Site5 Batch2 Mix2	biotin	7671	MTS Site5 Batch2 Mix2	Batch2	Mix2	Site5	8189	8190		A-MEXP-124		T00221471	Site5_Batch2_Mix2_Data.txt			
+175312	MTS Batch3	whole_organism	Rattus norvegicus	"Harlan, Frederick, MD"	6-7 weeks	male	Sprague-Dawley	6377	6380	Codelink	MTS Batch3	MTS Batch3 Mix1	6396	6396-mix1	MTS Site5 Batch3 Mix1	biotin	7671	MTS Site5 Batch3 Mix1	Batch3	Mix1	Site5	8189	8190		A-MEXP-124		T00221472	Site5_Batch3_Mix1_Data.txt
+175313	MTS Batch3	whole_organism	Rattus norvegicus	"Harlan, Frederick, MD"	6-7 weeks	male	Sprague-Dawley	6377	6380	Codelink	MTS Batch3	MTS Batch3 Mix2	6396	6396-mix2	MTS Site5 Batch3 Mix2	biotin	7671	MTS Site5 Batch3 Mix2	Batch3	Mix2	Site5	8189	8190		A-MEXP-124		T00221473	Site5_Batch3_Mix2_Data.txt
+177825	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch1	MTS Batch1 Mix2	6396	6396-mix2	MTS Site7 Batch1 Mix2 (Cy3)	Cy3	8532	MTS Site7 Batch1 rMix1 gMix2	Batch1	Mix1;Mix2	Site7	8573	8590		A-AGIL-6		16011868020448_A01	Site7_Batch1_rMix1_gMix2.txt
+177824	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch1	MTS Batch1 Mix1	6396	6396-mix1	MTS Site7 Batch1 Mix1 (Cy5)	Cy5	8534	MTS Site7 Batch1 rMix1 gMix2	Batch1	Mix1;Mix2	Site7	8573	8590		A-AGIL-6		16011868020448_A01	Site7_Batch1_rMix1_gMix2.txt
+177825	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch1	MTS Batch1 Mix2	6396	6396-mix2	MTS Site7 Batch1 Mix2 (Cy5)	Cy5	8534	MTS Site7 Batch1 rMix2 gMix1	Batch1	Mix1;Mix2	Site7	8573	8590		A-AGIL-6		16011868020457_A01	Site7_Batch1_rMix2_gMix1.txt
+177824	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch1	MTS Batch1 Mix1	6396	6396-mix1	MTS Site7 Batch1 Mix1 (Cy3)	Cy3	8532	MTS Site7 Batch1 rMix2 gMix1	Batch1	Mix1;Mix2	Site7	8573	8590		A-AGIL-6		16011868020457_A01	Site7_Batch1_rMix2_gMix1.txt
+177826	MTS Batch2	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch2	MTS Batch2 Mix1	6396	6396-mix1	MTS Site7 Batch2 Mix1 (Cy5)	Cy5	8534	MTS Site7 Batch2 rMix1 gMix2	Batch2	Mix1;Mix2	Site7	8573	8590		A-AGIL-6		16011868020452_A01	Site7_Batch2_rMix1_gMix2.txt
+177827	MTS Batch2	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Spraque-Dawley	6377	6380	Agilent	MTS Batch2	MTS Batch2 Mix2	6396	6396-mix2	MTS Site7 Batch2 Mix2 (Cy3)	Cy3	8532	MTS Site7 Batch2 rMix1 gMix2	Batch2	Mix1;Mix2	Site7	8573	8590		A-AGIL-6		16011868020452_A01	Site7_Batch2_rMix1_gMix2.txt
+177827	MTS Batch2	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Spraque-Dawley	6377	6380	Agilent	MTS Batch2	MTS Batch2 Mix2	6396	6396-mix2	MTS Site7 Batch2 Mix2 (Cy5)	Cy5	8534	MTS Site7 Batch2 rMix2 gMix1	Batch2	Mix1;Mix2	Site7	8573	8590		A-AGIL-6		16011868020451_A01	Site7_Batch2_rMix2_gMix1.txt
+177826	MTS Batch2	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch2	MTS Batch2 Mix1	6396	6396-mix1	MTS Site7 Batch2 Mix1 (Cy3)	Cy3	8532	MTS Site7 Batch2 rMix2 gMix1	Batch2	Mix1;Mix2	Site7	8573	8590		A-AGIL-6		16011868020451_A01	Site7_Batch2_rMix2_gMix1.txt
+177829	MTS Batch3	whole_organism	Rattus norvegicus	"Harlan, Frederick, MD"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch3	MTS Batch3 Mix2	6396	6396-mix2	MTS Site7 Batch3 Mix2 (Cy3)	Cy3	8532	MTS Site7 Batch3 rMix1 gMix2	Batch3	Mix1;Mix2	Site7	8573	8590		A-AGIL-6		16011868020450_A01	Site7_Batch3_rMix1_gMix2.txt
+177828	MTS Batch3	whole_organism	Rattus norvegicus	"Harlan, Frederick, MD"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch3	MTS Batch3 Mix1	6396	6396-mix1	MTS Site7 Batch3 Mix1 (Cy5)	Cy5	8534	MTS Site7 Batch3 rMix1 gMix2	Batch3	Mix1;Mix2	Site7	8573	8590		A-AGIL-6		16011868020450_A01	Site7_Batch3_rMix1_gMix2.txt
+177829	MTS Batch3	whole_organism	Rattus norvegicus	"Harlan, Frederick, MD"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch3	MTS Batch3 Mix2	6396	6396-mix2	MTS Site7 Batch3 Mix2 (Cy5)	Cy5	8534	MTS Site7 Batch3 rMix2 gMix1	Batch3	Mix1;Mix2	Site7	8573	8590		A-AGIL-6		16011868020449_A01	Site7_Batch3_rMix2_gMix1.txt
+177828	MTS Batch3	whole_organism	Rattus norvegicus	"Harlan, Frederick, MD"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch3	MTS Batch3 Mix1	6396	6396-mix1	MTS Site7 Batch3 Mix1 (Cy3)	Cy3	8532	MTS Site7 Batch3 rMix2 gMix1	Batch3	Mix1;Mix2	Site7	8573	8590		A-AGIL-6		16011868020449_A01	Site7_Batch3_rMix2_gMix1.txt
+177825	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch1	MTS Batch1 Mix2	6396	6396-mix2	MTS Site8 Batch1 Mix2 (Cy3)	Cy3	8535	MTS Site8 Batch1 rMix1 g Mix2	Batch1	Mix1;Mix2	Site8	8588	8590		A-AGIL-6		16011868016369_A01	Site8_Batch1_rMix1_gMix2.txt
+177824	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch1	MTS Batch1 Mix1	6396	6396-mix1	MTS Site8 Batch1 Mix1 (Cy5)	Cy5	8536	MTS Site8 Batch1 rMix1 g Mix2	Batch1	Mix1;Mix2	Site8	8588	8590		A-AGIL-6		16011868016369_A01	Site8_Batch1_rMix1_gMix2.txt
+177825	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch1	MTS Batch1 Mix2	6396	6396-mix2	MTS Site8 Batch1 Mix2 (Cy5)	Cy5	8536	MTS Site8 Batch1 rMix2 gMix1	Batch1	Mix1;Mix2	Site8	8588	8590		A-AGIL-6		16011868016362_A01	Site8_Batch1_rMix2_gMix1.txt
+177824	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch1	MTS Batch1 Mix1	6396	6396-mix1	MTS Site8 Batch1 Mix1 (Cy3)	Cy3	8535	MTS Site8 Batch1 rMix2 gMix1	Batch1	Mix1;Mix2	Site8	8588	8590		A-AGIL-6		16011868016362_A01	Site8_Batch1_rMix2_gMix1.txt
+177827	MTS Batch2	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Spraque-Dawley	6377	6380	Agilent	MTS Batch2	MTS Batch2 Mix2	6396	6396-mix2	MTS Site8 Batch2 Mix2 (Cy3)	Cy3	8535	MTS Site8 Batch2 rMix1 gMix2	Batch2	Mix1;Mix2	Site8	8588	8590		A-AGIL-6		16011868016360_A01	Site8_Batch2_rMix1_gMix2.txt
+177826	MTS Batch2	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch2	MTS Batch2 Mix1	6396	6396-mix1	MTS Site8 Batch2 Mix1 (Cy5)	Cy5	8536	MTS Site8 Batch2 rMix1 gMix2	Batch2	Mix1;Mix2	Site8	8588	8590		A-AGIL-6		16011868016360_A01	Site8_Batch2_rMix1_gMix2.txt
+177827	MTS Batch2	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Spraque-Dawley	6377	6380	Agilent	MTS Batch2	MTS Batch2 Mix2	6396	6396-mix2	MTS Site8 Batch2 Mix2 (Cy5)	Cy5	8536	MTS Site8 Batch2 rMix2 gMix1	Batch2	Mix1;Mix2	Site8	8588	8590		A-AGIL-6		16011868016352_A01	Site8_Batch2_rMix2_gMix1.txt
+177826	MTS Batch2	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch2	MTS Batch2 Mix1	6396	6396-mix1	MTS Site8 Batch2 Mix1 (Cy3)	Cy3	8535	MTS Site8 Batch2 rMix2 gMix1	Batch2	Mix1;Mix2	Site8	8588	8590		A-AGIL-6		16011868016352_A01	Site8_Batch2_rMix2_gMix1.txt
+177828	MTS Batch3	whole_organism	Rattus norvegicus	"Harlan, Frederick, MD"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch3	MTS Batch3 Mix1	6396	6396-mix1	MTS Site8 Batch3 Mix1 (Cy5)	Cy5	8536	MTS Site8 Batch3 rMix1 gMix2	Batch3	Mix1;Mix2	Site8	8588	8590		A-AGIL-6		16011868016683_A01	Site8_Batch3_rMix1_gMix2.txt
+177829	MTS Batch3	whole_organism	Rattus norvegicus	"Harlan, Frederick, MD"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch3	MTS Batch3 Mix2	6396	6396-mix2	MTS Site8 Batch3 Mix2 (Cy3)	Cy3	8535	MTS Site8 Batch3 rMix1 gMix2	Batch3	Mix1;Mix2	Site8	8588	8590		A-AGIL-6		16011868016683_A01	Site8_Batch3_rMix1_gMix2.txt
+177829	MTS Batch3	whole_organism	Rattus norvegicus	"Harlan, Frederick, MD"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch3	MTS Batch3 Mix2	6396	6396-mix2	MTS Site8 Batch3 Mix2 (Cy5)	Cy5	8536	MTS Site8 Batch3 rMix2 gMix1	Batch3	Mix1;Mix2	Site8	8588	8590		A-AGIL-6		16011868016334_A01	Site8_Batch3_rMix2_gMix1.txt
+177828	MTS Batch3	whole_organism	Rattus norvegicus	"Harlan, Frederick, MD"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch3	MTS Batch3 Mix1	6396	6396-mix1	MTS Site8 Batch3 Mix1 (Cy3)	Cy3	8535	MTS Site8 Batch3 rMix2 gMix1	Batch3	Mix1;Mix2	Site8	8588	8590		A-AGIL-6		16011868016334_A01	Site8_Batch3_rMix2_gMix1.txt
+177825	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch1	MTS Batch1 Mix2	6396	6396-mix2	MTS Site9 Batch1 Mix2 (Cy3)	Cy3	8537	MTS Site9 Batch1 rMix1 gMix2	Batch1	Mix1;Mix2	Site9	8589	8590		A-AGIL-6		16011868021269_A01	Site9_Batch1_rMix1_gMix2.txt
+177824	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch1	MTS Batch1 Mix1	6396	6396-mix1	MTS Site9 Batch1 Mix1 (Cy5)	Cy5	8538	MTS Site9 Batch1 rMix1 gMix2	Batch1	Mix1;Mix2	Site9	8589	8590		A-AGIL-6		16011868021269_A01	Site9_Batch1_rMix1_gMix2.txt
+177825	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch1	MTS Batch1 Mix2	6396	6396-mix2	MTS Site9 Batch1 Mix2 (Cy5)	Cy5	8538	MTS Site9 Batch1 rMix2 gMix1	Batch1	Mix1;Mix2	Site9	8589	8590		A-AGIL-6		16011868021268_A01	Site9_Batch1_rMix2_gMix1.txt
+177824	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch1	MTS Batch1 Mix1	6396	6396-mix1	MTS Site9 Batch1 Mix1 (Cy3)	Cy3	8537	MTS Site9 Batch1 rMix2 gMix1	Batch1	Mix1;Mix2	Site9	8589	8590		A-AGIL-6		16011868021268_A01	Site9_Batch1_rMix2_gMix1.txt
+177827	MTS Batch2	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Spraque-Dawley	6377	6380	Agilent	MTS Batch2	MTS Batch2 Mix2	6396	6396-mix2	MTS Site9 Batch2 Mix2 (Cy3)	Cy3	8537	MTS Site9 Batch2 rMix1 gMix2	Batch2	Mix1;Mix2	Site9	8589	8590		A-AGIL-6		16011868021050_A01	Site9_Batch2_rMix1_gMix2.txt
+177826	MTS Batch2	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch2	MTS Batch2 Mix1	6396	6396-mix1	MTS Site9 Batch2 Mix1 (Cy5)	Cy5	8538	MTS Site9 Batch2 rMix1 gMix2	Batch2	Mix1;Mix2	Site9	8589	8590		A-AGIL-6		16011868021050_A01	Site9_Batch2_rMix1_gMix2.txt
+177826	MTS Batch2	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch2	MTS Batch2 Mix1	6396	6396-mix1	MTS Site9 Batch2 Mix1 (Cy3)	Cy3	8537	MTS Site9 Batch2 rMix2 gMix1	Batch2	Mix1;Mix2	Site9	8589	8590		A-AGIL-6		16011868021270_A01	Site9_Batch2_rMix2_gMix1.txt
+177827	MTS Batch2	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Spraque-Dawley	6377	6380	Agilent	MTS Batch2	MTS Batch2 Mix2	6396	6396-mix2	MTS Site9 Batch2 Mix2 (Cy5)	Cy5	8538	MTS Site9 Batch2 rMix2 gMix1	Batch2	Mix1;Mix2	Site9	8589	8590		A-AGIL-6		16011868021270_A01	Site9_Batch2_rMix2_gMix1.txt
+177829	MTS Batch3	whole_organism	Rattus norvegicus	"Harlan, Frederick, MD"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch3	MTS Batch3 Mix2	6396	6396-mix2	MTS Site9 Batch3 Mix2 (Cy3)	Cy3	8537	MTS Site9 Batch3 rMix1 gMix2	Batch3	Mix1;Mix2	Site9	8589	8590		A-AGIL-6		16011868021048_A01	Site9_Batch3_rMix1_gMix2.txt
+177828	MTS Batch3	whole_organism	Rattus norvegicus	"Harlan, Frederick, MD"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch3	MTS Batch3 Mix1	6396	6396-mix1	MTS Site9 Batch3 Mix1 (Cy5)	Cy5	8538	MTS Site9 Batch3 rMix1 gMix2	Batch3	Mix1;Mix2	Site9	8589	8590		A-AGIL-6		16011868021048_A01	Site9_Batch3_rMix1_gMix2.txt
+177829	MTS Batch3	whole_organism	Rattus norvegicus	"Harlan, Frederick, MD"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch3	MTS Batch3 Mix2	6396	6396-mix2	MTS Site9 Batch3 Mix2 (Cy5)	Cy5	8538	MTS Site9 Batch3 rMix2 gMix1	Batch3	Mix1;Mix2	Site9	8589	8590		A-AGIL-6		16011868021049_A01	Site9_Batch3_rMix2_gMix1.txt
+177828	MTS Batch3	whole_organism	Rattus norvegicus	"Harlan, Frederick, MD"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch3	MTS Batch3 Mix1	6396	6396-mix1	MTS Site9 Batch3 Mix1 (Cy3)	Cy3	8537	MTS Site9 Batch3 rMix2 gMix1	Batch3	Mix1;Mix2	Site9	8589	8590		A-AGIL-6		16011868021049_A01	Site9_Batch3_rMix2_gMix1.txt
+177824	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch1	MTS Batch1 Mix1	6396	6396-mix1	MTS Site3 Batch1 Mix1 (Cy5)	Cy5	8538	MTS Site3 Batch1 rMix1 gMix2	Batch1	Mix1;Mix2	Site3	8589	8590		A-AGIL-6		16011868020210_A01	Site3_Batch1_rMix1_gMix2.txt
+177825	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch1	MTS Batch1 Mix2	6396	6396-mix2	MTS Site3 Batch1 Mix2 (Cy3)	Cy3	8537	MTS Site3 Batch1 rMix1 gMix2	Batch1	Mix1;Mix2	Site3	8589	8590		A-AGIL-6		16011868020210_A01	Site3_Batch1_rMix1_gMix2.txt
+177825	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch1	MTS Batch1 Mix2	6396	6396-mix2	MTS Site3 Batch1 Mix2 (Cy5)	Cy5	8538	MTS Site3 Batch1 rMix2 gMix1	Batch1	Mix1;Mix2	Site3	8589	8590		A-AGIL-6		16011868020211_A01	Site3_Batch1_rMix2_gMix1.txt
+177824	MTS Batch1	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch1	MTS Batch1 Mix1	6396	6396-mix1	MTS Site3 Batch1 Mix1 (Cy3)	Cy3	8537	MTS Site3 Batch1 rMix2 gMix1	Batch1	Mix1;Mix2	Site3	8589	8590		A-AGIL-6		16011868020211_A01	Site3_Batch1_rMix2_gMix1.txt
+177827	MTS Batch2	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Spraque-Dawley	6377	6380	Agilent	MTS Batch2	MTS Batch2 Mix2	6396	6396-mix2	MTS Site3 Batch2 Mix2 (Cy3)	Cy3	8537	MTS Site3 Batch2 rMix1 gMix2	Batch2	Mix1;Mix2	Site3	8589	8590		A-AGIL-6		16011868020208_A01	Site3_Batch2_rMix1_gMix2.txt
+177826	MTS Batch2	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch2	MTS Batch2 Mix1	6396	6396-mix1	MTS Site3 Batch2 Mix1 (Cy5)	Cy5	8538	MTS Site3 Batch2 rMix1 gMix2	Batch2	Mix1;Mix2	Site3	8589	8590		A-AGIL-6		16011868020208_A01	Site3_Batch2_rMix1_gMix2.txt
+177827	MTS Batch2	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Spraque-Dawley	6377	6380	Agilent	MTS Batch2	MTS Batch2 Mix2	6396	6396-mix2	MTS Site3 Batch2 Mix2 (Cy5)	Cy5	8538	MTS Site3 Batch2 rMix2 gMix1	Batch2	Mix1;Mix2	Site3	8589	8590		A-AGIL-6		16011868020209_A01	Site3_Batch2_rMix2_gMix1.txt
+177826	MTS Batch2	whole_organism	Rattus norvegicus	"Charles River, Raleigh, NC"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch2	MTS Batch2 Mix1	6396	6396-mix1	MTS Site3 Batch2 Mix1 (Cy3)	Cy3	8537	MTS Site3 Batch2 rMix2 gMix1	Batch2	Mix1;Mix2	Site3	8589	8590		A-AGIL-6		16011868020209_A01	Site3_Batch2_rMix2_gMix1.txt
+177829	MTS Batch3	whole_organism	Rattus norvegicus	"Harlan, Frederick, MD"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch3	MTS Batch3 Mix2	6396	6396-mix2	MTS Site3 Batch3 Mix2 (Cy3)	Cy3	8537	MTS Site3 Batch3 rMix1 gMix2	Batch3	Mix1;Mix2	Site3	8589	8590		A-AGIL-6		16011868020251_A01	Site3_Batch3_rMix1_gMix2.txt
+177828	MTS Batch3	whole_organism	Rattus norvegicus	"Harlan, Frederick, MD"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch3	MTS Batch3 Mix1	6396	6396-mix1	MTS Site3 Batch3 Mix1 (Cy5)	Cy5	8538	MTS Site3 Batch3 rMix1 gMix2	Batch3	Mix1;Mix2	Site3	8589	8590		A-AGIL-6		16011868020251_A01	Site3_Batch3_rMix1_gMix2.txt
+177828	MTS Batch3	whole_organism	Rattus norvegicus	"Harlan, Frederick, MD"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch3	MTS Batch3 Mix1	6396	6396-mix1	MTS Site3 Batch3 Mix1 (Cy3)	Cy3	8537	MTS Site3 Batch3 rMix2 gMix1	Batch3	Mix1;Mix2	Site3	8589	8590		A-AGIL-6		16011868020252_A01	Site3_Batch3_rMix2_gMix1.txt			
+177829	MTS Batch3	whole_organism	Rattus norvegicus	"Harlan, Frederick, MD"	6-7 weeks	male	Sprague-Dawley	6377	6380	Agilent	MTS Batch3	MTS Batch3 Mix2	6396	6396-mix2	MTS Site3 Batch3 Mix2 (Cy5)	Cy5	8538	MTS Site3 Batch3 rMix2 gMix1	Batch3	Mix1;Mix2	Site3	8589	8590		A-AGIL-6		16011868020252_A01	Site3_Batch3_rMix2_gMix1.txt			
+178297	MTS Batch6	whole_organism	Rattus norvegicus	Site3	6-7 weeks	male	Sprague-Dawley	6377	6380	Affymetrix	MTS Batch6	MTS Batch6 Mix1	6396	6396-mix1	MTS Site3 Batch6 Mix1	biotin	6486	MTS Site1 Batch6 Mix1	Batch6	Mix1	Site1	6491	6407		A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3002254	Site1_Batch6_Mix1.CEL	Site1_Batch6_Mix1.EXP	MTS Site1 Batch6 Mix1	
+178298	MTS Batch6	whole_organism	Rattus norvegicus	Site3	6-7 weeks	male	Sprague-Dawley	6377	6380	Affymetrix	MTS Batch6	MTS Batch6 Mix2	6396	6396-mix2	MTS Site1 Batch6 Mix2	biotin	6486	MTS Site1 Batch6 Mix2	Batch6	Mix2	Site1	6491	6407		A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3002254	Site1_Batch6_Mix2.CEL	Site1_Batch6_Mix2.EXP	MTS Site1 Batch6 Mix2	
+										Affymetrix								MTS Site1 Batch1 Mix1	Batch1	Mix1	Site1	6491	6407	6552	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3002245	Site1_Batch1_Mix1.CEL	Site1_Batch1_Mix1.EXP	Site1_Batch1_Mix1_MAS5	Site1_Batch1_Mix1_MAS5.CHP
+										Affymetrix								MTS Site1 Batch1 Mix2	Batch1	Mix2	Site1	6491	6407	6552	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3002245	Site1_Batch1_Mix2.CEL	Site1_Batch1_Mix2.EXP	Site1_Batch1_Mix2_MAS5	Site1_Batch1_Mix2_MAS5.CHP
+										Affymetrix								MTS Site1 Batch2 Mix1	Batch2	Mix1	Site1	6491	6407	6552	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3002245	Site1_Batch2_Mix1.CEL	Site1_Batch2_Mix1.EXP	Site1_Batch2_Mix1_MAS5	Site1_Batch2_Mix1_MAS5.CHP
+										Affymetrix								MTS Site1 Batch2 Mix2	Batch2	Mix2	Site1	6491	6407	6552	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3002245	Site1_Batch2_Mix2.CEL	Site1_Batch2_Mix2.EXP	Site1_Batch2_Mix2_MAS5	Site1_Batch2_Mix2_MAS5.CHP
+										Affymetrix								MTS Site1 Batch3 Mix1	Batch3	Mix1	Site1	6491	6407	6552	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3002245	Site1_Batch3_Mix1.CEL	Site1_Batch3_Mix1.EXP	Site1_Batch3_Mix1_MAS5	Site1_Batch3_Mix1_MAS5.CHP
+										Affymetrix								MTS Site1 Batch3 Mix2	Batch3	Mix2	Site1	6491	6407	6552	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3002245	Site1_Batch3_Mix2.CEL	Site1_Batch3_Mix2.EXP	Site1_Batch3_Mix2_MAS5	Site1_Batch3_Mix2_MAS5.CHP
+										Affymetrix								MTS Site2 Batch1 Mix1	Batch1	Mix1	Site2	6491	6407	6552	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3005334	Site2_Batch1_Mix1.CEL	Site2_Batch1_Mix1.EXP	Site2_Batch1_Mix1_MAS5	Site2_Batch1_Mix1_MAS5.CHP
+										Affymetrix								MTS Site2 Batch1 Mix2	Batch1	Mix2	Site2	6491	6407	6552	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3005334	Site2_Batch1_Mix2.CEL	Site2_Batch1_Mix2.EXP	Site2_Batch1_Mix2_MAS5	Site2_Batch1_Mix2_MAS5.CHP
+										Affymetrix								MTS Site2 Batch2 Mix1	Batch2	Mix1	Site2	6491	6407	6552	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3005334	Site2_Batch2_Mix1.CEL	Site2_Batch2_Mix1.EXP	Site2_Batch2_Mix1_MAS5	Site2_Batch2_Mix1_MAS5.CHP
+										Affymetrix								MTS Site2 Batch2 Mix2	Batch2	Mix2	Site2	6491	6407	6552	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3005334	Site2_Batch2_Mix2.CEL	Site2_Batch2_Mix2.EXP	Site2_Batch2_Mix2_MAS5	Site2_Batch2_Mix2_MAS5.CHP
+										Affymetrix								MTS Site2 Batch3 Mix1	Batch3	Mix1	Site2	6491	6407	6552	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3005334	Site2_Batch3_Mix1.CEL	Site2_Batch3_Mix1.EXP	Site2_Batch3_Mix1_MAS5	Site2_Batch3_Mix1_MAS5.CHP
+										Affymetrix								MTS Site2 Batch3 Mix2	Batch3	Mix2	Site2	6491	6407	6552	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3005334	Site2_Batch3_Mix2.CEL	Site2_Batch3_Mix2.EXP	Site2_Batch3_Mix2_MAS5	Site2_Batch3_Mix2_MAS5.CHP
+										Affymetrix								MTS Site3 Batch1 Mix1	Batch1	Mix1	Site3	6491	6409	6552	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3005331	Site3_Batch1_Mix1.CEL	Site3_Batch1_Mix1.EXP	Site3_Batch1_Mix1_MAS5	Site3_Batch1_Mix1_MAS5.CHP
+										Affymetrix								MTS Site3 Batch1 Mix2	Batch1	Mix2	Site3	6491	6409	6552	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3005331	Site3_Batch1_Mix2.CEL	Site3_Batch1_Mix2.EXP	Site3_Batch1_Mix2_MAS5	Site3_Batch1_Mix2_MAS5.CHP
+										Affymetrix								MTS Site3 Batch2 Mix1	Batch2	Mix1	Site3	6491	6409	6552	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3005331	Site3_Batch2_Mix1.CEL	Site3_Batch2_Mix1.EXP	Site3_Batch2_Mix1_MAS5	Site3_Batch2_Mix1_MAS5.CHP
+										Affymetrix								MTS Site3 Batch2 Mix2	Batch2	Mix2	Site3	6491	6409	6552	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3005331	Site3_Batch2_Mix2.CEL	Site3_Batch2_Mix2.EXP	Site3_Batch2_Mix2_MAS5	Site3_Batch2_Mix2_MAS5.CHP
+										Affymetrix								MTS Site3 Batch3 Mix1	Batch3	Mix1	Site3	6491	6409	6552	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3005331	Site3_Batch3_Mix1.CEL	Site3_Batch3_Mix1.EXP	Site3_Batch3_Mix1_MAS5	Site3_Batch3_Mix1_MAS5.CHP
+										Affymetrix								MTS Site3 Batch3 Mix2	Batch3	Mix2	Site3	6491	6409	6552	A-AFFY-25	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF	3005331	Site3_Batch3_Mix2.CEL	Site3_Batch3_Mix2.EXP	Site3_Batch3_Mix2_MAS5	Site3_Batch3_Mix2_MAS5.CHP
diff --git a/examples/real/E-TABM-163.png b/examples/real/E-TABM-163.png
new file mode 100644
index 0000000..db71e97
Binary files /dev/null and b/examples/real/E-TABM-163.png differ
diff --git a/examples/real/E-TABM-163.txt b/examples/real/E-TABM-163.txt
new file mode 100755
index 0000000..a89f805
--- /dev/null
+++ b/examples/real/E-TABM-163.txt
@@ -0,0 +1,55 @@
+Experiment section	
+domain	ebi.ac.uk
+accession	E-TABM-163
+quality_control	biological_replicate
+experiment_design_type	"time_series_design, development_or_differentiation_design,co_expression_design"
+name	Identification of cyclic genes of the mouse segmentation clock
+description	"A microarray time series was generated to identify cyclic genes of the segmentation clock in the mouse. The right posterior half presomitic mesoderms (PSM) from 17 mouse embryos were dissected while the contralateral side of the embryo containing the left PSM was immediately fixed to be analyzed by in situ hybridization using a Lfng probe to order the samples along the segmentation clock oscillation cycle. Probes were produced from RNA extracted from the 17 dissected posteri [...]
+release_date	2006-10-30
+submission_date	2006-10-30
+submitter	Mary-Lee Dequ�ant
+organization	Stowers Institute For Medical Research
+publication_title	A complex oscillating network of signaling genes underlies the mouse segmentation clock
+authors	"Mary-Lee Dequ�ant, Earl Glynn, Karin Gaudenz, Matthias Wahl, Jie Chen, Arcady Mushegian, Olivier Pourqui�"
+journal	Science
+volume	
+issue	
+pages																										
+year																										
+
+# Protocol text may be formatted using standard HTML tags																										
+Protocol section																										
+accession	text	name																								
+P-MLD6-1	"CD-1 mouse embryos (9.0 days post-coitus [dpc]) were dissected in cold Tyrode's solution prepared in RNAse-free conditions. Mouse embryos from each litter were dissected one by one, while the rest of the embryos were kept on ice. No more than three embryos per litter were dissected in order to limit the time between removal of the uterus and embryo dissection to reduce the risk of RNA degradation. The time spent for dissection per embryo was limited to 15-20 min. After tail bud [...]
+P-MLD6-2	"Total RNA was isolated from each dissected posterior half PSM (containing around 5,000 cells) using Trizol."	RNA extraction																								
+P-MLD6-3	"Amplification of the samples was performed as described in GeneChip Eukaryotic small sample target labeling Assay version II. Total RNA from each sample was used to generate first-strand cDNA by using a T7-linked oligo (dT) primer. After second-strand synthesis, in vitro transcription was performed with unlabelled ribonucleotides yielding 1.2 to 2.4 ug of cRNA. Using 400ng of cRNA from the first round of amplification, a second round of cDNA synthesis was then performed followe [...]
+P-MLD6-4	Arrays were hybridized according to the manufacturer's instructions. Each chip was hybridized to 13 ug fragmented cRNA.	Hybridization																								
+P-MLD6-5	Arrays were scanned with a GeneArray scanner (Agilent G2500A) at the Microarray Core Facility of the Stowers Institute for Medical Research	Scanning																								
+P-MLD6-6	The array readout was processed using Affymetrix Microarray Suite (MAS) 5.0	Image_analysis																								
+P-MLD6-7	Scaling normalisation (target signal 150) using Affymetrix MAS 5.0 was done for the .CHP files submitted to this database	Normalization																								
+
+Hybridization section																										
+File[raw]	File[normalized]	File[exp]	File[cdf]	Array[accession]	Array[serial]	BioSource	SampleMaterial	ExtractMaterial	LabeledExtractMaterial	BioSourceMaterial	BioMaterialCharacteristics[Individual]	BioMaterialCharacteristics[Organism]	BioMaterialCharacteristics[StrainOrLine]	BioMaterialCharacteristics[Age(days)]	BioMaterialCharacteristics[DevelopmentalStage]	BioMaterialCharacteristics[OrganismPart]	FactorValue[OrganismPart]	FactorValue[Individual]	FactorValue[time(m)]	Protocol[treatment [...]
+Emb1a-5D2_MGU74Av2.CEL	Emb1a-5D2_MGU74Av2.CHP	Emb1a-5D2_MGU74Av2.EXP	MG_U74Av2.CDF	A-AFFY-6	3000681	Charles River # 022	organism_part	total_RNA	synthetic_RNA	organism_part	Embryo 1	Mus musculus	CD-1	9.5	embryo	right or left presomitic mesoderm (posterior half)(a)	right or left presomitic mesoderm (posterior half)(a)	Embryo 1	n/a	P-MLD6-1	P-MLD6-2	P-MLD6-3	P-MLD6-4	P-MLD6-5	P-MLD6-6	P-MLD6-7
+Emb1b-5G1_MGU74Av2.CEL	Emb1b-5G1_MGU74Av2.CHP	Emb1b-5G1_MGU74Av2.EXP	MG_U74Av2.CDF	A-AFFY-6	3000681	Charles River # 023	organism_part	total_RNA	synthetic_RNA	organism_part	Embryo 1	Mus musculus	CD-1	9.5	embryo	right or left presomitic mesoderm (posterior half)(b)	right or left presomitic mesoderm (posterior half)(b)	Embryo 1	n/a	P-MLD6-1	P-MLD6-2	P-MLD6-3	P-MLD6-4	P-MLD6-5	P-MLD6-6	P-MLD6-7
+Emb2a-3_MGU74Av2.CEL	Emb2a-3_MGU74Av2.CHP	Emb2a-3_MGU74Av2.EXP	MG_U74Av2.CDF	A-AFFY-6	3000681	Charles River # 024	organism_part	total_RNA	synthetic_RNA	organism_part	Embryo 2	Mus musculus	CD-1	9.5	embryo	right or left presomitic mesoderm (posterior half)(a)	right or left presomitic mesoderm (posterior half)(a)	Embryo 2	n/a	P-MLD6-1	P-MLD6-2	P-MLD6-3	P-MLD6-4	P-MLD6-5	P-MLD6-6	P-MLD6-7
+Emb2b-4_MGU74Av2.CEL	Emb2b-4_MGU74Av2.CHP	Emb2b-4_MGU74Av2.EXP	MG_U74Av2.CDF	A-AFFY-6	3000681	Charles River # 025	organism_part	total_RNA	synthetic_RNA	organism_part	Embryo 2	Mus musculus	CD-1	9.5	embryo	right or left presomitic mesoderm (posterior half)(b)	right or left presomitic mesoderm (posterior half)(b)	Embryo 2	n/a	P-MLD6-1	P-MLD6-2	P-MLD6-3	P-MLD6-4	P-MLD6-5	P-MLD6-6	P-MLD6-7
+Emb3a-D_MGU74Av2.CEL	Emb3a-D_MGU74Av2.CHP	Emb3a-D_MGU74Av2.EXP	MG_U74Av2.CDF	A-AFFY-6	3000681	Charles River # 026	organism_part	total_RNA	synthetic_RNA	organism_part	Embryo 3	Mus musculus	CD-1	9.5	embryo	right or left presomitic mesoderm (posterior half)(a)	right or left presomitic mesoderm (posterior half)(a)	Embryo 3	n/a	P-MLD6-1	P-MLD6-2	P-MLD6-3	P-MLD6-4	P-MLD6-5	P-MLD6-6	P-MLD6-7
+Emb3b-E_MGU74Av2.CEL	Emb3b-E_MGU74Av2.CHP	Emb3b-E_MGU74Av2.EXP	MG_U74Av2.CDF	A-AFFY-6	3000681	Charles River # 027	organism_part	total_RNA	synthetic_RNA	organism_part	Embryo 3	Mus musculus	CD-1	9.5	embryo	right or left presomitic mesoderm (posterior half)(b)	right or left presomitic mesoderm (posterior half)(b)	Embryo 3	n/a	P-MLD6-1	P-MLD6-2	P-MLD6-3	P-MLD6-4	P-MLD6-5	P-MLD6-6	P-MLD6-7
+Emb01-6.3_MOE430A.CEL	Emb01-6.3_MOE430A.CHP	Emb01-6.3_MOE430A.EXP	MOE430A.CDF	A-AFFY-23	3005274	Charles River # 028	organism_part	total_RNA	synthetic_RNA	organism_part	Embryo 01	Mus musculus	CD-1	9.5	embryo	right presomitic mesoderm (posterior half)	right presomitic mesoderm (posterior half)	Embryo 01	0	P-MLD6-1	P-MLD6-2	P-MLD6-3	P-MLD6-4	P-MLD6-5	P-MLD6-6	P-MLD6-7
+Emb02-10.3_MOE430A.CEL	Emb02-10.3_MOE430A.CHP	Emb02-10.3_MOE430A.EXP	MOE430A.CDF	A-AFFY-23	3004217	Charles River # 029	organism_part	total_RNA	synthetic_RNA	organism_part	Embryo 02	Mus musculus	CD-1	9.5	embryo	right presomitic mesoderm (posterior half)	right presomitic mesoderm (posterior half)	Embryo 02	7	P-MLD6-1	P-MLD6-2	P-MLD6-3	P-MLD6-4	P-MLD6-5	P-MLD6-6	P-MLD6-7
+Emb03-7.3_MOE430A.CEL	Emb03-7.3_MOE430A.CHP	Emb03-7.3_MOE430A.EXP	MOE430A.CDF	A-AFFY-23	3004217	Charles River # 030	organism_part	total_RNA	synthetic_RNA	organism_part	Embryo 03	Mus musculus	CD-1	9.5	embryo	right presomitic mesoderm (posterior half)	right presomitic mesoderm (posterior half)	Embryo 03	14	P-MLD6-1	P-MLD6-2	P-MLD6-3	P-MLD6-4	P-MLD6-5	P-MLD6-6	P-MLD6-7
+Emb04-11.3_MOE430A.CEL	Emb04-11.3_MOE430A.CHP	Emb04-11.3_MOE430A.EXP	MOE430A.CDF	A-AFFY-23	3004217	Charles River # 031	organism_part	total_RNA	synthetic_RNA	organism_part	Embryo 04	Mus musculus	CD-1	9.5	embryo	right presomitic mesoderm (posterior half)	right presomitic mesoderm (posterior half)	Embryo 04	21	P-MLD6-1	P-MLD6-2	P-MLD6-3	P-MLD6-4	P-MLD6-5	P-MLD6-6	P-MLD6-7
+Emb05-12.1_MOE430A.CEL         	Emb05-12.1_MOE430A.CHP         	Emb05-12.1_MOE430A.EXP         	MOE430A.CDF	A-AFFY-23	3004217	Charles River # 032	organism_part	total_RNA	synthetic_RNA	organism_part	Embryo 05       	Mus musculus	CD-1	9.5	embryo	right presomitic mesoderm (posterior half)	right presomitic mesoderm (posterior half)	Embryo 05       	28	P-MLD6-1	P-MLD6-2	P-MLD6-3	P-MLD6-4	P-MLD6-5	P-MLD6-6	P-MLD6-7
+Emb06-19.1_MOE430A.CEL	Emb06-19.1_MOE430A.CHP	Emb06-19.1_MOE430A.EXP	MOE430A.CDF	A-AFFY-23	3005274	Charles River # 033	organism_part	total_RNA	synthetic_RNA	organism_part	Embryo 06	Mus musculus	CD-1	9.5	embryo	right presomitic mesoderm (posterior half)	right presomitic mesoderm (posterior half)	Embryo 06	35	P-MLD6-1	P-MLD6-2	P-MLD6-3	P-MLD6-4	P-MLD6-5	P-MLD6-6	P-MLD6-7
+Emb07-20.1_MOE430A.CEL	Emb07-20.1_MOE430A.CHP	Emb07-20.1_MOE430A.EXP	MOE430A.CDF	A-AFFY-23	3005274	Charles River # 034	organism_part	total_RNA	synthetic_RNA	organism_part	Embryo 07	Mus musculus	CD-1	9.5	embryo	right presomitic mesoderm (posterior half)	right presomitic mesoderm (posterior half)	Embryo 07	42	P-MLD6-1	P-MLD6-2	P-MLD6-3	P-MLD6-4	P-MLD6-5	P-MLD6-6	P-MLD6-7
+Emb08-4.1_MOE430A.CEL	Emb08-4.1_MOE430A.CHP	Emb08-4.1_MOE430A.EXP	MOE430A.CDF	A-AFFY-23	3004217	Charles River # 035	organism_part	total_RNA	synthetic_RNA	organism_part	Embryo 08	Mus musculus	CD-1	9.5	embryo	right presomitic mesoderm (posterior half)	right presomitic mesoderm (posterior half)	Embryo 08	49	P-MLD6-1	P-MLD6-2	P-MLD6-3	P-MLD6-4	P-MLD6-5	P-MLD6-6	P-MLD6-7
+Emb09-22.1_MOE430A.CEL	Emb09-22.1_MOE430A.CHP	Emb09-22.1_MOE430A.EXP	MOE430A.CDF	A-AFFY-23	3005274	Charles River # 036	organism_part	total_RNA	synthetic_RNA	organism_part	Embryo 09	Mus musculus	CD-1	9.5	embryo	right presomitic mesoderm (posterior half)	right presomitic mesoderm (posterior half)	Embryo 09	56	P-MLD6-1	P-MLD6-2	P-MLD6-3	P-MLD6-4	P-MLD6-5	P-MLD6-6	P-MLD6-7
+Emb10-9.1_MOE430A.CEL	Emb10-9.1_MOE430A.CHP	Emb10-9.1_MOE430A.EXP	MOE430A.CDF	A-AFFY-23	3004217	Charles River # 037	organism_part	total_RNA	synthetic_RNA	organism_part	Embryo 10	Mus musculus	CD-1	9.5	embryo	right presomitic mesoderm (posterior half)	right presomitic mesoderm (posterior half)	Embryo 10	63	P-MLD6-1	P-MLD6-2	P-MLD6-3	P-MLD6-4	P-MLD6-5	P-MLD6-6	P-MLD6-7
+Emb11-23.2_MOE430A.CEL	Emb11-23.2_MOE430A.CHP	Emb11-23.2_MOE430A.EXP	MOE430A.CDF	A-AFFY-23	3005274	Charles River # 038	organism_part	total_RNA	synthetic_RNA	organism_part	Embryo 11	Mus musculus	CD-1	9.5	embryo	right presomitic mesoderm (posterior half)	right presomitic mesoderm (posterior half)	Embryo 11	70	P-MLD6-1	P-MLD6-2	P-MLD6-3	P-MLD6-4	P-MLD6-5	P-MLD6-6	P-MLD6-7
+Emb12-13.2_MOE430A.CEL	Emb12-13.2_MOE430A.CHP	Emb12-13.2_MOE430A.EXP	MOE430A.CDF	A-AFFY-23	3004217	Charles River # 039	organism_part	total_RNA	synthetic_RNA	organism_part	Embryo 12	Mus musculus	CD-1	9.5	embryo	right presomitic mesoderm (posterior half)	right presomitic mesoderm (posterior half)	Embryo 12	77	P-MLD6-1	P-MLD6-2	P-MLD6-3	P-MLD6-4	P-MLD6-5	P-MLD6-6	P-MLD6-7
+Emb13-14.2_MOE430A.CEL	Emb13-14.2_MOE430A.CHP	Emb13-14.2_MOE430A.EXP	MOE430A.CDF	A-AFFY-23	3004217	Charles River # 040	organism_part	total_RNA	synthetic_RNA	organism_part	Embryo 13	Mus musculus	CD-1	9.5	embryo	right presomitic mesoderm (posterior half)	right presomitic mesoderm (posterior half)	Embryo 13	84	P-MLD6-1	P-MLD6-2	P-MLD6-3	P-MLD6-4	P-MLD6-5	P-MLD6-6	P-MLD6-7
+Emb14-17.2_MOE430A.CEL	Emb14-17.2_MOE430A.CHP	Emb14-17.2_MOE430A.EXP	MOE430A.CDF	A-AFFY-23	3005274	Charles River # 041	organism_part	total_RNA	synthetic_RNA	organism_part	Embryo 14	Mus musculus	CD-1	9.5	embryo	right presomitic mesoderm (posterior half)	right presomitic mesoderm (posterior half)	Embryo 14	91	P-MLD6-1	P-MLD6-2	P-MLD6-3	P-MLD6-4	P-MLD6-5	P-MLD6-6	P-MLD6-7
+Emb15-16.2_MOE430A.CEL	Emb15-16.2_MOE430A.CHP	Emb15-16.2_MOE430A.EXP	MOE430A.CDF	A-AFFY-23	3005274	Charles River # 042	organism_part	total_RNA	synthetic_RNA	organism_part	Embryo 15	Mus musculus	CD-1	9.5	embryo	right presomitic mesoderm (posterior half)	right presomitic mesoderm (posterior half)	Embryo 15	98	P-MLD6-1	P-MLD6-2	P-MLD6-3	P-MLD6-4	P-MLD6-5	P-MLD6-6	P-MLD6-7
+Emb16-15.2_MOE430A.CEL	Emb16-15.2_MOE430A.CHP	Emb16-15.2_MOE430A.EXP	MOE430A.CDF	A-AFFY-23	3004217	Charles River # 043	organism_part	total_RNA	synthetic_RNA	organism_part	Embryo 16	Mus musculus	CD-1	9.5	embryo	right presomitic mesoderm (posterior half)	right presomitic mesoderm (posterior half)	Embryo 16	105	P-MLD6-1	P-MLD6-2	P-MLD6-3	P-MLD6-4	P-MLD6-5	P-MLD6-6	P-MLD6-7
+Emb17-18.2_MOE430A.CEL	Emb17-18.2_MOE430A.CHP	Emb17-18.2_MOE430A.EXP	MOE430A.CDF	A-AFFY-23	3005274	Charles River # 044	organism_part	total_RNA	synthetic_RNA	organism_part	Embryo 17	Mus musculus	CD-1	9.5	embryo	right presomitic mesoderm (posterior half)	right presomitic mesoderm (posterior half)	Embryo 17	112	P-MLD6-1	P-MLD6-2	P-MLD6-3	P-MLD6-4	P-MLD6-5	P-MLD6-6	P-MLD6-7
diff --git a/examples/real/E-TABM-18.png b/examples/real/E-TABM-18.png
new file mode 100644
index 0000000..dea1b84
Binary files /dev/null and b/examples/real/E-TABM-18.png differ
diff --git a/examples/real/E-TABM-18.txt b/examples/real/E-TABM-18.txt
new file mode 100755
index 0000000..126131f
--- /dev/null
+++ b/examples/real/E-TABM-18.txt
@@ -0,0 +1,82 @@
+Experiment section   	
+domain	ebi.ac.uk
+accession	E-TABM-18
+experiment_design_type	strain_or_line_design
+name	Ecotype
+description	"Seedlings of 35 different Arabidopsis thaliana ecotypes were compared. Triplicates were performed of 10 ecotpyes, single arrays of 25 ecotypes. "
+release_date	2005-06-15
+submission_date	
+submitter	Janne Lempe
+organization	"Max Planck Institute for Developmental Biology, Department of Molecular Biology, Detlef Weigel"
+publication_title	Diversity of flowering responses in wild Arabidopsis thaliana strains
+authors	Janne Lempe;Sureshkumar Balasubramanian;Sridevi Sureshkumar;Anandita Singh;Markus Schmid;Detlef Weigel
+journal	PLOS Genetics
+volume	1
+issue	
+pages	
+year	2005																	
+
+Protocol section																		
+accession	type	text	name	parameters														
+P-TABM-Janne5	growth	"Plants were grown on soil for 4 days under continuous light  at 23�C and  65% relative humidity. Most replicates were grown at the same time, exceptions to this are indicated by the 'replicate timepoint' parameter."	strain comparison	replicate timepoint														
+P-TABM-Janne2	extraction	pdf of the protocol (Probe preparation Development) can be downloaded at http://www.weigelworld.org/resources/microarray/AtGenExpress/; also at TAIR http://www.arabidopsis.org Protocol:501712557																
+P-TABM-Janne6	pooling	above soil plant material of 15 seedlings was pooled per replicate																
+P-TABM-Janne4	labeling	pdf of the protocol (Probe preparation Development) can be downloaded at http://www.weigelworld.org/resources/microarray/AtGenExpress/; also at TAIR http://www.arabidopsis.org Protocol:501712557																
+
+Hybridization section																		
+File[raw]	File[exp]	File[normalized]	Array[accession]	Extract	BioSource	BioSourceMaterial	BioMaterialCharacteristics[Ecotype]	FactorValue[Ecotype]	BioMaterialCharacteristics[nasc]	BioMaterialCharacteristics[Organism]	BioMaterialCharacteristics[OrganismPart]	Protocol[grow]	Parameter[replicate timepoint]	Protocol[extraction]	Protocol[pool]	Protocol[labeling]	Dye	File[cdf]
+ATGE_111_A.CEL	ATGE_111_A.EXP	ATGE_111_A.CHP	A-AFFY-2	AtGE_111_A	AtGE_111_A	organism_part	Bay-0	Bay-0	CS954	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_111_B.CEL	ATGE_111_B.EXP	ATGE_111_B.CHP	A-AFFY-2	AtGE_111_B	AtGE_111_B	organism_part	Bay-0	Bay-0	CS954	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_111_C.CEL	ATGE_111_C.EXP	ATGE_111_C.CHP	A-AFFY-2	AtGE_111_C	AtGE_111_C	organism_part	Bay-0	Bay-0	CS954	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_112_A.CEL	ATGE_112_A.EXP	ATGE_112_A.CHP	A-AFFY-2	AtGE_112_A	AtGE_112_A	organism_part	C24	C24	CS906	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_112_C.CEL	ATGE_112_C.EXP	ATGE_112_C.CHP	A-AFFY-2	AtGE_112_C	AtGE_112_C	organism_part	C24	C24	CS906	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_112_D.CEL	ATGE_112_D.EXP	ATGE_112_D.CHP	A-AFFY-2	AtGE_112_D	AtGE_112_D	organism_part	C24	C24	CS906	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_113_A.CEL	ATGE_113_A.EXP	ATGE_113_A.CHP	A-AFFY-2	AtGE_113_A	AtGE_113_A	organism_part	Col-0	Col-0	N1093	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_113_C.CEL	ATGE_113_C.EXP	ATGE_113_C.CHP	A-AFFY-2	AtGE_113_C	AtGE_113_C	organism_part	Col-0	Col-0	N1093	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_113_D.CEL	ATGE_113_D.EXP	ATGE_113_D.CHP	A-AFFY-2	AtGE_113_D	AtGE_113_D	organism_part	Col-0	Col-0	N1093	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	B	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_114_A.CEL	ATGE_114_A.EXP	ATGE_114_A.CHP	A-AFFY-2	AtGE_114_A	AtGE_114_A	organism_part	Cvi	Cvi	CS8580	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_114_B.CEL	ATGE_114_B.EXP	ATGE_114_B.CHP	A-AFFY-2	AtGE_114_B	AtGE_114_B	organism_part	Cvi	Cvi	CS8580	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_114_C.CEL	ATGE_114_C.EXP	ATGE_114_C.CHP	A-AFFY-2	AtGE_114_C	AtGE_114_C	organism_part	Cvi	Cvi	CS8580	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_115_A.CEL	ATGE_115_A.EXP	ATGE_115_A.CHP	A-AFFY-2	AtGE_115_A	AtGE_115_A	organism_part	Est	Est	CS6173	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_115_B.CEL	ATGE_115_B.EXP	ATGE_115_B.CHP	A-AFFY-2	AtGE_115_B	AtGE_115_B	organism_part	Est	Est	CS6173	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_115_D.CEL	ATGE_115_D.EXP	ATGE_115_D.CHP	A-AFFY-2	AtGE_115_D	AtGE_115_D	organism_part	Est	Est	CS6173	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	B	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_116_A.CEL	ATGE_116_A.EXP	ATGE_116_A.CHP	A-AFFY-2	AtGE_116_A	AtGE_116_A	organism_part	Kin-0	Kin-0	CS6755	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_116_B.CEL	ATGE_116_B.EXP	ATGE_116_B.CHP	A-AFFY-2	AtGE_116_B	AtGE_116_B	organism_part	Kin-0	Kin-0	CS6755	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_116_C.CEL	ATGE_116_C.EXP	ATGE_116_C.CHP	A-AFFY-2	AtGE_116_C	AtGE_116_C	organism_part	Kin-0	Kin-0	CS6755	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_117_B.CEL	ATGE_117_B.EXP	ATGE_117_B.CHP	A-AFFY-2	AtGE_117_B	AtGE_117_B	organism_part	Ler	Ler	CS8581	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_117_C.CEL	ATGE_117_C.EXP	ATGE_117_C.CHP	A-AFFY-2	AtGE_117_C	AtGE_117_C	organism_part	Ler	Ler	CS8581	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_117_D.CEL	ATGE_117_D.EXP	ATGE_117_D.CHP	A-AFFY-2	AtGE_117_D	AtGE_117_D	organism_part	Ler	Ler	CS8581	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	B	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_118_A.CEL	ATGE_118_A.EXP	ATGE_118_A.CHP	A-AFFY-2	AtGE_118_A	AtGE_118_A	organism_part	Nd-1	Nd-1	CS1636	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_118_B.CEL	ATGE_118_B.EXP	ATGE_118_B.CHP	A-AFFY-2	AtGE_118_B	AtGE_118_B	organism_part	Nd-1	Nd-1	CS1636	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_118_C.CEL	ATGE_118_C.EXP	ATGE_118_C.CHP	A-AFFY-2	AtGE_118_C	AtGE_118_C	organism_part	Nd-1	Nd-1	CS1636	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_119_A.CEL	ATGE_119_A.EXP	ATGE_119_A.CHP	A-AFFY-2	AtGE_119_A	AtGE_119_A	organism_part	Shahdara	Shahdara	CS929	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_119_C.CEL	ATGE_119_C.EXP	ATGE_119_C.CHP	A-AFFY-2	AtGE_119_C	AtGE_119_C	organism_part	Shahdara	Shahdara	CS929	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_119_D.CEL	ATGE_119_D.EXP	ATGE_119_D.CHP	A-AFFY-2	AtGE_119_D	AtGE_119_D	organism_part	Shahdara	Shahdara	CS929	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_120_A.CEL	ATGE_120_A.EXP	ATGE_120_A.CHP	A-AFFY-2	AtGE_120_A	AtGE_120_A	organism_part	Van-0	Van-0	CS6884	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_120_B.CEL	ATGE_120_B.EXP	ATGE_120_B.CHP	A-AFFY-2	AtGE_120_B	AtGE_120_B	organism_part	Van-0	Van-0	CS6884	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_120_C.CEL	ATGE_120_C.EXP	ATGE_120_C.CHP	A-AFFY-2	AtGE_120_C	AtGE_120_C	organism_part	Van-0	Van-0	CS6884	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5	A	P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_121_A.CEL	ATGE_121_A.EXP	ATGE_121_A.CHP	A-AFFY-2	AtGE_121_A	AtGE_121_A	organism_part	Ak-1	Ak-1	N939 	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5		P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_124_A.CEL	ATGE_124_A.EXP	ATGE_124_A.CHP	A-AFFY-2	AtGE_124_A	AtGE_124_A	organism_part	Bla-5	Bla-5	N6620 	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5		P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_125_A.CEL	ATGE_125_A.EXP	ATGE_125_A.CHP	A-AFFY-2	AtGE_125_A	AtGE_125_A	organism_part	Can-0	Can-0	N1065 	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5		P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_126_A.CEL	ATGE_126_A.EXP	ATGE_126_A.CHP	A-AFFY-2	AtGE_126_A	AtGE_126_A	organism_part	Cen-0	Cen-0	N1067 	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5		P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_127_A.CEL	ATGE_127_A.EXP	ATGE_127_A.CHP	A-AFFY-2	AtGE_127_A	AtGE_127_A	organism_part	CIBC10	CIBC10	N22229 	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5		P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_128_A.CEL	ATGE_128_A.EXP	ATGE_128_A.CHP	A-AFFY-2	AtGE_128_A	AtGE_128_A	organism_part	Dra-1	Dra-1	N1119 	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5		P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_129_A.CEL	ATGE_129_A.EXP	ATGE_129_A.CHP	A-AFFY-2	AtGE_129_A	AtGE_129_A	organism_part	Enkheim-T	Enkheim-T	N6176 	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5		P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_130_A.CEL	ATGE_130_A.EXP	ATGE_130_A.CHP	A-AFFY-2	AtGE_130_A	AtGE_130_A	organism_part	Er-0	Er-0	N1143 	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5		P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_131_A.CEL	ATGE_131_A.EXP	ATGE_131_A.CHP	A-AFFY-2	AtGE_131_A	AtGE_131_A	organism_part	Fr-2	Fr-2	N1169 	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5		P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_132_A.CEL	ATGE_132_A.EXP	ATGE_132_A.CHP	A-AFFY-2	AtGE_132_A	AtGE_132_A	organism_part	GOT1	GOT1	N22277 	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5		P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_133_A.CEL	ATGE_133_A.EXP	ATGE_133_A.CHP	A-AFFY-2	AtGE_133_A	AtGE_133_A	organism_part	HR-5	HR-5	N22205 	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5		P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_134_A.CEL	ATGE_134_A.EXP	ATGE_134_A.CHP	A-AFFY-2	AtGE_134_A	AtGE_134_A	organism_part	Is-0	Is-0	N1241 	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5		P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_136_A.CEL	ATGE_136_A.EXP	ATGE_136_A.CHP	A-AFFY-2	AtGE_136_A	AtGE_136_A	organism_part	Li-2:1	Li-2:1	N1315 	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5		P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_137_A.CEL	ATGE_137_A.EXP	ATGE_137_A.CHP	A-AFFY-2	AtGE_137_A	AtGE_137_A	organism_part	M7323S	M7323S	N6184 	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5		P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_138_A.CEL	ATGE_138_A.EXP	ATGE_138_A.CHP	A-AFFY-2	AtGE_138_A	AtGE_138_A	organism_part	Ms-0	Ms-0	N1377 	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5		P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_139_A.CEL	ATGE_139_A.EXP	ATGE_139_A.CHP	A-AFFY-2	AtGE_139_A	AtGE_139_A	organism_part	Nc-1	Nc-1	N1389 	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5		P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_140_A.CEL	ATGE_140_A.EXP	ATGE_140_A.CHP	A-AFFY-2	AtGE_140_A	AtGE_140_A	organism_part	NFE1	NFE1	N22163 	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5		P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_141_A.CEL	ATGE_141_A.EXP	ATGE_141_A.CHP	A-AFFY-2	AtGE_141_A	AtGE_141_A	organism_part	Nok-1	Nok-1	N1401 	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5		P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_142_A.CEL	ATGE_142_A.EXP	ATGE_142_A.CHP	A-AFFY-2	AtGE_142_A	AtGE_142_A	organism_part	Nw-1	Nw-1	N1411 	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5		P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_144_A.CEL	ATGE_144_A.EXP	ATGE_144_A.CHP	A-AFFY-2	AtGE_144_A	AtGE_144_A	organism_part	Old-2	Old-2	N1429 	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5		P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_145_A.CEL	ATGE_145_A.EXP	ATGE_145_A.CHP	A-AFFY-2	AtGE_145_A	AtGE_145_A	organism_part	Ove-0	Ove-0	N1435 	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5		P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_146_A.CEL	ATGE_146_A.EXP	ATGE_146_A.CHP	A-AFFY-2	AtGE_146_A	AtGE_146_A	organism_part	Se-0	Se-0	N1503 	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5		P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_147_A.CEL	ATGE_147_A.EXP	ATGE_147_A.CHP	A-AFFY-2	AtGE_147_A	AtGE_147_A	organism_part	Sf-2e	Sf-2e	N1675 	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5		P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_148_A.CEL	ATGE_148_A.EXP	ATGE_148_A.CHP	A-AFFY-2	AtGE_148_A	AtGE_148_A	organism_part	Ta-0	Ta-0	N1549 	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5		P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_149_A.CEL	ATGE_149_A.EXP	ATGE_149_A.CHP	A-AFFY-2	AtGE_149_A	AtGE_149_A	organism_part	Uk-3	Uk-3	N1577 	Arabidopsis thaliana	"seedling, aerial parts"	P-TABM-Janne5		P-TABM-Janne2	P-TABM-Janne6	P-TABM-Janne4	biotin	/net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
diff --git a/examples/real/E-TABM-22.png b/examples/real/E-TABM-22.png
new file mode 100644
index 0000000..5e3837f
Binary files /dev/null and b/examples/real/E-TABM-22.png differ
diff --git a/examples/real/E-TABM-22.txt b/examples/real/E-TABM-22.txt
new file mode 100644
index 0000000..11e925f
--- /dev/null
+++ b/examples/real/E-TABM-22.txt
@@ -0,0 +1,274 @@
+"Experiment section"																															
+"domain"	"ebi.ac.uk"																														
+"accession"	"E-TABM-22"																														
+"experiment_design_type"	"cell_type_comparison_design, stimulus_or_stress_design, compound_treatment_design, disease_state_design"																														
+"name"	"Lung cancers"																														
+"description"	"Distinct miRNA expression significance in human lung cancers and lung cancer cell lines."																														
+"release_date"	"2006-07-01"																														
+"submission_date"	"2005-06-20"																														
+"submitter"	"Nozomu Yanaihara"																														
+"organization"	"Laboratory of Human Carcinogenesis, NCI, NIH"																														
+																															
+"Protocol section"																															
+"accession"	"type"	"text"	"name"	"parameters"																											
+"P-MEXP-9141"	"grow"	"Human NSCLC cell lines were cultured with 10% FCS containing RPMI 1640 at 37C with 5% CO2."	"lung cancer cell line"	"medium"																											
+"P-MEXP-9142"	"compound_based_treatment"	"For the first 48h, cells were incubated with medium containing 1.0 uM 5-aza-dC, and then for another 24h with the addition of 1.0 uM TSA."	"Aza and TSA"	"compound"																											
+"P-MEXP-9143"	"nucleic_acid_extraction"	"Total RNA was isolated with TRIzol according to the manufacturer's protocol."	"RNA"																												
+"P-MEXP-9144"	"labeling"	"Five micrograms of total RNA were separately added to reaction mix in a final volume of 12 ul, containing 1 ug of [3'-(N)8-(A)12-biotin-(A)12-biotin-5_] oligonucleotide primer. The mixture was incubated for 10 min at 70C and chilled on ice. With the mixture remaining on ice, 4 ul of 5x first-strand buffer, 2 ul of 0.1 M DTT, 1 ul of 10 mM dNTP mix, and 1 ul of SuperScript II RNaseH reverse transcriptase (200 units/ul) was added to a final volume of 20 ul, and th [...]
+"P-MEXP-9145"	"hybridization"	"The microarrays were hybridized in 6xSSPE (0.9M sodium chloride/60mM sodium phosphate/8 mM EDTA, pH 7.4)/30% formamide at 25C for 18 h, washed in 0.75x TNT (Tris HCl/sodium chloride/Tween 20) at 37C for 40 min, and processed by using direct detection of the biotin-containing transcripts by Streptavidin-Alexa647 conjugate."	"miRNA array hybridization"	"RNA hybridized(ug); Hyb temperature(degree_C)"																											
+"P-MEXP-9146"	"image_acquisition"	"Processed slides were scanned by using a PerkinElmer ScanArray XL5K Scanner with the laser set to 635 nm, at power 80 and PMT 70 settings, and a scan resolution of 10 microm.Images were quantified by QUANTARRAY software (PerkinElmer). Signal intensities for each spot were calculated by subtracting local background (based on the median intensity of the area surrounding each spot) from total intensities."	"miRNA array scanning"																												
+"P-MEXP-9147"	"bioassay_data_transformation"	"An average value of the three spot replicates of each miRNA were normalized and analyzed in BRB-ArrayTools version 3.2.3.  After excluding negative values that is hybridization intensity below background, normalization was performed by using a per-chip on median normalization method and normalization to median array as reference."	"miRNA array normalization"																												
+																															
+"Hybridization section"																															
+"File[raw]"	"Array[accession]"	"BioSource"	"Protocol[grow]"	"Protocol[treatment]"	"Parameter[compound]"	"Parameter[medium]"	"Protocol[extraction]"	"Protocol[labeling]"	"Parameter[RNA labelled]"	"Dye"	"Protocol[hybridization]"	"Parameter[RNA hybridized]"	"Parameter[Hyb temperature]"	"Protocol[scanning]"	"xProtocol[normalization]"	"BioSourceMaterial"	"BioMaterialCharacteristics[BioSourceType]"	"BioMaterialCharacteristics[Organism]"	"BioMaterialCharacteristics[DiseaseState]"	"BioMaterialCha [...]
+"R1791_A549.txt"	"A-MEXP-86"	"A549"	"P-MEXP-9141"			"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"A549"			"Non-Small Cell Lung Carcinoma"		"A549"	"cell"	
+"R1790_A427.txt"	"A-MEXP-86"	"A-427"	"P-MEXP-9141"			"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"A-427"			"Non-Small Cell Lung Carcinoma"		"A-427"	"cell"	
+"R1792_A2182.txt"	"A-MEXP-86"	"A2182"	"P-MEXP-9141"			"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"A2182"			"Non-Small Cell Lung Carcinoma"		"A2182"	"cell"	
+"R1793_Calu1.txt"	"A-MEXP-86"	"Calu-1"	"P-MEXP-9141"			"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"Calu-1"			"Non-Small Cell Lung Carcinoma"		"Calu-1"	"cell"	
+"R1794_Calu6.txt"	"A-MEXP-86"	"Calu-6"	"P-MEXP-9141"			"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"Calu-6"			"Non-Small Cell Lung Carcinoma"		"Calu-6"	"cell"	
+"R1795_DMS92.txt"	"A-MEXP-86"	"DMS92"	"P-MEXP-9141"			"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Small Cell Lung Carcinoma"					"DMS92"			"Small Cell Lung Carcinoma"		"DMS92"	"cell"	
+"R1796_NCIH82.txt"	"A-MEXP-86"	"NCI-H82"	"P-MEXP-9141"			"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Small Cell Lung Carcinoma"					"NCI-H82"			"Small Cell Lung Carcinoma"		"NCI-H82"	"cell"	
+"R1797_NCIH146.txt"	"A-MEXP-86"	"NCI-H146"	"P-MEXP-9141"			"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Small Cell Lung Carcinoma"					"NCI-H146"			"Small Cell Lung Carcinoma"		"NCI-H146"	"cell"	
+"R1798_NCIH157.txt"	"A-MEXP-86"	"NCI-H157"	"P-MEXP-9141"			"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"NCI-H157"			"Non-Small Cell Lung Carcinoma"		"NCI-H157"	"cell"	
+"R1805_NCIH446.txt"	"A-MEXP-86"	"NCI-H446"	"P-MEXP-9141"			"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Small Cell Lung Carcinoma"					"NCI-H446"			"Small Cell Lung Carcinoma"		"NCI-H446"	"cell"	
+"R1807_NCIH1155.txt"	"A-MEXP-86"	"NCI-H1155"	"P-MEXP-9141"			"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"NCI-H1155"			"Non-Small Cell Lung Carcinoma"		"NCI-H1155"	"cell"	
+"R1808_NCIN417.txt"	"A-MEXP-86"	"NCI-N417"	"P-MEXP-9141"			"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Small Cell Lung Carcinoma"					"NCI-N417"			"Small Cell Lung Carcinoma"		"NCI-N417"	"cell"	
+"R1806_NCIH596.txt"	"A-MEXP-86"	"NCI-H596"	"P-MEXP-9141"			"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"NCI-H596"			"Non-Small Cell Lung Carcinoma"		"NCI-H596"	"cell"	
+"R1811_A5491-1.txt"	"A-MEXP-86"	"A549_ex1_nontreat"	"P-MEXP-9141"	"P-MEXP-9142"	"none"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"A549"		"null"	"Non-Small Cell Lung Carcinoma"		"A549"	"cell"	
+"R1812_A5491-2.txt"	"A-MEXP-86"	"A549_ex1_aza"	"P-MEXP-9141"	"P-MEXP-9142"	"Aza"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"A549"		"Aza"	"Non-Small Cell Lung Carcinoma"		"A549"	"cell"	
+"R1813_A5491-3.txt"	"A-MEXP-86"	"A549_ex1_tsa"	"P-MEXP-9141"	"P-MEXP-9142"	"TSA"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"A549"		"TSA"	"Non-Small Cell Lung Carcinoma"		"A549"	"cell"	
+"R1814_A5491-4.txt"	"A-MEXP-86"	"A549_ex1_both"	"P-MEXP-9141"	"P-MEXP-9142"	"Aza+TSA"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"A549"		"Aza"	"Non-Small Cell Lung Carcinoma"		"A549"	"cell"	
+"R1814_A5491-4.txt"	"A-MEXP-86"	"A549_ex1_both"	"P-MEXP-9141"	"P-MEXP-9142"	"Aza+TSA"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"A549"		"TSA"	"Non-Small Cell Lung Carcinoma"		"A549"	"cell"	
+"R1815_A5492-1.txt"	"A-MEXP-86"	"A549_ex2_nontreat"	"P-MEXP-9141"	"P-MEXP-9142"	"none"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"A549"		"null"	"Non-Small Cell Lung Carcinoma"		"A549"	"cell"	
+"R1816_A5492-2.txt"	"A-MEXP-86"	"A549_ex2_aza"	"P-MEXP-9141"	"P-MEXP-9142"	"Aza"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"A549"		"Aza"	"Non-Small Cell Lung Carcinoma"		"A549"	"cell"	
+"R1817_A5492-3.txt"	"A-MEXP-86"	"A549_ex2_tsa"	"P-MEXP-9141"	"P-MEXP-9142"	"TSA"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"A549"		"TSA"	"Non-Small Cell Lung Carcinoma"		"A549"	"cell"	
+"R1818_A5492-4.txt"	"A-MEXP-86"	"A549_ex2_both"	"P-MEXP-9141"	"P-MEXP-9142"	"Aza+TSA"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"A549"		"Aza"	"Non-Small Cell Lung Carcinoma"		"A549"	"cell"	
+"R1818_A5492-4.txt"	"A-MEXP-86"	"A549_ex2_both"	"P-MEXP-9141"	"P-MEXP-9142"	"Aza+TSA"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"A549"		"TSA"	"Non-Small Cell Lung Carcinoma"		"A549"	"cell"	
+"R1819_A5493-1.txt"	"A-MEXP-86"	"A549_ex3_nontreat"	"P-MEXP-9141"	"P-MEXP-9142"	"none"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"A549"		"null"	"Non-Small Cell Lung Carcinoma"		"A549"	"cell"	
+"R1820_A5493-2.txt"	"A-MEXP-86"	"A549_ex3_aza"	"P-MEXP-9141"	"P-MEXP-9142"	"Aza"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"A549"		"Aza"	"Non-Small Cell Lung Carcinoma"		"A549"	"cell"	
+"R1821_A5493-3.txt"	"A-MEXP-86"	"A549_ex3_tsa"	"P-MEXP-9141"	"P-MEXP-9142"	"TSA"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"A549"		"TSA"	"Non-Small Cell Lung Carcinoma"		"A549"	"cell"	
+"R1822_A5493-4.txt"	"A-MEXP-86"	"A549_ex3_both"	"P-MEXP-9141"	"P-MEXP-9142"	"Aza+TSA"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"A549"		"Aza"	"Non-Small Cell Lung Carcinoma"		"A549"	"cell"	
+"R1822_A5493-4.txt"	"A-MEXP-86"	"A549_ex3_both"	"P-MEXP-9141"	"P-MEXP-9142"	"Aza+TSA"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"A549"		"TSA"	"Non-Small Cell Lung Carcinoma"		"A549"	"cell"	
+"R1835_NCIH1571-1.txt"	"A-MEXP-86"	"NCI-H157_ex1_nontreat"	"P-MEXP-9141"	"P-MEXP-9142"	"none"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"NCI-H157"		"null"	"Non-Small Cell Lung Carcinoma"		"NCI-H157"	"cell"	
+"R1836_NCIH1571-2.txt"	"A-MEXP-86"	"NCI-H157_ex1_aza"	"P-MEXP-9141"	"P-MEXP-9142"	"Aza"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"NCI-H157"		"Aza"	"Non-Small Cell Lung Carcinoma"		"NCI-H157"	"cell"	
+"R1837_NCIH1571-3.txt"	"A-MEXP-86"	"NCI-H157_ex1_tsa"	"P-MEXP-9141"	"P-MEXP-9142"	"TSA"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"NCI-H157"		"TSA"	"Non-Small Cell Lung Carcinoma"		"NCI-H157"	"cell"	
+"R1838_NCIH1571-4.txt"	"A-MEXP-86"	"NCI-H157_ex1_both"	"P-MEXP-9141"	"P-MEXP-9142"	"Aza+TSA"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"NCI-H157"		"Aza"	"Non-Small Cell Lung Carcinoma"		"NCI-H157"	"cell"	
+"R1838_NCIH1571-4.txt"	"A-MEXP-86"	"NCI-H157_ex1_both"	"P-MEXP-9141"	"P-MEXP-9142"	"Aza+TSA"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"NCI-H157"		"TSA"	"Non-Small Cell Lung Carcinoma"		"NCI-H157"	"cell"	
+"R1839_NCIH1572-1.txt"	"A-MEXP-86"	"NCI-H157_ex2_nontreat"	"P-MEXP-9141"	"P-MEXP-9142"	"none"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"NCI-H157"		"null"	"Non-Small Cell Lung Carcinoma"		"NCI-H157"	"cell"	
+"R1840_NCIH1572-2.txt"	"A-MEXP-86"	"NCI-H157_ex2_aza"	"P-MEXP-9141"	"P-MEXP-9142"	"Aza"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"NCI-H157"		"Aza"	"Non-Small Cell Lung Carcinoma"		"NCI-H157"	"cell"	
+"R1841_NCIH1572-3.txt"	"A-MEXP-86"	"NCI-H157_ex2_tsa"	"P-MEXP-9141"	"P-MEXP-9142"	"TSA"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"NCI-H157"		"TSA"	"Non-Small Cell Lung Carcinoma"		"NCI-H157"	"cell"	
+"R1842_NCIH1572-4.txt"	"A-MEXP-86"	"NCI-H157_ex2_both"	"P-MEXP-9141"	"P-MEXP-9142"	"Aza+TSA"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"NCI-H157"		"Aza"	"Non-Small Cell Lung Carcinoma"		"NCI-H157"	"cell"	
+"R1842_NCIH1572-4.txt"	"A-MEXP-86"	"NCI-H157_ex2_both"	"P-MEXP-9141"	"P-MEXP-9142"	"Aza+TSA"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"NCI-H157"		"TSA"	"Non-Small Cell Lung Carcinoma"		"NCI-H157"	"cell"	
+"R1843_NCIH1573-1.txt"	"A-MEXP-86"	"NCI-H157_ex3_nontreat"	"P-MEXP-9141"	"P-MEXP-9142"	"none"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"NCI-H157"		"null"	"Non-Small Cell Lung Carcinoma"		"NCI-H157"	"cell"	
+"R1844_NCIH1573-2.txt"	"A-MEXP-86"	"NCI-H157_ex3_aza"	"P-MEXP-9141"	"P-MEXP-9142"	"Aza"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"NCI-H157"		"Aza"	"Non-Small Cell Lung Carcinoma"		"NCI-H157"	"cell"	
+"R1845_NCIH1573-3.txt"	"A-MEXP-86"	"NCI-H157_ex3_tsa"	"P-MEXP-9141"	"P-MEXP-9142"	"TSA"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"NCI-H157"		"TSA"	"Non-Small Cell Lung Carcinoma"		"NCI-H157"	"cell"	
+"R1846_NCIH1573-4.txt"	"A-MEXP-86"	"NCI-H157_ex3_both"	"P-MEXP-9141"	"P-MEXP-9142"	"Aza+TSA"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"NCI-H157"		"Aza"	"Non-Small Cell Lung Carcinoma"		"NCI-H157"	"cell"	
+"R1846_NCIH1573-4.txt"	"A-MEXP-86"	"NCI-H157_ex3_both"	"P-MEXP-9141"	"P-MEXP-9142"	"Aza+TSA"	"RPMI 1640"	"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"cell"	"fresh_sample"	"Homo sapiens"	"Non-Small Cell Lung Carcinoma"					"NCI-H157"		"TSA"	"Non-Small Cell Lung Carcinoma"		"NCI-H157"	"cell"	
+"R1595_12902T.txt"	"A-MEXP-86"	"12902T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	74	"years"	"male"	"Stage II"		"12902N"		"Adenocarcinoma"	"Stage II"			"12902N"
+"R1594_12902N.txt"	"A-MEXP-86"	"12902N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	74	"years"	"male"			"12902N"		"normal"				"12902N"
+"R1591_12822T.txt"	"A-MEXP-86"	"12822T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	66	"years"	"male"	"Stage I"		"12822N"		"Adenocarcinoma"	"Stage I"			"12822N"
+"R1590_12822N.txt"	"A-MEXP-86"	"12822N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	66	"years"	"male"			"12822N"		"normal"				"12822N"
+"R1583_12821T.txt"	"A-MEXP-86"	"12821T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	68	"years"	"male"	"Stage II"		"12821N"		"Adenocarcinoma"	"Stage II"			"12821N"
+"R1582_12821N.txt"	"A-MEXP-86"	"12821N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	68	"years"	"male"			"12821N"		"normal"				"12821N"
+"R1581_12745T.txt"	"A-MEXP-86"	"12745T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	71	"years"	"male"	"Stage I"		"12745N"		"Adenocarcinoma"	"Stage I"			"12745N"
+"R1580_12745N.txt"	"A-MEXP-86"	"12745N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	71	"years"	"male"			"12745N"		"normal"				"12745N"
+"R1579_12681T.txt"	"A-MEXP-86"	"12681T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	75	"years"	"male"	"Stage I"		"12681N"		"Adenocarcinoma"	"Stage I"			"12681N"
+"R1578_12681N.txt"	"A-MEXP-86"	"12681N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	75	"years"	"male"			"12681N"		"normal"				"12681N"
+"R1569_12606T.txt"	"A-MEXP-86"	"12606T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	66	"years"	"male"	"Stage I"		"12606N"		"Adenocarcinoma"	"Stage I"			"12606N"
+"R1568_12606N.txt"	"A-MEXP-86"	"12606N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	66	"years"	"male"			"12606N"		"normal"				"12606N"
+"R1567_12577T.txt"	"A-MEXP-86"	"12577T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	71	"years"	"male"	"Stage I"		"12577N"		"Adenocarcinoma"	"Stage I"			"12577N"
+"R1566_12577N.txt"	"A-MEXP-86"	"12577N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	71	"years"	"male"			"12577N"		"normal"				"12577N"
+"R1563_12520T.txt"	"A-MEXP-86"	"12520T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	49	"years"	"male"	"Stage I"		"12520N"		"Adenocarcinoma"	"Stage I"			"12520N"
+"R1562_12520N.txt"	"A-MEXP-86"	"12520N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	49	"years"	"male"			"12520N"		"normal"				"12520N"
+"R1559_12503T.txt"	"A-MEXP-86"	"12503T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	75	"years"	"male"	"Stage I"		"12503N"		"Adenocarcinoma"	"Stage I"			"12503N"
+"R1558_12503N.txt"	"A-MEXP-86"	"12503N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	75	"years"	"male"			"12503N"		"normal"				"12503N"
+"R1547_12490T.txt"	"A-MEXP-86"	"12490T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	57	"years"	"male"	"Stage III or IV"		"12490N"		"Adenocarcinoma"	"Stage III or IV"			"12490N"
+"R1546_12490N.txt"	"A-MEXP-86"	"12490N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	57	"years"	"male"			"12490N"		"normal"				"12490N"
+"R1539_12394T.txt"	"A-MEXP-86"	"12394T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	51	"years"	"female"	"Stage I"		"12394N"		"Adenocarcinoma"	"Stage I"			"12394N"
+"R1538_12394N.txt"	"A-MEXP-86"	"12394N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	51	"years"	"female"			"12394N"		"normal"				"12394N"
+"R1537_12374T.txt"	"A-MEXP-86"	"12374T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	68	"years"	"male"	"Stage I"		"12374N"		"Adenocarcinoma"	"Stage I"			"12374N"
+"R1536_12374N.txt"	"A-MEXP-86"	"12374N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	68	"years"	"male"			"12374N"		"normal"				"12374N"
+"R1527_12251T.txt"	"A-MEXP-86"	"12251T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	63	"years"	"female"	"Stage I"		"12251N"		"Adenocarcinoma"	"Stage I"			"12251N"
+"R1526_12251N.txt"	"A-MEXP-86"	"12251N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	63	"years"	"female"			"12251N"		"normal"				"12251N"
+"R1525_12189T.txt"	"A-MEXP-86"	"12189T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	61	"years"	"male"	"Stage I"		"12189N"		"Adenocarcinoma"	"Stage I"			"12189N"
+"R1524_12189N.txt"	"A-MEXP-86"	"12189N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	61	"years"	"male"			"12189N"		"normal"				"12189N"
+"R1521_12178T.txt"	"A-MEXP-86"	"12178T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	63	"years"	"male"	"Stage II"		"12178N"		"Adenocarcinoma"	"Stage II"			"12178N"
+"R1520_12178N.txt"	"A-MEXP-86"	"12178N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	63	"years"	"male"			"12178N"		"normal"				"12178N"
+"R1519_12170T.txt"	"A-MEXP-86"	"12170T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	50	"years"	"female"	"Stage I"		"12170N"		"Adenocarcinoma"	"Stage I"			"12170N"
+"R1518_12170N.txt"	"A-MEXP-86"	"12170N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	50	"years"	"female"			"12170N"		"normal"				"12170N"
+"R1517_12167T.txt"	"A-MEXP-86"	"12167T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	71	"years"	"male"	"Stage I"		"12167N"		"Adenocarcinoma"	"Stage I"			"12167N"
+"R1516_12167N.txt"	"A-MEXP-86"	"12167N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	71	"years"	"male"			"12167N"		"normal"				"12167N"
+"R1778_12165T.txt"	"A-MEXP-86"	"12165T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	38	"years"	"female"	"Stage III or IV"		"12165N"		"Adenocarcinoma"	"Stage III or IV"			"12165N"
+"R1777_12165N.txt"	"A-MEXP-86"	"12165N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	38	"years"	"female"			"12165N"		"normal"				"12165N"
+"R1513_12118T.txt"	"A-MEXP-86"	"12118T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	57	"years"	"male"	"Stage I"		"12118N"		"Adenocarcinoma"	"Stage I"			"12118N"
+"R1512_12118N.txt"	"A-MEXP-86"	"12118N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	57	"years"	"male"			"12118N"		"normal"				"12118N"
+"R1507_12084T.txt"	"A-MEXP-86"	"12084T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	75	"years"	"male"	"Stage I"		"12084N"		"Adenocarcinoma"	"Stage I"			"12084N"
+"R1500_12084N.txt"	"A-MEXP-86"	"12084N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	75	"years"	"male"			"12084N"		"normal"				"12084N"
+"R1099_12012T.txt"	"A-MEXP-86"	"12012T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	61	"years"	"male"	"Stage I"		"12012N"		"Adenocarcinoma"	"Stage I"			"12012N"
+"R1098_12012N.txt"	"A-MEXP-86"	"12012N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	61	"years"	"male"			"12012N"		"normal"				"12012N"
+"R1091_11922T.txt"	"A-MEXP-86"	"11922T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	80	"years"	"male"	"Stage III or IV"		"11922N"		"Adenocarcinoma"	"Stage III or IV"			"11922N"
+"R1090_11922N.txt"	"A-MEXP-86"	"11922N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	80	"years"	"male"			"11922N"		"normal"				"11922N"
+"R1776_11908T.txt"	"A-MEXP-86"	"11908T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	57	"years"	"female"	"Stage I"		"11908N"		"Adenocarcinoma"	"Stage I"			"11908N"
+"R1775_11908N.txt"	"A-MEXP-86"	"11908N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	57	"years"	"female"			"11908N"		"normal"				"11908N"
+"R1089_11898T.txt"	"A-MEXP-86"	"11898T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	84	"years"	"female"	"Stage III or IV"		"11898N"		"Adenocarcinoma"	"Stage III or IV"			"11898N"
+"R1088_11898N.txt"	"A-MEXP-86"	"11898N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	84	"years"	"female"			"11898N"		"normal"				"11898N"
+"R1087_11891T.txt"	"A-MEXP-86"	"11891T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	69	"years"	"female"	"Stage III or IV"		"11891N"		"Adenocarcinoma"	"Stage III or IV"			"11891N"
+"R1085_11891N.txt"	"A-MEXP-86"	"11891N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	69	"years"	"female"			"11891N"		"normal"				"11891N"
+"R1084_11885T.txt"	"A-MEXP-86"	"11885T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	65	"years"	"male"	"Stage I"		"11885N"		"Adenocarcinoma"	"Stage I"			"11885N"
+"R1083_11885N.txt"	"A-MEXP-86"	"11885N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	65	"years"	"male"			"11885N"		"normal"				"11885N"
+"R896_11823T.txt"	"A-MEXP-86"	"11823T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	67	"years"	"male"	"Stage I"		"11823N"		"Adenocarcinoma"	"Stage I"			"11823N"
+"R895_11823N.txt"	"A-MEXP-86"	"11823N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	67	"years"	"male"			"11823N"		"normal"				"11823N"
+"R894_11812T.txt"	"A-MEXP-86"	"11812T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	67	"years"	"male"	"Stage III or IV"		"11812N"		"Adenocarcinoma"	"Stage III or IV"			"11812N"
+"R893_11812N.txt"	"A-MEXP-86"	"11812N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	67	"years"	"male"			"11812N"		"normal"				"11812N"
+"R890_11807T.txt"	"A-MEXP-86"	"11807T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	73	"years"	"female"	"Stage I"		"11807N"		"Adenocarcinoma"	"Stage I"			"11807N"
+"R889_11807N.txt"	"A-MEXP-86"	"11807N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	73	"years"	"female"			"11807N"		"normal"				"11807N"
+"R888_11777T.txt"	"A-MEXP-86"	"11777T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	72	"years"	"female"	"Stage I"		"11777N"		"Adenocarcinoma"	"Stage I"			"11777N"
+"R887_11777N.txt"	"A-MEXP-86"	"11777N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	72	"years"	"female"			"11777N"		"normal"				"11777N"
+"R882_11671T.txt"	"A-MEXP-86"	"11671T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	53	"years"	"male"	"Stage I"		"11671N"		"Adenocarcinoma"	"Stage I"			"11671N"
+"R881_11671N.txt"	"A-MEXP-86"	"11671N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	53	"years"	"male"			"11671N"		"normal"				"11671N"
+"R880_11669T.txt"	"A-MEXP-86"	"11669T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	67	"years"	"female"	"Stage I"		"11669N"		"Adenocarcinoma"	"Stage I"			"11669N"
+"R879_11669N.txt"	"A-MEXP-86"	"11669N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	67	"years"	"female"			"11669N"		"normal"				"11669N"
+"R876_11616T.txt"	"A-MEXP-86"	"11616T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	65	"years"	"male"	"Stage III or IV"		"11616N"		"Adenocarcinoma"	"Stage III or IV"			"11616N"
+"R875_11616N.txt"	"A-MEXP-86"	"11616N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	65	"years"	"male"			"11616N"		"normal"				"11616N"
+"R1074_11547T.txt"	"A-MEXP-86"	"11547T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	74	"years"	"female"	"Stage III or IV"		"11547N"		"Adenocarcinoma"	"Stage III or IV"			"11547N"
+"R1073_11547N.txt"	"A-MEXP-86"	"11547N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	74	"years"	"female"			"11547N"		"normal"				"11547N"
+"R1072_11541T.txt"	"A-MEXP-86"	"11541T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	70	"years"	"male"	"Stage I"		"11541N"		"Adenocarcinoma"	"Stage I"			"11541N"
+"R1071_11541N.txt"	"A-MEXP-86"	"11541N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	70	"years"	"male"			"11541N"		"normal"				"11541N"
+"R1070_11502T.txt"	"A-MEXP-86"	"11502T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	61	"years"	"female"	"Stage III or IV"		"11502N"		"Adenocarcinoma"	"Stage III or IV"			"11502N"
+"R1069_11502N.txt"	"A-MEXP-86"	"11502N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	61	"years"	"female"			"11502N"		"normal"				"11502N"
+"R1068_11447T.txt"	"A-MEXP-86"	"11447T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	52	"years"	"female"	"Stage I"		"11447N"		"Adenocarcinoma"	"Stage I"			"11447N"
+"R1067_11447N.txt"	"A-MEXP-86"	"11447N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	52	"years"	"female"			"11447N"		"normal"				"11447N"
+"R900_11404T.txt"	"A-MEXP-86"	"11404T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	38	"years"	"female"	"Stage I"		"11404N"		"Adenocarcinoma"	"Stage I"			"11404N"
+"R899_11404N.txt"	"A-MEXP-86"	"11404N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	38	"years"	"female"			"11404N"		"normal"				"11404N"
+"R1050_11343T.txt"	"A-MEXP-86"	"11343T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	68	"years"	"male"	"Stage II"		"11343N"		"Adenocarcinoma"	"Stage II"			"11343N"
+"R1049_11343N.txt"	"A-MEXP-86"	"11343N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	68	"years"	"male"			"11343N"		"normal"				"11343N"
+"R1048_11231T.txt"	"A-MEXP-86"	"11231T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	56	"years"	"male"	"Stage III or IV"		"11231N"		"Adenocarcinoma"	"Stage III or IV"			"11231N"
+"R1047_11231N.txt"	"A-MEXP-86"	"11231N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	56	"years"	"male"			"11231N"		"normal"				"11231N"
+"R1042_11191T.txt"	"A-MEXP-86"	"11191T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	47	"years"	"male"	"Stage I"		"11191N"		"Adenocarcinoma"	"Stage I"			"11191N"
+"R1041_11191N.txt"	"A-MEXP-86"	"11191N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	47	"years"	"male"			"11191N"		"normal"				"11191N"
+"R1040_11184T.txt"	"A-MEXP-86"	"11184T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	53	"years"	"female"	"Stage I"		"11184N"		"Adenocarcinoma"	"Stage I"			"11184N"
+"R1039_11184N.txt"	"A-MEXP-86"	"11184N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	53	"years"	"female"			"11184N"		"normal"				"11184N"
+"R1036_11139T.txt"	"A-MEXP-86"	"11139T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	49	"years"	"male"	"Stage I"		"11139N"		"Adenocarcinoma"	"Stage I"			"11139N"
+"R1035_11139N.txt"	"A-MEXP-86"	"11139N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	49	"years"	"male"			"11139N"		"normal"				"11139N"
+"R1026_11019T.txt"	"A-MEXP-86"	"11019T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	59	"years"	"male"	"Stage III or IV"		"11019N"		"Adenocarcinoma"	"Stage III or IV"			"11019N"
+"R1025_11019N.txt"	"A-MEXP-86"	"11019N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	59	"years"	"male"			"11019N"		"normal"				"11019N"
+"R1020_11005T.txt"	"A-MEXP-86"	"11005T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	76	"years"	"female"	"Stage I"		"11005N"		"Adenocarcinoma"	"Stage I"			"11005N"
+"R1019_11005N.txt"	"A-MEXP-86"	"11005N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	76	"years"	"female"			"11005N"		"normal"				"11005N"
+"R991_10622T.txt"	"A-MEXP-86"	"10622T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	57	"years"	"male"	"Stage I"		"10622N"		"Adenocarcinoma"	"Stage I"			"10622N"
+"R990_10622N.txt"	"A-MEXP-86"	"10622N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	57	"years"	"male"			"10622N"		"normal"				"10622N"
+"R989_10605T.txt"	"A-MEXP-86"	"10605T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	62	"years"	"male"	"Stage I"		"10605N"		"Adenocarcinoma"	"Stage I"			"10605N"
+"R988_10605N.txt"	"A-MEXP-86"	"10605N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	62	"years"	"male"			"10605N"		"normal"				"10605N"
+"R983_10455T.txt"	"A-MEXP-86"	"10455T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	66	"years"	"female"	"Stage III or IV"		"10455N"		"Adenocarcinoma"	"Stage III or IV"			"10455N"
+"R982_10455N.txt"	"A-MEXP-86"	"10455N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	66	"years"	"female"			"10455N"		"normal"				"10455N"
+"R977_10419T.txt"	"A-MEXP-86"	"10419T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	72	"years"	"female"	"Stage I"		"10419N"		"Adenocarcinoma"	"Stage I"			"10419N"
+"R976_10419N.txt"	"A-MEXP-86"	"10419N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	72	"years"	"female"			"10419N"		"normal"				"10419N"
+"R868_10213T.txt"	"A-MEXP-86"	"10213T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	72	"years"	"male"	"Stage III or IV"		"10213N"		"Adenocarcinoma"	"Stage III or IV"			"10213N"
+"R867_10213N.txt"	"A-MEXP-86"	"10213N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	72	"years"	"male"			"10213N"		"normal"				"10213N"
+"R866_10210T.txt"	"A-MEXP-86"	"10210T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	79	"years"	"male"	"Stage I"		"10210N"		"Adenocarcinoma"	"Stage I"			"10210N"
+"R865_10210N.txt"	"A-MEXP-86"	"10210N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	79	"years"	"male"			"10210N"		"normal"				"10210N"
+"R1749_10018T.txt"	"A-MEXP-86"	"10018T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	63	"years"	"female"	"Stage III or IV"		"10018N"		"Adenocarcinoma"	"Stage III or IV"			"10018N"
+"R1748_10018N.txt"	"A-MEXP-86"	"10018N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	63	"years"	"female"			"10018N"		"normal"				"10018N"
+"R1747_1822T.txt"	"A-MEXP-86"	"1822T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	61	"years"	"male"	"Stage III or IV"		"1822N"		"Adenocarcinoma"	"Stage III or IV"			"1822N"
+"R1746_1822N.txt"	"A-MEXP-86"	"1822N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	61	"years"	"male"			"1822N"		"normal"				"1822N"
+"R1743_1818T.txt"	"A-MEXP-86"	"1818T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	55	"years"	"male"	"Stage I"		"1818N"		"Adenocarcinoma"	"Stage I"			"1818N"
+"R1742_1818N.txt"	"A-MEXP-86"	"1818N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	55	"years"	"male"			"1818N"		"normal"				"1818N"
+"R1731_1729T.txt"	"A-MEXP-86"	"1729T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	73	"years"	"female"	"Stage I"		"1729N"		"Adenocarcinoma"	"Stage I"			"1729N"
+"R1730_1729N.txt"	"A-MEXP-86"	"1729N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	73	"years"	"female"			"1729N"		"normal"				"1729N"
+"R1729_1718T.txt"	"A-MEXP-86"	"1718T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	58	"years"	"female"	"Stage II"		"1718N"		"Adenocarcinoma"	"Stage II"			"1718N"
+"R1728_1718N.txt"	"A-MEXP-86"	"1718N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	58	"years"	"female"			"1718N"		"normal"				"1718N"
+"R1723_1610T.txt"	"A-MEXP-86"	"1610T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	69	"years"	"female"	"Stage II"		"1610N"		"Adenocarcinoma"	"Stage II"			"1610N"
+"R1722_1610N.txt"	"A-MEXP-86"	"1610N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	69	"years"	"female"			"1610N"		"normal"				"1610N"
+"R1721_1604T.txt"	"A-MEXP-86"	"1604T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	59	"years"	"female"	"Stage I"		"1604N"		"Adenocarcinoma"	"Stage I"			"1604N"
+"R1720_1604N.txt"	"A-MEXP-86"	"1604N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	59	"years"	"female"			"1604N"		"normal"				"1604N"
+"R1719_1570T.txt"	"A-MEXP-86"	"1570T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	70	"years"	"male"	"Stage III or IV"		"1570N"		"Adenocarcinoma"	"Stage III or IV"			"1570N"
+"R1718_1570N.txt"	"A-MEXP-86"	"1570N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	70	"years"	"male"			"1570N"		"normal"				"1570N"
+"R1715_1545T.txt"	"A-MEXP-86"	"1545T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	69	"years"	"female"	"Stage I"		"1545N"		"Adenocarcinoma"	"Stage I"			"1545N"
+"R1714_1545N.txt"	"A-MEXP-86"	"1545N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	69	"years"	"female"			"1545N"		"normal"				"1545N"
+"R1711_1532T.txt"	"A-MEXP-86"	"1532T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	62	"years"	"male"	"Stage II"		"1532N"		"Adenocarcinoma"	"Stage II"			"1532N"
+"R1710_1532N.txt"	"A-MEXP-86"	"1532N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	62	"years"	"male"			"1532N"		"normal"				"1532N"
+"R1707_1498T.txt"	"A-MEXP-86"	"1498T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	67	"years"	"female"	"Stage I"		"1498N"		"Adenocarcinoma"	"Stage I"			"1498N"
+"R1600_1498N.txt"	"A-MEXP-86"	"1498N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	67	"years"	"female"			"1498N"		"normal"				"1498N"
+"R1599_1449T.txt"	"A-MEXP-86"	"1449T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	79	"years"	"female"	"Stage II"		"1449N"		"Adenocarcinoma"	"Stage II"			"1449N"
+"R1598_1449N.txt"	"A-MEXP-86"	"1449N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	79	"years"	"female"			"1449N"		"normal"				"1449N"
+"R1557_1249T.txt"	"A-MEXP-86"	"1249T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	54	"years"	"male"	"Stage I"		"1249N"		"Adenocarcinoma"	"Stage I"			"1249N"
+"R1550_1249N.txt"	"A-MEXP-86"	"1249N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	54	"years"	"male"			"1249N"		"normal"				"1249N"
+"R892_1180T.txt"	"A-MEXP-86"	"1180T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Adenocarcinoma"	76	"years"	"male"	"Stage I"		"1180N"		"Adenocarcinoma"	"Stage I"			"1180N"
+"R891_1180N.txt"	"A-MEXP-86"	"1180N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	76	"years"	"male"			"1180N"		"normal"				"1180N"
+"R1597_12979T.txt"	"A-MEXP-86"	"12979T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	52	"years"	"male"	"Stage I"		"12979N"		"Squamous Cell Carcinoma"	"Stage I"			"12979N"
+"R1596_12979N.txt"	"A-MEXP-86"	"12979N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	52	"years"	"male"			"12979N"		"normal"				"12979N"
+"R1531_12281T.txt"	"A-MEXP-86"	"12281T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	59	"years"	"female"	"Stage I"		"12281N"		"Squamous Cell Carcinoma"	"Stage I"			"12281N"
+"R1530_12281N.txt"	"A-MEXP-86"	"12281N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	59	"years"	"female"			"12281N"		"normal"				"12281N"
+"R1523_12187T.txt"	"A-MEXP-86"	"12187T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	72	"years"	"female"	"Stage I"		"12187N"		"Squamous Cell Carcinoma"	"Stage I"			"12187N"
+"R1522_12187N.txt"	"A-MEXP-86"	"12187N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	72	"years"	"female"			"12187N"		"normal"				"12187N"
+"R1515_12147T.txt"	"A-MEXP-86"	"12147T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	56	"years"	"male"	"Stage I"		"12147N"		"Squamous Cell Carcinoma"	"Stage I"			"12147N"
+"R1514_12147N.txt"	"A-MEXP-86"	"12147N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	56	"years"	"male"			"12147N"		"normal"				"12147N"
+"R1511_12104T.txt"	"A-MEXP-86"	"12104T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	55	"years"	"female"	"Stage II"		"12104N"		"Squamous Cell Carcinoma"	"Stage II"			"12104N"
+"R1510_12104N.txt"	"A-MEXP-86"	"12104N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	55	"years"	"female"			"12104N"		"normal"				"12104N"
+"R1499_12027T.txt"	"A-MEXP-86"	"12027T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	73	"years"	"female"	"Stage I"		"12027N"		"Squamous Cell Carcinoma"	"Stage I"			"12027N"
+"R1498_12027N.txt"	"A-MEXP-86"	"12027N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	73	"years"	"female"			"12027N"		"normal"				"12027N"
+"R1497_12025T.txt"	"A-MEXP-86"	"12025T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	49	"years"	"male"	"Stage II"		"12025N"		"Squamous Cell Carcinoma"	"Stage II"			"12025N"
+"R1100_12025N.txt"	"A-MEXP-86"	"12025N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	49	"years"	"male"			"12025N"		"normal"				"12025N"
+"R1765_12017T.txt"	"A-MEXP-86"	"12017T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	67	"years"	"female"	"Stage I"		"12017N"		"Squamous Cell Carcinoma"	"Stage I"			"12017N"
+"R1764_12017N.txt"	"A-MEXP-86"	"12017N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	67	"years"	"female"			"12017N"		"normal"				"12017N"
+"R1496_12003T.txt"	"A-MEXP-86"	"12003T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	66	"years"	"male"	"Stage I"		"12003N"		"Squamous Cell Carcinoma"	"Stage I"			"12003N"
+"R1495_12003N.txt"	"A-MEXP-86"	"12003N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	66	"years"	"male"			"12003N"		"normal"				"12003N"
+"R1097_11950T.txt"	"A-MEXP-86"	"11950T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	66	"years"	"male"	"Stage II"		"11950N"		"Squamous Cell Carcinoma"	"Stage II"			"11950N"
+"R1096_11950N.txt"	"A-MEXP-86"	"11950N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	66	"years"	"male"			"11950N"		"normal"				"11950N"
+"R1093_11923T.txt"	"A-MEXP-86"	"11923T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	72	"years"	"female"	"Stage I"		"11923N"		"Squamous Cell Carcinoma"	"Stage I"			"11923N"
+"R1092_11923N.txt"	"A-MEXP-86"	"11923N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	72	"years"	"female"			"11923N"		"normal"				"11923N"
+"R1763_11764T.txt"	"A-MEXP-86"	"11764T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	68	"years"	"male"	"Stage I"		"11764N"		"Squamous Cell Carcinoma"	"Stage I"			"11764N"
+"R1762_11764N.txt"	"A-MEXP-86"	"11764N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	68	"years"	"male"			"11764N"		"normal"				"11764N"
+"R886_11759T.txt"	"A-MEXP-86"	"11759T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	67	"years"	"male"	"Stage I"		"11759N"		"Squamous Cell Carcinoma"	"Stage I"			"11759N"
+"R885_11759N.txt"	"A-MEXP-86"	"11759N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	67	"years"	"male"			"11759N"		"normal"				"11759N"
+"R884_11728T.txt"	"A-MEXP-86"	"11728T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	53	"years"	"male"	"Stage II"		"11728N"		"Squamous Cell Carcinoma"	"Stage II"			"11728N"
+"R883_11728N.txt"	"A-MEXP-86"	"11728N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	53	"years"	"male"			"11728N"		"normal"				"11728N"
+"R1082_11588T.txt"	"A-MEXP-86"	"11588T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	69	"years"	"female"	"Stage I"		"11588N"		"Squamous Cell Carcinoma"	"Stage I"			"11588N"
+"R1081_11588N.txt"	"A-MEXP-86"	"11588N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	69	"years"	"female"			"11588N"		"normal"				"11588N"
+"R1080_11574T.txt"	"A-MEXP-86"	"11574T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	63	"years"	"male"	"Stage III or IV"		"11574N"		"Squamous Cell Carcinoma"	"Stage III or IV"			"11574N"
+"R1079_11574N.txt"	"A-MEXP-86"	"11574N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	63	"years"	"male"			"11574N"		"normal"				"11574N"
+"R1058_11238T.txt"	"A-MEXP-86"	"11238T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	69	"years"	"male"	"Stage I"		"11238N"		"Squamous Cell Carcinoma"	"Stage I"			"11238N"
+"R1057_11238N.txt"	"A-MEXP-86"	"11238N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	69	"years"	"male"			"11238N"		"normal"				"11238N"
+"R1030_11047T.txt"	"A-MEXP-86"	"11047T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	45	"years"	"female"	"Stage II"		"11047N"		"Squamous Cell Carcinoma"	"Stage II"			"11047N"
+"R1029_11047N.txt"	"A-MEXP-86"	"11047N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	45	"years"	"female"			"11047N"		"normal"				"11047N"
+"R1028_11045T.txt"	"A-MEXP-86"	"11045T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	59	"years"	"male"	"Stage III or IV"		"11045N"		"Squamous Cell Carcinoma"	"Stage III or IV"			"11045N"
+"R1027_11045N.txt"	"A-MEXP-86"	"11045N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	59	"years"	"male"			"11045N"		"normal"				"11045N"
+"R1024_11018T.txt"	"A-MEXP-86"	"11018T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	70	"years"	"male"	"Stage II"		"11018N"		"Squamous Cell Carcinoma"	"Stage II"			"11018N"
+"R1023_11018N.txt"	"A-MEXP-86"	"11018N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	70	"years"	"male"			"11018N"		"normal"				"11018N"
+"R1022_11008T.txt"	"A-MEXP-86"	"11008T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	60	"years"	"female"	"Stage I"		"11008N"		"Squamous Cell Carcinoma"	"Stage I"			"11008N"
+"R1021_11008N.txt"	"A-MEXP-86"	"11008N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	60	"years"	"female"			"11008N"		"normal"				"11008N"
+"R1018_11002T.txt"	"A-MEXP-86"	"11002T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	74	"years"	"male"	"Stage I"		"11002N"		"Squamous Cell Carcinoma"	"Stage I"			"11002N"
+"R1017_11002N.txt"	"A-MEXP-86"	"11002N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	74	"years"	"male"			"11002N"		"normal"				"11002N"
+"R1014_10941T.txt"	"A-MEXP-86"	"10941T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	68	"years"	"male"	"Stage I"		"10941N"		"Squamous Cell Carcinoma"	"Stage I"			"10941N"
+"R1013_10941N.txt"	"A-MEXP-86"	"10941N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	68	"years"	"male"			"10941N"		"normal"				"10941N"
+"R1007_10788T.txt"	"A-MEXP-86"	"10788T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	54	"years"	"female"	"Stage III or IV"		"10788N"		"Squamous Cell Carcinoma"	"Stage III or IV"			"10788N"
+"R1000_10788N.txt"	"A-MEXP-86"	"10788N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	54	"years"	"female"			"10788N"		"normal"				"10788N"
+"R999_10780T.txt"	"A-MEXP-86"	"10780T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	75	"years"	"female"	"Stage III or IV"		"10780N"		"Squamous Cell Carcinoma"	"Stage III or IV"			"10780N"
+"R998_10780N.txt"	"A-MEXP-86"	"10780N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	75	"years"	"female"			"10780N"		"normal"				"10780N"
+"R997_10759T.txt"	"A-MEXP-86"	"10759T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	72	"years"	"male"	"Stage III or IV"		"10759N"		"Squamous Cell Carcinoma"	"Stage III or IV"			"10759N"
+"R996_10759N.txt"	"A-MEXP-86"	"10759N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	72	"years"	"male"			"10759N"		"normal"				"10759N"
+"R995_10743T.txt"	"A-MEXP-86"	"10743T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	54	"years"	"male"	"Stage I"		"10743N"		"Squamous Cell Carcinoma"	"Stage I"			"10743N"
+"R994_10743N.txt"	"A-MEXP-86"	"10743N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	54	"years"	"male"			"10743N"		"normal"				"10743N"
+"R975_10417T.txt"	"A-MEXP-86"	"10417T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	74	"years"	"male"	"Stage II"		"10417N"		"Squamous Cell Carcinoma"	"Stage II"			"10417N"
+"R874_10417N.txt"	"A-MEXP-86"	"10417N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	74	"years"	"male"			"10417N"		"normal"				"10417N"
+"R873_10404T.txt"	"A-MEXP-86"	"10404T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	78	"years"	"male"	"Stage II"		"10404N"		"Squamous Cell Carcinoma"	"Stage II"			"10404N"
+"R871_10404N.txt"	"A-MEXP-86"	"10404N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	78	"years"	"male"			"10404N"		"normal"				"10404N"
+"R870_10296T.txt"	"A-MEXP-86"	"10296T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	73	"years"	"male"	"Stage I"		"10296N"		"Squamous Cell Carcinoma"	"Stage I"			"10296N"
+"R869_10296N.txt"	"A-MEXP-86"	"10296N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	73	"years"	"male"			"10296N"		"normal"				"10296N"
+"R1759_10286T.txt"	"A-MEXP-86"	"10286T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	65	"years"	"male"	"Stage I"		"10286N"		"Squamous Cell Carcinoma"	"Stage I"			"10286N"
+"R1758_10286N.txt"	"A-MEXP-86"	"10286N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	65	"years"	"male"			"10286N"		"normal"				"10286N"
+"R1757_10070T.txt"	"A-MEXP-86"	"10070T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	75	"years"	"male"	"Stage I"		"10070N"		"Squamous Cell Carcinoma"	"Stage I"			"10070N"
+"R1750_10070N.txt"	"A-MEXP-86"	"10070N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	75	"years"	"male"			"10070N"		"normal"				"10070N"
+"R1745_1820T.txt"	"A-MEXP-86"	"1820T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	51	"years"	"male"	"Stage II"		"1820N"		"Squamous Cell Carcinoma"	"Stage II"			"1820N"
+"R1744_1820N.txt"	"A-MEXP-86"	"1820N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	51	"years"	"male"			"1820N"		"normal"				"1820N"
+"R1741_1800T.txt"	"A-MEXP-86"	"1800T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	74	"years"	"male"	"Stage I"		"1800N"		"Squamous Cell Carcinoma"	"Stage I"			"1800N"
+"R1740_1800N.txt"	"A-MEXP-86"	"1800N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	74	"years"	"male"			"1800N"		"normal"				"1800N"
+"R1739_1799T.txt"	"A-MEXP-86"	"1799T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	66	"years"	"male"	"Stage I"		"1799N"		"Squamous Cell Carcinoma"	"Stage I"			"1799N"
+"R1738_1799N.txt"	"A-MEXP-86"	"1799N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	66	"years"	"male"			"1799N"		"normal"				"1799N"
+"R1735_1797T.txt"	"A-MEXP-86"	"1797T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	73	"years"	"female"	"Stage I"		"1797N"		"Squamous Cell Carcinoma"	"Stage I"			"1797N"
+"R1734_1797N.txt"	"A-MEXP-86"	"1797N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	73	"years"	"female"			"1797N"		"normal"				"1797N"
+"R1733_1746T.txt"	"A-MEXP-86"	"1746T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	72	"years"	"female"	"Stage I"		"1746N"		"Squamous Cell Carcinoma"	"Stage I"			"1746N"
+"R1732_1746N.txt"	"A-MEXP-86"	"1746N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	72	"years"	"female"			"1746N"		"normal"				"1746N"
+"R1725_1614T.txt"	"A-MEXP-86"	"1614T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	63	"years"	"male"	"Stage III or IV"		"1614N"		"Squamous Cell Carcinoma"	"Stage III or IV"			"1614N"
+"R1724_1614N.txt"	"A-MEXP-86"	"1614N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	63	"years"	"male"			"1614N"		"normal"				"1614N"
+"R1713_1535T.txt"	"A-MEXP-86"	"1535T"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"Squamous Cell Carcinoma"	71	"years"	"male"	"Stage I"		"1535N"		"Squamous Cell Carcinoma"	"Stage I"			"1535N"
+"R1712_1535N.txt"	"A-MEXP-86"	"1535N"					"P-MEXP-9143"	"P-MEXP-9144"	5	"biotin"	"P-MEXP-9145"	5	25	"P-MEXP-9146"	"P-MEXP-9147"	"organism_part"	"fresh_sample"	"Homo sapiens"	"normal"	71	"years"	"male"			"1535N"		"normal"				"1535N"
diff --git a/examples/real/E-TABM-33.png b/examples/real/E-TABM-33.png
new file mode 100644
index 0000000..a56dfa4
Binary files /dev/null and b/examples/real/E-TABM-33.png differ
diff --git a/examples/real/E-TABM-33.txt b/examples/real/E-TABM-33.txt
new file mode 100644
index 0000000..313941b
--- /dev/null
+++ b/examples/real/E-TABM-33.txt
@@ -0,0 +1,55 @@
+"# Data for immediate release, although no paper has been submitted yet."																				
+"Experiment section"																				
+"domain"	"ebi.ac.uk"																			
+"accession"	"E-TABM-33"																			
+"quality_control"	"biological_replicate, spike_quality_control, real_time_PCR_quality_control"																			
+"experiment_design_type"	"development_or_differentiation_design, time_series_design"																			
+"name"	"Micorarray analysis of gene expression in zebrafish development"																			
+"description"	"We are performing microarray experiments for expression profiling of zebrafish embryogenesis, both as a baseline for future analysis of mutant and other conditions and to validate our microarray technology.  For our purpose we used the Affymetrix zebrafish array which contains approximately 15,000 genes. This represents about 50 % of the estimated number of zebrafish genes.  Total RNA was collected from embryos at 16 different stages (zygote, shield stage, 75 % epiboly, 90 [...]
+"release_date"	2005-08-18																			
+"submission_date"	2005-08-18																			
+"submitter"	"Marc Saric; Martina Konantz "																			
+"organization"	"Max-Planck-Institute for Developmental Biology, T�bingen, Germany"																			
+"publication_title"	"Micorarray analysis of gene expression in zebrafish development"																			
+"authors"	"Martina Konantz; Georg-Wilhelm Otto; Christian Weiler; Marc Saric; Robert Geisler"																			
+																				
+																				
+"# Protocol for labeling is P-AFFY-2 with standard-parameters as found in ArrayExpress"																				
+"Protocol section"	" "	" "	" "	" "																
+"accession "	"text "	"name "	"type "	"parameters"																
+"P-TABM-55"	"T�bingen wildtype-fish were maintained as described in N�sslein-Volhard and Dahm: Zebrafish, PAS Oxford Univ. Press ISBN 019963808 X. Staging was done by hours postfertilization (Kimmel et al) in liquid nitrogen and embryos were stored at  - 80 �C. "	"Zebrafish growth"	"grow, sacrifice, store"	"degree_C"																
+"P-TABM-56"	"Briefly, total RNAs were prepared from every stages using TRIzol reagent (Invitrogen GmbH, Germany) with an added chlorophorm extraction before precipitation. Quality of embryonic RNA was assesed using the NanoDrop ND-1000 UV-Bis Spectrophotometer (NanoDrop Technologies, Wilmington, DE, USA). Samples where stored at -80�C"	"RNA extraction"	"nucleic_acid_extraction, store"	"degree_C"																
+"P-AFFY-2"	"Affymetrix_IVT_protocol In vitro transcription cRNA labeling"	"Affymetrix_IVT_protocol"																		
+																				
+"Hybridization section"																				
+"File[raw]  "	"File[normalized]  "	"File[exp]  "	"File[cdf]  "	"Array[accession]  "	"Array[serial]"	"Protocol[grow]"	"Protocol[extraction]"	"Protocol[labeling]"	"BioSource"	"BioSourceMaterial"	"BioMaterialCharacteristics[StrainOrLine]"	"BioMaterialCharacteristics[Genotype]"	"BioMaterialCharacteristics[Organism]"	"FactorValue[Time]"	"BioMaterialCharacteristics[Age]"	"BioMaterialCharacteristics[TimeUnit]"	"BioMaterialCharacteristics[InitialTimePoint]"	"FactorValue[DevelopmentalStage]"	"Bio [...]
+"Tue-10h-1.CEL"	"Tue-10h-1.CHP"	"Tue-10h-1.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_10h-1"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"10h"	10	"hours"	"fertilization"	"Gastrula:Bud"	"Gastrula:Bud"	"frozen_sample"
+"Tue-10h-2.CEL"	"Tue-10h-2.CHP"	"Tue-10h-2.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_10h-2"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"10h"	10	"hours"	"fertilization"	"Gastrula:Bud"	"Gastrula:Bud"	"frozen_sample"
+"Tue-11.7h-1.CEL"	"Tue-11.7h-1.CHP"	"Tue-11.7h-1.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_11.7h-1"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"11.7h"	11.7	"hours"	"fertilization"	"Segmentation:5-9 somites"	"Segmentation:5-9 somites"	"frozen_sample"
+"Tue-11.7h-2.CEL"	"Tue-11.7h-2.CHP"	"Tue-11.7h-2.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_11.7h-2"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"11.7h"	11.7	"hours"	"fertilization"	"Segmentation:5-9 somites"	"Segmentation:5-9 somites"	"frozen_sample"
+"Tue-14d-1.CEL"	"Tue-14d-1.CHP"	"Tue-14d-1.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_14d-1"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"14d"	14	"days"	"fertilization"	"Larval:Days 14-20"	"Larval:Days 14-20"	"frozen_sample"
+"Tue-14d-2.CEL"	"Tue-14d-2.CHP"	"Tue-14d-2.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_14d_2"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"14d"	14	"days"	"fertilization"	"Larval:Days 14-20"	"Larval:Days 14-20"	"frozen_sample"
+"Tue-16h-1.CEL"	"Tue-16h-1.CHP"	"Tue-16h-1.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_16h-1"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"16h"	16	"hours"	"fertilization"	"Segmentation:14-19 somites"	"Segmentation:14-19 somites"	"frozen_sample"
+"Tue-16h-2.CEL"	"Tue-16h-2.CHP"	"Tue-16h-2.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_16h-2"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"16h"	16	"hours"	"fertilization"	"Segmentation:14-19 somites"	"Segmentation:14-19 somites"	"frozen_sample"
+"Tue-1c-1.CEL"	"Tue-1c-1.CHP"	"Tue-1c-1.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_1c-1"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"0.25h"	0.25	"hours"	"fertilization"	"Zygote:1-cell"	"Zygote:1-cell"	"frozen_sample"
+"Tue-1c-2.CEL"	"Tue-1c-2.CHP"	"Tue-1c-2.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_1c-2"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"0.25h"	0.25	"hours"	"fertilization"	"Zygote:1-cell"	"Zygote:1-cell"	"frozen_sample"
+"Tue-24h-1.CEL"	"Tue-24h-1.CHP"	"Tue-24h-1.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_24h-1"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"24h"	24	"hours"	"fertilization"	"Pharyngula:Prim-5"	"Pharyngula:Prim-5"	"frozen_sample"
+"Tue-24h-2.CEL"	"Tue-24h-2.CHP"	"Tue-24h-2.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_24h-2"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"24h"	24	"hours"	"fertilization"	"Pharyngula:Prim-5"	"Pharyngula:Prim-5"	"frozen_sample"
+"Tue-30d-1.CEL"	"Tue-30d-1.CHP"	"Tue-30d-1.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_30d-1"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"30d"	30	"days"	"fertilization"	"Juvenile:Days 30-44"	"Juvenile:Days 30-44"	"frozen_sample"
+"Tue-30d-2.CEL"	"Tue-30d-2.CHP"	"Tue-30d-2.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_30d-2"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"30d"	30	"days"	"fertilization"	"Juvenile:Days 30-44"	"Juvenile:Days 30-44"	"frozen_sample"
+"Tue-32h-1.CEL"	"Tue-32h-1.CHP"	"Tue-32h-1.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_32h-1"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"32h"	32	"hours"	"fertilization"	"Pharyngula:Prim-15"	"Pharyngula:Prim-15"	"frozen_sample"
+"Tue-32h-2.CEL"	"Tue-32h-2.CHP"	"Tue-32h-2.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_32h-2"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"32h"	32	"hours"	"fertilization"	"Pharyngula:Prim-15"	"Pharyngula:Prim-15"	"frozen_sample"
+"Tue-48h-1.CEL"	"Tue-48h-1.CHP"	"Tue-48h-1.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_48h-1"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"48h"	48	"hours"	"fertilization"	"Hatching:Long-pec"	"Hatching:Long-pec"	"frozen_sample"
+"Tue-48h-2.CEL"	"Tue-48h-2.CHP"	"Tue-48h-2.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_48h-2"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"48h"	48	"hours"	"fertilization"	"Hatching:Long-pec"	"Hatching:Long-pec"	"frozen_sample"
+"Tue-4d-1.CEL"	"Tue-4d-1.CHP"	"Tue-4d-1.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_4d-1"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"4d"	4	"days"	"fertilization"	"Larval:Day 4"	"Larval:Day 4"	"frozen_sample"
+"Tue-4d-2.CEL"	"Tue-4d-2.CHP"	"Tue-4d-2.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_4d-2"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"4d"	4	"days"	"fertilization"	"Larval:Day 4"	"Larval:Day 4"	"frozen_sample"
+"Tue-5d-1.CEL"	"Tue-5d-1.CHP"	"Tue-5d-1.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_5d-1"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"5d"	5	"days"	"fertilization"	"Larval:Day 5"	"Larval:Day 5"	"frozen_sample"
+"Tue-5d-2.CEL"	"Tue-5d-2.CHP"	"Tue-5d-2.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_5d-2"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"5d"	5	"days"	"fertilization"	"Larval:Day 5"	"Larval:Day 5"	"frozen_sample"
+"Tue-6h-1.CEL"	"Tue-6h-1.CHP"	"Tue-6h-1.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_6h-1"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"6h"	6	"hours"	"fertilization"	"Gastrula:Shield"	"Gastrula:Shield"	"frozen_sample"
+"Tue-6h-2.CEL"	"Tue-6h-2.CHP"	"Tue-6h-2.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_6h-2"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"6h"	6	"hours"	"fertilization"	"Gastrula:Shield"	"Gastrula:Shield"	"frozen_sample"
+"Tue-8h-1.CEL"	"Tue-8h-1.CHP"	"Tue-8h-1.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_8h-1"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"8h"	8	"hours"	"fertilization"	"Gastrula:75%-epiboly"	"Gastrula:75%-epiboly"	"frozen_sample"
+"Tue-8h-2.CEL"	"Tue-8h-2.CHP"	"Tue-8h-2.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_8h-2"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"8h"	8	"hours"	"fertilization"	"Gastrula:75%-epiboly"	"Gastrula:75%-epiboly"	"frozen_sample"
+"Tue-90d-1.CEL"	"Tue-90d-1.CHP"	"Tue-90d-1.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_90d-1"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"90d"	90	"days"	"fertilization"	"Adult"	"Adult"	"frozen_sample"
+"Tue-90d-2.CEL"	"Tue-90d-2.CHP"	"Tue-90d-2.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_90d-2"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"90d"	90	"days"	"fertilization"	"Adult"	"Adult"	"frozen_sample"
+"Tue-9h-1.CEL"	"Tue-9h-1.CHP"	"Tue-9h-1.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_9h-1"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"9h"	9	"hours"	"fertilization"	"Gastrula:90%-epiboly"	"Gastrula:90%-epiboly"	"frozen_sample"
+"Tue-9h-2.CEL"	"Tue-9h-2.CHP"	"Tue-9h-2.EXP"	"Zebrafish.cdf"	"A-AFFY-38"	4009543	"P-TABM-55"	"P-TABM-56"	"P-AFFY-2"	"Danio rerio Tuebingen wildtype_9h-2"	"whole_organism"	"Tuebingen"	"wild_type"	"Danio rerio"	"9h"	9	"hours"	"fertilization"	"Gastrula:90%-epiboly"	"Gastrula:90%-epiboly"	"frozen_sample"
diff --git a/examples/real/E-TABM-35.png b/examples/real/E-TABM-35.png
new file mode 100644
index 0000000..b46deea
Binary files /dev/null and b/examples/real/E-TABM-35.png differ
diff --git a/examples/real/E-TABM-35.txt b/examples/real/E-TABM-35.txt
new file mode 100755
index 0000000..3edc589
--- /dev/null
+++ b/examples/real/E-TABM-35.txt
@@ -0,0 +1,94 @@
+Experiment section   	
+domain	ebi.ac.uk
+accession	E-TABM-35
+quality_control	
+experiment_design_type	"strain_or_line_design, comparative_genome_hybridization_design"
+name	RML C. burnetii CGH analysis
+description	Analysis of genetic diversity of 25 Coxiella burnetii isolates with relative to the Nine Mile (RSA493) reference isolate.
+release_date	2006-06-21
+submission_date	
+submitter	Paul Beare
+organization	"Rocky Mountain Laboratories, Laboratory of Intracellular Parasites, National Institute of Allergy and Infectious Diseases, National Institute of Health"
+publication_title	Genetic Diversity of the Q Fever agent Coxiella burnetii Assessed by Microarray-based Whole Genome Comparisons 
+authors	"P. A. Beare; J. E. Samuel; D. Howe; K. Virtaneva; S. F. Porcella; R. A. Heinzen"
+journal	submitted to Genome Research
+volume	
+issue	
+pages			
+year			
+
+Protocol section			
+accession	text	name	type
+P-TABM-61	C. burnetii isolates were propagated in embryonated hen�s eggs for 8 days.	egg propagation	grow
+P-TABM-62	C. burnetii isolates were propagated in African green monkey kidney (Vero) fibroblasts (CCL-81; American Type Culture Collection) grown in RPMI medium (Invitrogen) supplemented with 2% fetal bovine serum for 14 days.	fibroblast propagation	grow
+P-TABM-63	"Organisms were purified from infected cells by Renografin or sucrose density gradient centrifugation. Total DNA was extracted using the UltraClean microbial DNA isolation kit (Mo Bio Laboratories Inc) as recommended by the supplier, with an additional heating step (85�C for 10 min) before physical disruption of the bacterial cells. "	method A	nucleic_acid_extraction
+P-TABM-64	"Organisms were purified from infected cells by Renografin or sucrose density gradient centrifugation. Purified C. burnetii organisms (60 to 70 mg, dry weight) were suspended in 50 mM NaCl-20 mM Tris (pH 9.1) and incubated for 60 min at 37 degrees celsius with 100ug of thermolysin (10). Lysis was completed by adding sodium dodecyl sulfate (SDS) to a 1% final concentration. The plasmid DNA in this lysate was separated from chromosomal DNA by cesium chloride-ethidium bromide (CsC [...]
+P-TABM-65	"Total DNA was extracted from crude samples using the UltraClean microbial DNA isolation kit (Mo Bio Laboratories Inc) as recommended by the supplier, with an additional heating step (85�C for 10 min) before physical disruption of the bacterial cells. gDNA was resuspended in 80 ul of distilled H2O, and quantified using a Beckman Du 530 spectrophotometer (Beckman Coulter, Inc.)."	method C	nucleic_acid_extraction
+P-TABM-66	"Total DNA was extracted from crude samples using the UltraClean microbial DNA isolation kit (Mo Bio Laboratories Inc) as recommended by the supplier, with an additional heating step (85�C for 10 min) before physical disruption of the bacterial cells. Whole genome amplification (WGA) was conducted on total DNA from 4 ul of a infected yolk sac or 4 ul of a infected guinea pig spleen homogenate . Amplifications employed a GenomiPhi DNA Amplification Kit (Amersham Biosciences). Sp [...]
+P-TABM-67	"C. burnetii replicative vacuoles where harvested from infected Vero cells on 12-mm glass coverslips by micromanipulation.  At 5 days post-infection, coverslips were transferred to a 90 cm diameter Petri dish containing approximately 10 ml of RPMI medium.  Individual vacuoles were extracted from infected monolayers using a Micromanipulator 5171 (Eppendorf, Westbury, N.Y.), a CellTram vario manual microinjector (Eppendorf), and TransferTips MDS (Eppendorf).  The micromanipulator [...]
+P-TABM-68	"1. Concentrate gDNA samples (Total DNA from purified gDNA (7 ug), gDNA from infected yolk sacs (10 ug), or WGA DNA from purified gDNA (7 ug), C. burnetii vacuoles (7 ug), infected yolk sacs (10 ug) or infected guinea pig spleen homogenates (30 ug)) down to 40 uL at room temperature under vacuum in a DNA 110 speedvac (Thermo Savant). Place samples on ice. 2. Add 5 ul of DNaseI 10x buffer 3. Add 5 ul of 0.01 ul/ug of DNaseI (Roche Diagnostics Corp.) 4. Incubate samples at 37 deg [...]
+P-TABM-69	"The difference in intensity value for each probe pair was calculated by subtracting the MM probe intensity value from the PM probe intensity value. The average difference in intensity values for the entire 16 probe pairs for each ORF was calculated using the Statistical Expression Algorithm incorporated into GCOS. Normalization between arrays was achieved by implementing array-specific scaling factors that were calculated as a ratio of 500 divided by the average of the average [...]
+
+Hybridization section			
+BioSource	BioSourceMaterial	BioMaterialCharacteristics[Organism]	BioMaterialCharacteristics[StrainOrLine]	Protocol[grow]	Sample	Protocol[extraction]	Extract	ExtractMaterial	Protocol[labeling]	LabeledExtract	LabeledExtractMaterial	File[raw]	File[normalized]	File[exp]	File[cdf]	Array[accession]	FactorValue[StrainOrLine]	Bioassay data transformation
+Dugway 5G61-63	whole_organism	Coxiella burnetii	Dugway 5G61-63	P-TABM-62	Dugway 5G61-63 A	P-TABM-63	Dugway 5G61-63 A	genomic_DNA	P-TABM-68	Dugway 5G61-63 A	synthetic_DNA	PAB-1-Cburnetti-Dugway 5G61-63 A.CEL	PAB-1-Cburnetti-Dugway 5G61-63 A.CHP	PAB-1-Cburnetti-Dugway 5G61-63 A.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	Dugway 5G61-63	P-TABM-69
+Dugway 5G61-63	whole_organism	Coxiella burnetii	Dugway 5G61-63	P-TABM-62	Dugway 5G61-63 B	P-TABM-63	Dugway 5G61-63 B	genomic_DNA	P-TABM-68	Dugway 5G61-63 B	synthetic_DNA	PAB-1-Cburnetti-Dugway 5G61-63 B.CEL	PAB-1-Cburnetti-Dugway 5G61-63 B.CHP	PAB-1-Cburnetti-Dugway 5G61-63 B.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	Dugway 5G61-63	P-TABM-69
+Dugway 7E9-12	whole_organism	Coxiella burnetii	Dugway 7E9-12	P-TABM-61	Dugway 7E9-12 A	P-TABM-64	Dugway 7E9-12 A	genomic_DNA	P-TABM-68	Dugway 7E9-12 A	synthetic_DNA	PAB-1-Cburnetti-Dugway 7E9-12 A.CEL	PAB-1-Cburnetti-Dugway 7E9-12 A.CHP	PAB-1-Cburnetti-Dugway 7E9-12 A.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	Dugway 7E9-12	P-TABM-69
+Dugway 7E9-12	whole_organism	Coxiella burnetii	Dugway 7E9-12	P-TABM-61	Dugway 7E9-12 B	P-TABM-64	Dugway 7E9-12 B	genomic_DNA	P-TABM-68	Dugway 7E9-12 B	synthetic_DNA	PAB-1-Cburnetti-Dugway 7E9-12 B.CEL	PAB-1-Cburnetti-Dugway 7E9-12 B.CHP	PAB-1-Cburnetti-Dugway 7E9-12 B.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	Dugway 7E9-12	P-TABM-69
+African	whole_organism	Coxiella burnetii	African	P-TABM-61	African A	P-TABM-64	African A	genomic_DNA	P-TABM-68	African A	synthetic_DNA	PAB-1-Cburnetti-African A.CEL	PAB-1-Cburnetti-African A.CHP	PAB-1-Cburnetti-African A.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	African	P-TABM-69
+African	whole_organism	Coxiella burnetii	African	P-TABM-61	African B	P-TABM-64	African B	genomic_DNA	P-TABM-68	African B	synthetic_DNA	PAB-1-Cburnetti-African B.CEL	PAB-1-Cburnetti-African B.CHP	PAB-1-Cburnetti-African B.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	African	P-TABM-69
+African	whole_organism	Coxiella burnetii	African	P-TABM-61	African C	P-TABM-64	African C	genomic_DNA	P-TABM-68	African C	synthetic_DNA	PAB-1-Cburnetti-African C.CEL	PAB-1-Cburnetti-African C.CHP	PAB-1-Cburnetti-African C.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	African	P-TABM-69
+Australia	whole_organism	Coxiella burnetii	Australian	P-TABM-62	Australian A	P-TABM-63	Australian A	genomic_DNA	P-TABM-68	Australian A	synthetic_DNA	PAB-1-Cburnetti-Australian A.CEL	PAB-1-Cburnetti-Australian A.CHP	PAB-1-Cburnetti-Australian A.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	Australian 	P-TABM-69
+Australia	whole_organism	Coxiella burnetii	Australian	P-TABM-62	Australian B	P-TABM-63	Australian B	genomic_DNA	P-TABM-68	Australian B	synthetic_DNA	PAB-1-Cburnetti-Australian B.CEL	PAB-1-Cburnetti-Australian B.CHP	PAB-1-Cburnetti-Australian B.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	Australian 	P-TABM-69
+BDT	whole_organism	Coxiella burnetii	BDT	P-TABM-62	BDT A	P-TABM-66	BDT A	genomic_DNA	P-TABM-68	BDT A	synthetic_DNA	PAB-1-Cburnetti-BDT A.CEL	PAB-1-Cburnetti-BDT A.CHP	PAB-1-Cburnetti-BDT A.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	BDT	P-TABM-69
+BDT	whole_organism	Coxiella burnetii	BDT	P-TABM-62	BDT B	P-TABM-66	BDT B	genomic_DNA	P-TABM-68	BDT B	synthetic_DNA	PAB-1-Cburnetti-BDT B.CEL	PAB-1-Cburnetti-BDT B.CHP	PAB-1-Cburnetti-BDT B.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	BDT	P-TABM-69
+California 16	whole_organism	Coxiella burnetii	California 16	P-TABM-61	California 16	P-TABM-64	California 16	genomic_DNA	P-TABM-68	California 16	synthetic_DNA	PAB-1-Cburnetti-California 16.CEL	PAB-1-Cburnetti-California 16.CHP	PAB-1-Cburnetti-California 16.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	California 16	P-TABM-69
+California 33	whole_organism	Coxiella burnetii	California 33	P-TABM-61	California 33	P-TABM-64	California 33	genomic_DNA	P-TABM-68	California 33	synthetic_DNA	PAB-1-Cburnetti-California 33.CEL	PAB-1-Cburnetti-California 33.CHP	PAB-1-Cburnetti-California 33.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	California 33	P-TABM-69
+Crazy	whole_organism	Coxiella burnetii	Crazy	P-TABM-62	Crazy A	P-TABM-63	Crazy A	genomic_DNA	P-TABM-68	Crazy A	synthetic_DNA	PAB-1-Cburnetti-Crazy A.CEL	PAB-1-Cburnetti-Crazy A.CHP	PAB-1-Cburnetti-Crazy A.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	Crazy	P-TABM-69
+Crazy	whole_organism	Coxiella burnetii	Crazy	P-TABM-62	Crazy B	P-TABM-63	Crazy B	genomic_DNA	P-TABM-68	Crazy B	synthetic_DNA	PAB-1-Cburnetti-Crazy B.CEL	PAB-1-Cburnetti-Crazy B.CHP	PAB-1-Cburnetti-Crazy B.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	Crazy	P-TABM-69
+D	whole_organism	Coxiella burnetii	D	P-TABM-61	D A	P-TABM-64	D A	genomic_DNA	P-TABM-68	D A	synthetic_DNA	PAB-1-Cburnetti-D A.CEL	PAB-1-Cburnetti-D A.CHP	PAB-1-Cburnetti-D A.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	D	P-TABM-69
+D	whole_organism	Coxiella burnetii	D	P-TABM-61	D B	P-TABM-64	D B	genomic_DNA	P-TABM-68	D B	synthetic_DNA	PAB-1-Cburnetti-D B.CEL	PAB-1-Cburnetti-D B.CHP	PAB-1-Cburnetti-D B.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	D	P-TABM-69
+El Tayeb	whole_organism	Coxiella burnetii	El Tayeb	P-TABM-61	El Tayeb A	P-TABM-64	El Tayeb A	genomic_DNA	P-TABM-68	El Tayeb A	synthetic_DNA	PAB-1-Cburnetti-El Tayeb A.CEL	PAB-1-Cburnetti-El Tayeb A.CHP	PAB-1-Cburnetti-El Tayeb A.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	El Tayeb	P-TABM-69
+El Tayeb	whole_organism	Coxiella burnetii	El Tayeb	P-TABM-61	El Tayeb B	P-TABM-64	El Tayeb B	genomic_DNA	P-TABM-68	El Tayeb B	synthetic_DNA	PAB-1-Cburnetti-El Tayeb B.CEL	PAB-1-Cburnetti-El Tayeb B.CHP	PAB-1-Cburnetti-El Tayeb B.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	El Tayeb	P-TABM-69
+G	whole_organism	Coxiella burnetii	G	P-TABM-62	G A	P-TABM-63	G A	genomic_DNA	P-TABM-68	G A	synthetic_DNA	PAB-1-Cburnetti-G A.CEL	PAB-1-Cburnetti-G A.CHP	PAB-1-Cburnetti-G A.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	G	P-TABM-69
+G	whole_organism	Coxiella burnetii	G	P-TABM-62	G B	P-TABM-63	G B	genomic_DNA	P-TABM-68	G B	synthetic_DNA	PAB-1-Cburnetti-G B.CEL	PAB-1-Cburnetti-G B.CHP	PAB-1-Cburnetti-G B.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	G	P-TABM-69
+G	whole_organism	Coxiella burnetii	G	P-TABM-61	G C	P-TABM-65	G C	genomic_DNA	P-TABM-68	G C	synthetic_DNA	PAB-1-Cburnetti-G C.CEL	PAB-1-Cburnetti-G C.CHP	PAB-1-Cburnetti-G C.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	G	P-TABM-69
+G	whole_organism	Coxiella burnetii	G	P-TABM-61	G D	P-TABM-66	G D	genomic_DNA	P-TABM-68	G D	synthetic_DNA	PAB-1-Cburnetti-G D.CEL	PAB-1-Cburnetti-G D.CHP	PAB-1-Cburnetti-G D.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	G	P-TABM-69
+Giroud	whole_organism	Coxiella burnetii	Giroud	P-TABM-61	Giroud A	P-TABM-64	Giroud A	genomic_DNA	P-TABM-68	Giroud A	synthetic_DNA	PAB-1-Cburnetti-Giroud A.CEL	PAB-1-Cburnetti-Giroud A.CHP	PAB-1-Cburnetti-Giroud A.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	Giroud	P-TABM-69
+Giroud	whole_organism	Coxiella burnetii	Giroud	P-TABM-61	Giroud B	P-TABM-64	Giroud B	genomic_DNA	P-TABM-68	Giroud B	synthetic_DNA	PAB-1-Cburnetti-Giroud B.CEL	PAB-1-Cburnetti-Giroud B.CHP	PAB-1-Cburnetti-Giroud B.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	Giroud	P-TABM-69
+He	whole_organism	Coxiella burnetii	He	P-TABM-61	He A	P-TABM-64	He A	genomic_DNA	P-TABM-68	He A	synthetic_DNA	PAB-1-Cburnetti-He A.CEL	PAB-1-Cburnetti-He A.CHP	PAB-1-Cburnetti-He A.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	He 	P-TABM-69
+He	whole_organism	Coxiella burnetii	He	P-TABM-61	He B	P-TABM-64	He B	genomic_DNA	P-TABM-68	He B	synthetic_DNA	PAB-1-Cburnetti-He B.CEL	PAB-1-Cburnetti-He B.CHP	PAB-1-Cburnetti-He B.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	He 	P-TABM-69
+Idaho	whole_organism	Coxiella burnetii	Idaho	P-TABM-62	Idaho A	P-TABM-63	Idaho A	genomic_DNA	P-TABM-68	Idaho A	synthetic_DNA	PAB-1-Cburnetti-Idaho A.CEL	PAB-1-Cburnetti-Idaho A.CHP	PAB-1-Cburnetti-Idaho A.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	Idaho	P-TABM-69
+Idaho	whole_organism	Coxiella burnetii	Idaho	P-TABM-62	Idaho B	P-TABM-63	Idaho B	genomic_DNA	P-TABM-68	Idaho B	synthetic_DNA	PAB-1-Cburnetti-Idaho B.CEL	PAB-1-Cburnetti-Idaho B.CHP	PAB-1-Cburnetti-Idaho B.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	Idaho	P-TABM-69
+K	whole_organism	Coxiella burnetii	K	P-TABM-61	K A	P-TABM-64	K A	genomic_DNA	P-TABM-68	K A	synthetic_DNA	PAB-1-Cburnetti-K A.CEL	PAB-1-Cburnetti-K A.CHP	PAB-1-Cburnetti-K A.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	K	P-TABM-69
+K	whole_organism	Coxiella burnetii	K	P-TABM-61	K B	P-TABM-64	K B	genomic_DNA	P-TABM-68	K B	synthetic_DNA	PAB-1-Cburnetti-K B.CEL	PAB-1-Cburnetti-K B.CHP	PAB-1-Cburnetti-K B.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	K	P-TABM-69
+K	whole_organism	Coxiella burnetii	K	P-TABM-61	K C	P-TABM-64	K C	genomic_DNA	P-TABM-68	K C	synthetic_DNA	PAB-1-Cburnetti-K C.CEL	PAB-1-Cburnetti-K C.CHP	PAB-1-Cburnetti-K C.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	K	P-TABM-69
+KO	whole_organism	Coxiella burnetii	KO	P-TABM-62	KO A	P-TABM-63	KO A	genomic_DNA	P-TABM-68	KO A	synthetic_DNA	PAB-1-Cburnetti-KO A.CEL	PAB-1-Cburnetti-KO A.CHP	PAB-1-Cburnetti-KO A.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	KO	P-TABM-69
+KO	whole_organism	Coxiella burnetii	KO	P-TABM-62	KO B	P-TABM-63	KO B	genomic_DNA	P-TABM-68	KO B	synthetic_DNA	PAB-1-Cburnetti-KO B.CEL	PAB-1-Cburnetti-KO B.CHP	PAB-1-Cburnetti-KO B.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	KO	P-TABM-69
+KO	whole_organism	Coxiella burnetii	KO	P-TABM-62	KO C	P-TABM-63	KO C	genomic_DNA	P-TABM-68	KO C	synthetic_DNA	PAB-1-Cburnetti-KO C.CEL	PAB-1-Cburnetti-KO C.CHP	PAB-1-Cburnetti-KO C.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	KO	P-TABM-69
+Le B	whole_organism	Coxiella burnetii	Le B	P-TABM-62	Le B A	P-TABM-63	Le B A	genomic_DNA	P-TABM-68	Le B A	synthetic_DNA	PAB-1-Cburnetti-LeB A.CEL	PAB-1-Cburnetti-LeB A.CHP	PAB-1-Cburnetti-LeB A.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	Le B	P-TABM-69
+Le B	whole_organism	Coxiella burnetii	Le B	P-TABM-62	Le B B	P-TABM-63	Le B B	genomic_DNA	P-TABM-68	Le B B	synthetic_DNA	PAB-1-Cburnetti-LeB B.CEL	PAB-1-Cburnetti-LeB B.CHP	PAB-1-Cburnetti-LeB B.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	Le B	P-TABM-69
+MSU	whole_organism	Coxiella burnetii	MSU	P-TABM-62	MSU A	P-TABM-63	MSU A	genomic_DNA	P-TABM-68	MSU A	synthetic_DNA	PAB-1-Cburnetti-MSU A.CEL	PAB-1-Cburnetti-MSU A.CHP	PAB-1-Cburnetti-MSU A.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	MSU	P-TABM-69
+MSU	whole_organism	Coxiella burnetii	MSU	P-TABM-62	MSU B	P-TABM-63	MSU B	genomic_DNA	P-TABM-68	MSU B	synthetic_DNA	PAB-1-Cburnetti-MSU B.CEL	PAB-1-Cburnetti-MSU B.CHP	PAB-1-Cburnetti-MSU B.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	MSU	P-TABM-69
+MSU	whole_organism	Coxiella burnetii	MSU	P-TABM-62	MSU C	P-TABM-63	MSU C	genomic_DNA	P-TABM-68	MSU C	synthetic_DNA	PAB-1-Cburnetti-MSU C.CEL	PAB-1-Cburnetti-MSU C.CHP	PAB-1-Cburnetti-MSU C.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	MSU	P-TABM-69
+NMI	whole_organism	Coxiella burnetii	NMI	P-TABM-62	NMI A	P-TABM-63	NMI A	genomic_DNA	P-TABM-68	NMI A	synthetic_DNA	PAB-1-Cburnetti-NMI A.CEL	PAB-1-Cburnetti-NMI A.CHP	PAB-1-Cburnetti-NMI A.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	NMI	P-TABM-69
+NMI	whole_organism	Coxiella burnetii	NMI	P-TABM-62	NMI B	P-TABM-63	NMI B	genomic_DNA	P-TABM-68	NMI B	synthetic_DNA	PAB-1-Cburnetti-NMI B.CEL	PAB-1-Cburnetti-NMI B.CHP	PAB-1-Cburnetti-NMI B.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	NMI	P-TABM-69
+NMI	whole_organism	Coxiella burnetii	NMI	P-TABM-62	NMI C	P-TABM-63	NMI C	genomic_DNA	P-TABM-68	NMI C	synthetic_DNA	PAB-1-Cburnetti-NMI C.CEL	PAB-1-Cburnetti-NMI C.CHP	PAB-1-Cburnetti-NMI C.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	NMI	P-TABM-69
+NMI	whole_organism	Coxiella burnetii	NMI	P-TABM-62	NMI D	P-TABM-63	NMI D	genomic_DNA	P-TABM-68	NMI D	synthetic_DNA	PAB-1-Cburnetti-NMI D.CEL	PAB-1-Cburnetti-NMI D.CHP	PAB-1-Cburnetti-NMI D.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	NMI	P-TABM-69
+NMII	whole_organism	Coxiella burnetii	NMII	P-TABM-62	NMII A	P-TABM-63	NMII A	genomic_DNA	P-TABM-68	NMII A	synthetic_DNA	PAB-1-Cburnetti-NMII A.CEL	PAB-1-Cburnetti-NMII A.CHP	PAB-1-Cburnetti-NMII A.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	NMII	P-TABM-69
+NMII	whole_organism	Coxiella burnetii	NMII	P-TABM-62	NMII B	P-TABM-67	NMII B	genomic_DNA	P-TABM-68	NMII B	synthetic_DNA	PAB-1-Cburnetti-NMII B.CEL	PAB-1-Cburnetti-NMII B.CHP	PAB-1-Cburnetti-NMII B.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	NMII	P-TABM-69
+Ohio	whole_organism	Coxiella burnetii	Ohio	P-TABM-61	Ohio A	P-TABM-64	Ohio A	genomic_DNA	P-TABM-68	Ohio A	synthetic_DNA	PAB-1-Cburnetti-Ohio A.CEL	PAB-1-Cburnetti-Ohio A.CHP	PAB-1-Cburnetti-Ohio A.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	Ohio	P-TABM-69
+Ohio	whole_organism	Coxiella burnetii	Ohio	P-TABM-61	Ohio B	P-TABM-64	Ohio B	genomic_DNA	P-TABM-68	Ohio B	synthetic_DNA	PAB-1-Cburnetti-Ohio B.CEL	PAB-1-Cburnetti-Ohio B.CHP	PAB-1-Cburnetti-Ohio B.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	Ohio	P-TABM-69
+P	whole_organism	Coxiella burnetii	P	P-TABM-61	P A	P-TABM-64	P A	genomic_DNA	P-TABM-68	P A	synthetic_DNA	PAB-1-Cburnetti-P A.CEL	PAB-1-Cburnetti-P A.CHP	PAB-1-Cburnetti-P A.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	P	P-TABM-69
+P	whole_organism	Coxiella burnetii	P	P-TABM-61	P B	P-TABM-64	P B	genomic_DNA	P-TABM-68	P B	synthetic_DNA	PAB-1-Cburnetti-P B.CEL	PAB-1-Cburnetti-P B.CHP	PAB-1-Cburnetti-P B.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	P	P-TABM-69
+P	whole_organism	Coxiella burnetii	P	P-TABM-61	P C	P-TABM-64	P C	genomic_DNA	P-TABM-68	P C	synthetic_DNA	PAB-1-Cburnetti-P C.CEL	PAB-1-Cburnetti-P C.CHP	PAB-1-Cburnetti-P C.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	P	P-TABM-69
+Panama	whole_organism	Coxiella burnetii	Panama	P-TABM-61	Panama A	P-TABM-64	Panama A	genomic_DNA	P-TABM-68	Panama A	synthetic_DNA	PAB-1-Cburnetti-Panama A.CEL	PAB-1-Cburnetti-Panama A.CHP	PAB-1-Cburnetti-Panama A.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	Panama	P-TABM-69
+Panama	whole_organism	Coxiella burnetii	Panama	P-TABM-61	Panama B	P-TABM-64	Panama B	genomic_DNA	P-TABM-68	Panama B	synthetic_DNA	PAB-1-Cburnetti-Panama B.CEL	PAB-1-Cburnetti-Panama B.CHP	PAB-1-Cburnetti-Panama B.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	Panama	P-TABM-69
+Q321	whole_organism	Coxiella burnetii	Q321	P-TABM-62	Q321 A	P-TABM-63	Q321 A	genomic_DNA	P-TABM-68	Q321 A	synthetic_DNA	PAB-1-Cburnetti-Q321 A.CEL	PAB-1-Cburnetti-Q321 A.CHP	PAB-1-Cburnetti-Q321 A.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	Q321	P-TABM-69
+Q321	whole_organism	Coxiella burnetii	Q321	P-TABM-62	Q321 B	P-TABM-63	Q321 B	genomic_DNA	P-TABM-68	Q321 B	synthetic_DNA	PAB-1-Cburnetti-Q321 B.CEL	PAB-1-Cburnetti-Q321 B.CHP	PAB-1-Cburnetti-Q321 B.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	Q321	P-TABM-69
+Q321	whole_organism	Coxiella burnetii	Q321	P-TABM-62	Q321 C	P-TABM-63	Q321 C	genomic_DNA	P-TABM-68	Q321 C	synthetic_DNA	PAB-1-Cburnetti-Q321 C.CEL	PAB-1-Cburnetti-Q321 C.CHP	PAB-1-Cburnetti-Q321 C.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	Q321	P-TABM-69
+S	whole_organism	Coxiella burnetii	S	P-TABM-62	S A	P-TABM-63	S A	genomic_DNA	P-TABM-68	S A	synthetic_DNA	PAB-1-Cburnetti-S A.CEL	PAB-1-Cburnetti-S A.CHP	PAB-1-Cburnetti-S A.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	S	P-TABM-69
+S	whole_organism	Coxiella burnetii	S	P-TABM-62	S B	P-TABM-63	S B	genomic_DNA	P-TABM-68	S B	synthetic_DNA	PAB-1-Cburnetti-S B.CEL	PAB-1-Cburnetti-S B.CHP	PAB-1-Cburnetti-S B.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	S	P-TABM-69
+S	whole_organism	Coxiella burnetii	S	P-TABM-62	S C	P-TABM-63	S C	genomic_DNA	P-TABM-68	S C	synthetic_DNA	PAB-1-Cburnetti-S C.CEL	PAB-1-Cburnetti-S C.CHP	PAB-1-Cburnetti-S C.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	S	P-TABM-69
+Turkey	whole_organism	Coxiella burnetii	Turkey	P-TABM-61	Turkey A	P-TABM-64	Turkey A	genomic_DNA	P-TABM-68	Turkey A	synthetic_DNA	PAB-1-Cburnetti-Turkey A.CEL	PAB-1-Cburnetti-Turkey A.CHP	PAB-1-Cburnetti-Turkey A.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	Turkey	P-TABM-69
+Turkey	whole_organism	Coxiella burnetii	Turkey	P-TABM-61	Turkey B	P-TABM-64	Turkey B	genomic_DNA	P-TABM-68	Turkey B	synthetic_DNA	PAB-1-Cburnetti-Turkey B.CEL	PAB-1-Cburnetti-Turkey B.CHP	PAB-1-Cburnetti-Turkey B.EXP	/net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF	A-AFFY-48	Turkey	P-TABM-69
diff --git a/examples/real/E-TABM-54.png b/examples/real/E-TABM-54.png
new file mode 100644
index 0000000..8d91b05
Binary files /dev/null and b/examples/real/E-TABM-54.png differ
diff --git a/examples/real/E-TABM-54.txt b/examples/real/E-TABM-54.txt
new file mode 100755
index 0000000..e21d985
--- /dev/null
+++ b/examples/real/E-TABM-54.txt
@@ -0,0 +1,287 @@
+Experiment section																									
+accession	E-TABM-54																								
+domain	www.stanford.edu																								
+experiment_design_type	strain_or_line_design;comparative_genome_hybridization																								
+name	Bordetella pertussis comparative genome hybridization																								
+description	Comparative genome hybridization (CGH) of sample of 137 Bordetella pertussis strains																								
+release_date	2005-12-31																								
+submitter	Craig Cummings																								
+submitter_email	cummings at pmgm2.stanford.edu																								
+quality_control	technical_replicate; biological_replicate																								
+
+Hybridization section																									
+BioMaterialCharacteristics[Organism]	BioMaterialCharacteristics[StrainOrLine]	BioSource	BioSourceMaterial	SampleMaterial	ExtractMaterial	Sample	Extract	LabeledExtract	LabeledExtractMaterial	Dye	Protocol[grow]	Protocol[extraction]	Protocol[pool]	Protocol[labeling]	Protocol[hybridization]	Protocol[scanning]	Protocol[normalization]	FactorValue[StrainOrLine]	FactorValue[clinical_information]	File[raw]	File[normalized]	Array[accession]	Array[serial]	Hybridization	Normalization
+Bordetella pertussis	Bpe60	Bpe60	whole_organism	whole_organism	genomic_DNA	Bpe60	Bpe60	Bpe60	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe60	Tohama I laboratory strain	b23n061.txt	b23n061_norm.txt	A-MEXP-234	b23n061	b23n061	b23n061
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe60	Tohama I laboratory strain	b23n061.txt	b23n061_norm.txt	A-MEXP-234	b23n061	b23n061	b23n061
+Bordetella pertussis	Bpe317	Bpe317	whole_organism	whole_organism	genomic_DNA	Bpe317	Bpe317	Bpe317	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe317	GMT-1 recent lab strain	b29n119.txt	b29n119_norm.txt	A-MEXP-236	b29n119	b29n119	b29n119
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe317	GMT-1 recent lab strain	b29n119.txt	b29n119_norm.txt	A-MEXP-236	b29n119	b29n119	b29n119
+Bordetella pertussis	Bpe55	Bpe55	whole_organism	whole_organism	genomic_DNA	Bpe55	Bpe55	Bpe55	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe55	Tohama II laboratory strain	b25n048.txt	b25n048_norm.txt	A-MEXP-234	b25n048	b25n048	b25n048
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe55	Tohama II laboratory strain	b25n048.txt	b25n048_norm.txt	A-MEXP-234	b25n048	b25n048	b25n048
+Bordetella pertussis	Bpe243	Bpe243	whole_organism	whole_organism	genomic_DNA	Bpe243	Bpe243	Bpe243	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe243	US pre-vaccine	b29n112.txt	b29n112_norm.txt	A-MEXP-236	b29n112	b29n112	b29n112
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe243	US pre-vaccine	b29n112.txt	b29n112_norm.txt	A-MEXP-236	b29n112	b29n112	b29n112
+Bordetella pertussis	Bpe244	Bpe244	whole_organism	whole_organism	genomic_DNA	Bpe244	Bpe244	Bpe244	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe244	US pre-vaccine	b29n113.txt	b29n113_norm.txt	A-MEXP-236	b29n113	b29n113	b29n113
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe244	US pre-vaccine	b29n113.txt	b29n113_norm.txt	A-MEXP-236	b29n113	b29n113	b29n113
+Bordetella pertussis	Bpe245	Bpe245	whole_organism	whole_organism	genomic_DNA	Bpe245	Bpe245	Bpe245	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe245	US pre-vaccine	b29n114.txt	b29n114_norm.txt	A-MEXP-236	b29n114	b29n114	b29n114
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe245	US pre-vaccine	b29n114.txt	b29n114_norm.txt	A-MEXP-236	b29n114	b29n114	b29n114
+Bordetella pertussis	Bpe247	Bpe247	whole_organism	whole_organism	genomic_DNA	Bpe247	Bpe247	Bpe247	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe247	US pre-vaccine	b29n116.txt	b29n116_norm.txt	A-MEXP-236	b29n116	b29n116	b29n116
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe247	US pre-vaccine	b29n116.txt	b29n116_norm.txt	A-MEXP-236	b29n116	b29n116	b29n116
+Bordetella pertussis	Bpe41	Bpe41	whole_organism	whole_organism	genomic_DNA	Bpe41	Bpe41	Bpe41	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe41	US Cincinnati outbreak	b23n124.txt	b23n124_norm.txt	A-MEXP-234	b23n124	b23n124	b23n124
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe41	US Cincinnati outbreak	b23n124.txt	b23n124_norm.txt	A-MEXP-234	b23n124	b23n124	b23n124
+Bordetella pertussis	Bpe51	Bpe51	whole_organism	whole_organism	genomic_DNA	Bpe51	Bpe51	Bpe51	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe51	US Cincinnati outbreak	b24n122.txt	b24n122_norm.txt	A-MEXP-234	b24n122	b24n122	b24n122
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe51	US Cincinnati outbreak	b24n122.txt	b24n122_norm.txt	A-MEXP-234	b24n122	b24n122	b24n122
+Bordetella pertussis	Bpe43	Bpe43	whole_organism	whole_organism	genomic_DNA	Bpe43	Bpe43	Bpe43	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe43	US Cincinnati outbreak	b24n016.txt	b24n016_norm.txt	A-MEXP-234	b24n016	b24n016	b24n016
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe43	US Cincinnati outbreak	b24n016.txt	b24n016_norm.txt	A-MEXP-234	b24n016	b24n016	b24n016
+vBordetella pertussis	Bpe44	Bpe44	whole_organism	whole_organism	genomic_DNA	Bpe44	Bpe44	Bpe44	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe44	US Cincinnati outbreak	b24n017.txt	b24n017_norm.txt	A-MEXP-234	b24n017	b24n017	b24n017
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe44	US Cincinnati outbreak	b24n017.txt	b24n017_norm.txt	A-MEXP-234	b24n017	b24n017	b24n017
+Bordetella pertussis	Bpe48	Bpe48	whole_organism	whole_organism	genomic_DNA	Bpe48	Bpe48	Bpe48	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe48	US Cincinnati outbreak	b24n020.txt	b24n020_norm.txt	A-MEXP-234	b24n020	b24n020	b24n020
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe48	US Cincinnati outbreak	b24n020.txt	b24n020_norm.txt	A-MEXP-234	b24n020	b24n020	b24n020
+Bordetella pertussis	Bpe50	Bpe50	whole_organism	whole_organism	genomic_DNA	Bpe50	Bpe50	Bpe50	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe50	US Cincinnati non-outbreak	b29n105.txt	b29n105_norm.txt	A-MEXP-236	b29n105	b29n105	b29n105
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe50	US Cincinnati non-outbreak	b29n105.txt	b29n105_norm.txt	A-MEXP-236	b29n105	b29n105	b29n105
+Bordetella pertussis	Bpe52	Bpe52	whole_organism	whole_organism	genomic_DNA	Bpe52	Bpe52	Bpe52	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe52	US Cincinnati non-outbreak	b24n052.txt	b24n052_norm.txt	A-MEXP-234	b24n052	b24n052	b24n052
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe52	US Cincinnati non-outbreak	b24n052.txt	b24n052_norm.txt	A-MEXP-234	b24n052	b24n052	b24n052
+Bordetella pertussis	Bpe37	Bpe37	whole_organism	whole_organism	genomic_DNA	Bpe37	Bpe37	Bpe37	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe37	US Cincinnati non-outbreak	b24n054.txt	b24n054_norm.txt	A-MEXP-234	b24n054	b24n054	b24n054
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe37	US Cincinnati non-outbreak	b24n054.txt	b24n054_norm.txt	A-MEXP-234	b24n054	b24n054	b24n054
+Bordetella pertussis	Bpe33	Bpe33	whole_organism	whole_organism	genomic_DNA	Bpe33	Bpe33	Bpe33	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe33	US Cincinnati non-outbreak	b24n113.txt	b24n113_norm.txt	A-MEXP-234	b24n113	b24n113	b24n113
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe33	US Cincinnati non-outbreak	b24n113.txt	b24n113_norm.txt	A-MEXP-234	b24n113	b24n113	b24n113
+Bordetella pertussis	Bpe34	Bpe34	whole_organism	whole_organism	genomic_DNA	Bpe34	Bpe34	Bpe34	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe34	US Cincinnati non-outbreak	b24n114.txt	b24n114_norm.txt	A-MEXP-234	b24n114	b24n114	b24n114
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe34	US Cincinnati non-outbreak	b24n114.txt	b24n114_norm.txt	A-MEXP-234	b24n114	b24n114	b24n114
+Bordetella pertussis	Bpe35	Bpe35	whole_organism	whole_organism	genomic_DNA	Bpe35	Bpe35	Bpe35	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe35	US Cincinnati non-outbreak	b24n115.txt	b24n115_norm.txt	A-MEXP-234	b24n115	b24n115	b24n115
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe35	US Cincinnati non-outbreak	b24n115.txt	b24n115_norm.txt	A-MEXP-234	b24n115	b24n115	b24n115
+Bordetella pertussis	Bpe36	Bpe36	whole_organism	whole_organism	genomic_DNA	Bpe36	Bpe36	Bpe36	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe36	US Cincinnati non-outbreak	b24n116.txt	b24n116_norm.txt	A-MEXP-234	b24n116	b24n116	b24n116
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe36	US Cincinnati non-outbreak	b24n116.txt	b24n116_norm.txt	A-MEXP-234	b24n116	b24n116	b24n116
+Bordetella pertussis	Bpe38	Bpe38	whole_organism	whole_organism	genomic_DNA	Bpe38	Bpe38	Bpe38	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe38	US Cincinnati non-outbreak	b24n117.txt	b24n117_norm.txt	A-MEXP-234	b24n117	b24n117	b24n117
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe38	US Cincinnati non-outbreak	b24n117.txt	b24n117_norm.txt	A-MEXP-234	b24n117	b24n117	b24n117
+Bordetella pertussis	Bpe40	Bpe40	whole_organism	whole_organism	genomic_DNA	Bpe40	Bpe40	Bpe40	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe40	US Cincinnati non-outbreak	b24n118.txt	b24n118_norm.txt	A-MEXP-234	b24n118	b24n118	b24n118
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe40	US Cincinnati non-outbreak	b24n118.txt	b24n118_norm.txt	A-MEXP-234	b24n118	b24n118	b24n118
+Bordetella pertussis	Bpe47	Bpe47	whole_organism	whole_organism	genomic_DNA	Bpe47	Bpe47	Bpe47	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe47	US Cincinnati non-outbreak	b24n120.txt	b24n120_norm.txt	A-MEXP-234	b24n120	b24n120	b24n120
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe47	US Cincinnati non-outbreak	b24n120.txt	b24n120_norm.txt	A-MEXP-234	b24n120	b24n120	b24n120
+Bordetella pertussis	Bpe49	Bpe49	whole_organism	whole_organism	genomic_DNA	Bpe49	Bpe49	Bpe49	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe49	US Cincinnati non-outbreak	b24n121.txt	b24n121_norm.txt	A-MEXP-234	b24n121	b24n121	b24n121
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe49	US Cincinnati non-outbreak	b24n121.txt	b24n121_norm.txt	A-MEXP-234	b24n121	b24n121	b24n121
+Bordetella pertussis	Bpe59	Bpe59	whole_organism	whole_organism	genomic_DNA	Bpe59	Bpe59	Bpe59	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe59	US Cincinnati non-outbreak	b29n108.txt	b29n108_norm.txt	A-MEXP-236	b29n108	b29n108	b29n108
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe59	US Cincinnati non-outbreak	b29n108.txt	b29n108_norm.txt	A-MEXP-236	b29n108	b29n108	b29n108
+Bordetella pertussis	Bpe58	Bpe58	whole_organism	whole_organism	genomic_DNA	Bpe58	Bpe58	Bpe58	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe58	Other US post-vaccine	b29n106.txt	b29n106_norm.txt	A-MEXP-236	b29n106	b29n106	b29n106
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe58	Other US post-vaccine	b29n106.txt	b29n106_norm.txt	A-MEXP-236	b29n106	b29n106	b29n106
+Bordetella pertussis	Bpe45	Bpe45	whole_organism	whole_organism	genomic_DNA	Bpe45	Bpe45	Bpe45	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe45	Other US post-vaccine	b24n032.txt	b24n032_norm.txt	A-MEXP-234	b24n032	b24n032	b24n032
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe45	Other US post-vaccine	b24n032.txt	b24n032_norm.txt	A-MEXP-234	b24n032	b24n032	b24n032
+Bordetella pertussis	Bpe329	Bpe329	whole_organism	whole_organism	genomic_DNA	Bpe329	Bpe329	Bpe329	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe329	Other US post-vaccine	b29n132.txt	b29n132_norm.txt	A-MEXP-236	b29n132	b29n132	b29n132
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe329	Other US post-vaccine	b29n132.txt	b29n132_norm.txt	A-MEXP-236	b29n132	b29n132	b29n132
+Bordetella pertussis	Bpe39	Bpe39	whole_organism	whole_organism	genomic_DNA	Bpe39	Bpe39	Bpe39	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe39	Other US post-vaccine	b23n123.txt	b23n123_norm.txt	A-MEXP-234	b23n123	b23n123	b23n123
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe39	Other US post-vaccine	b23n123.txt	b23n123_norm.txt	A-MEXP-234	b23n123	b23n123	b23n123
+Bordetella pertussis	Bpe42	Bpe42	whole_organism	whole_organism	genomic_DNA	Bpe42	Bpe42	Bpe42	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe42	Other US post-vaccine	b23n125.txt	b23n125_norm.txt	A-MEXP-234	b23n125	b23n125	b23n125
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe42	Other US post-vaccine	b23n125.txt	b23n125_norm.txt	A-MEXP-234	b23n125	b23n125	b23n125
+Bordetella pertussis	Bpe241	Bpe241	whole_organism	whole_organism	genomic_DNA	Bpe241	Bpe241	Bpe241	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe241	Other US post-vaccine	b29n111.txt	b29n111_norm.txt	A-MEXP-236	b29n111	b29n111	b29n111
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe241	Other US post-vaccine	b29n111.txt	b29n111_norm.txt	A-MEXP-236	b29n111	b29n111	b29n111
+Bordetella pertussis	Bpe46	Bpe46	whole_organism	whole_organism	genomic_DNA	Bpe46	Bpe46	Bpe46	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe46	Other US post-vaccine	b24n119.txt	b24n119_norm.txt	A-MEXP-234	b24n119	b24n119	b24n119
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe46	Other US post-vaccine	b24n119.txt	b24n119_norm.txt	A-MEXP-234	b24n119	b24n119	b24n119
+Bordetella pertussis	Bpe280	Bpe280	whole_organism	whole_organism	genomic_DNA	Bpe280	Bpe280	Bpe280	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe280	Other US post-vaccine	b29n117.txt	b29n117_norm.txt	A-MEXP-236	b29n117	b29n117	b29n117
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe280	Other US post-vaccine	b29n117.txt	b29n117_norm.txt	A-MEXP-236	b29n117	b29n117	b29n117
+Bordetella pertussis	Bpe281	Bpe281	whole_organism	whole_organism	genomic_DNA	Bpe281	Bpe281	Bpe281	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe281	Other US post-vaccine	b29n118.txt	b29n118_norm.txt	A-MEXP-236	b29n118	b29n118	b29n118
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe281	Other US post-vaccine	b29n118.txt	b29n118_norm.txt	A-MEXP-236	b29n118	b29n118	b29n118
+Bordetella pertussis	Bpe319	Bpe319	whole_organism	whole_organism	genomic_DNA	Bpe319	Bpe319	Bpe319	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe319	Other US post-vaccine	b29n120.txt	b29n120_norm.txt	A-MEXP-236	b29n120	b29n120	b29n120
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe319	Other US post-vaccine	b29n120.txt	b29n120_norm.txt	A-MEXP-236	b29n120	b29n120	b29n120
+Bordetella pertussis	Bpe320	Bpe320	whole_organism	whole_organism	genomic_DNA	Bpe320	Bpe320	Bpe320	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe320	Other US post-vaccine	b29n121.txt	b29n121_norm.txt	A-MEXP-236	b29n121	b29n121	b29n121
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe320	Other US post-vaccine	b29n121.txt	b29n121_norm.txt	A-MEXP-236	b29n121	b29n121	b29n121
+Bordetella pertussis	Bpe321	Bpe321	whole_organism	whole_organism	genomic_DNA	Bpe321	Bpe321	Bpe321	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe321	Other US post-vaccine	b29n122.txt	b29n122_norm.txt	A-MEXP-236	b29n122	b29n122	b29n122
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe321	Other US post-vaccine	b29n122.txt	b29n122_norm.txt	A-MEXP-236	b29n122	b29n122	b29n122
+Bordetella pertussis	Bpe322	Bpe322	whole_organism	whole_organism	genomic_DNA	Bpe322	Bpe322	Bpe322	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe322	Other US post-vaccine	b29n125.txt	b29n125_norm.txt	A-MEXP-236	b29n125	b29n125	b29n125
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe322	Other US post-vaccine	b29n125.txt	b29n125_norm.txt	A-MEXP-236	b29n125	b29n125	b29n125
+Bordetella pertussis	Bpe323	Bpe323	whole_organism	whole_organism	genomic_DNA	Bpe323	Bpe323	Bpe323	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe323	Other US post-vaccine	b29n126.txt	b29n126_norm.txt	A-MEXP-236	b29n126	b29n126	b29n126
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe323	Other US post-vaccine	b29n126.txt	b29n126_norm.txt	A-MEXP-236	b29n126	b29n126	b29n126
+Bordetella pertussis	Bpe324	Bpe324	whole_organism	whole_organism	genomic_DNA	Bpe324	Bpe324	Bpe324	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe324	Other US post-vaccine	b29n127.txt	b29n127_norm.txt	A-MEXP-236	b29n127	b29n127	b29n127
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe324	Other US post-vaccine	b29n127.txt	b29n127_norm.txt	A-MEXP-236	b29n127	b29n127	b29n127
+Bordetella pertussis	Bpe325	Bpe325	whole_organism	whole_organism	genomic_DNA	Bpe325	Bpe325	Bpe325	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe325	Other US post-vaccine	b29n128.txt	b29n128_norm.txt	A-MEXP-236	b29n128	b29n128	b29n128
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe325	Other US post-vaccine	b29n128.txt	b29n128_norm.txt	A-MEXP-236	b29n128	b29n128	b29n128
+Bordetella pertussis	Bpe326	Bpe326	whole_organism	whole_organism	genomic_DNA	Bpe326	Bpe326	Bpe326	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe326	Other US post-vaccine	b29n129.txt	b29n129_norm.txt	A-MEXP-236	b29n129	b29n129	b29n129
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe326	Other US post-vaccine	b29n129.txt	b29n129_norm.txt	A-MEXP-236	b29n129	b29n129	b29n129
+Bordetella pertussis	Bpe327	Bpe327	whole_organism	whole_organism	genomic_DNA	Bpe327	Bpe327	Bpe327	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe327	Other US post-vaccine	b29n130.txt	b29n130_norm.txt	A-MEXP-236	b29n130	b29n130	b29n130
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe327	Other US post-vaccine	b29n130.txt	b29n130_norm.txt	A-MEXP-236	b29n130	b29n130	b29n130
+Bordetella pertussis	Bpe328	Bpe328	whole_organism	whole_organism	genomic_DNA	Bpe328	Bpe328	Bpe328	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe328	Other US post-vaccine	b29n131.txt	b29n131_norm.txt	A-MEXP-236	b29n131	b29n131	b29n131
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe328	Other US post-vaccine	b29n131.txt	b29n131_norm.txt	A-MEXP-236	b29n131	b29n131	b29n131
+Bordetella pertussis	Bpe330	Bpe330	whole_organism	whole_organism	genomic_DNA	Bpe330	Bpe330	Bpe330	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe330	Other US post-vaccine	b31n040.txt	b31n040_norm.txt	a-mexp-281!	b31n040	b31n040	b31n040
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe330	Other US post-vaccine	b31n040.txt	b31n040_norm.txt	a-mexp-281!	b31n040	b31n040	b31n040
+Bordetella pertussis	Bpe331	Bpe331	whole_organism	whole_organism	genomic_DNA	Bpe331	Bpe331	Bpe331	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe331	Other US post-vaccine	b31n041.txt	b31n041_norm.txt	a-mexp-281!	b31n041	b31n041	b31n041
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe331	Other US post-vaccine	b31n041.txt	b31n041_norm.txt	a-mexp-281!	b31n041	b31n041	b31n041
+Bordetella pertussis	Bpe332	Bpe332	whole_organism	whole_organism	genomic_DNA	Bpe332	Bpe332	Bpe332	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe332	Other US post-vaccine	b31n042.txt	b31n042_norm.txt	a-mexp-281!	b31n042	b31n042	b31n042
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe332	Other US post-vaccine	b31n042.txt	b31n042_norm.txt	a-mexp-281!	b31n042	b31n042	b31n042
+Bordetella pertussis	Bpe333	Bpe333	whole_organism	whole_organism	genomic_DNA	Bpe333	Bpe333	Bpe333	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe333	Other US post-vaccine	b31n043.txt	b31n043_norm.txt	a-mexp-281!	b31n043	b31n043	b31n043
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe333	Other US post-vaccine	b31n043.txt	b31n043_norm.txt	a-mexp-281!	b31n043	b31n043	b31n043
+Bordetella pertussis	Bpe334	Bpe334	whole_organism	whole_organism	genomic_DNA	Bpe334	Bpe334	Bpe334	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe334	Other US post-vaccine	b31n044.txt	b31n044_norm.txt	a-mexp-281!	b31n044	b31n044	b31n044
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe334	Other US post-vaccine	b31n044.txt	b31n044_norm.txt	a-mexp-281!	b31n044	b31n044	b31n044
+Bordetella pertussis	Bpe335	Bpe335	whole_organism	whole_organism	genomic_DNA	Bpe335	Bpe335	Bpe335	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe335	Other US post-vaccine	b31n045.txt	b31n045_norm.txt	a-mexp-281!	b31n045	b31n045	b31n045
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe335	Other US post-vaccine	b31n045.txt	b31n045_norm.txt	a-mexp-281!	b31n045	b31n045	b31n045
+Bordetella pertussis	Bpe336	Bpe336	whole_organism	whole_organism	genomic_DNA	Bpe336	Bpe336	Bpe336	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe336	Other US post-vaccine	b31n046.txt	b31n046_norm.txt	a-mexp-281!	b31n046	b31n046	b31n046
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe336	Other US post-vaccine	b31n046.txt	b31n046_norm.txt	a-mexp-281!	b31n046	b31n046	b31n046
+Bordetella pertussis	Bpe337	Bpe337	whole_organism	whole_organism	genomic_DNA	Bpe337	Bpe337	Bpe337	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe337	Other US post-vaccine	b31n047.txt	b31n047_norm.txt	a-mexp-281!	b31n047	b31n047	b31n047
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe337	Other US post-vaccine	b31n047.txt	b31n047_norm.txt	a-mexp-281!	b31n047	b31n047	b31n047
+Bordetella pertussis	Bpe338	Bpe338	whole_organism	whole_organism	genomic_DNA	Bpe338	Bpe338	Bpe338	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe338	Other US post-vaccine	b31n048.txt	b31n048_norm.txt	a-mexp-281!	b31n048	b31n048	b31n048
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe338	Other US post-vaccine	b31n048.txt	b31n048_norm.txt	a-mexp-281!	b31n048	b31n048	b31n048
+Bordetella pertussis	Bpe339	Bpe339	whole_organism	whole_organism	genomic_DNA	Bpe339	Bpe339	Bpe339	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe339	Other US post-vaccine	b31n049.txt	b31n049_norm.txt	a-mexp-281!	b31n049	b31n049	b31n049
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe339	Other US post-vaccine	b31n049.txt	b31n049_norm.txt	a-mexp-281!	b31n049	b31n049	b31n049
+Bordetella pertussis	Bpe340	Bpe340	whole_organism	whole_organism	genomic_DNA	Bpe340	Bpe340	Bpe340	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe340	Other US post-vaccine	b31n050.txt	b31n050_norm.txt	a-mexp-281!	b31n050	b31n050	b31n050
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe340	Other US post-vaccine	b31n050.txt	b31n050_norm.txt	a-mexp-281!	b31n050	b31n050	b31n050
+Bordetella pertussis	Bpe341	Bpe341	whole_organism	whole_organism	genomic_DNA	Bpe341	Bpe341	Bpe341	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe341	Other US post-vaccine	b31n051.txt	b31n051_norm.txt	a-mexp-281!	b31n051	b31n051	b31n051
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe341	Other US post-vaccine	b31n051.txt	b31n051_norm.txt	a-mexp-281!	b31n051	b31n051	b31n051
+Bordetella pertussis	Bpe342	Bpe342	whole_organism	whole_organism	genomic_DNA	Bpe342	Bpe342	Bpe342	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe342	Other US post-vaccine	b31n052.txt	b31n052_norm.txt	a-mexp-281!	b31n052	b31n052	b31n052
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe342	Other US post-vaccine	b31n052.txt	b31n052_norm.txt	a-mexp-281!	b31n052	b31n052	b31n052
+Bordetella pertussis	Bpe343	Bpe343	whole_organism	whole_organism	genomic_DNA	Bpe343	Bpe343	Bpe343	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe343	Other US post-vaccine	b31n053.txt	b31n053_norm.txt	a-mexp-281!	b31n053	b31n053	b31n053
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe343	Other US post-vaccine	b31n053.txt	b31n053_norm.txt	a-mexp-281!	b31n053	b31n053	b31n053
+Bordetella pertussis	Bpe344	Bpe344	whole_organism	whole_organism	genomic_DNA	Bpe344	Bpe344	Bpe344	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe344	Other US post-vaccine	b31n054.txt	b31n054_norm.txt	a-mexp-281!	b31n054	b31n054	b31n054
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe344	Other US post-vaccine	b31n054.txt	b31n054_norm.txt	a-mexp-281!	b31n054	b31n054	b31n054
+Bordetella pertussis	Bpe345	Bpe345	whole_organism	whole_organism	genomic_DNA	Bpe345	Bpe345	Bpe345	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe345	Other US post-vaccine	b31n055.txt	b31n055_norm.txt	a-mexp-281!	b31n055	b31n055	b31n055
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe345	Other US post-vaccine	b31n055.txt	b31n055_norm.txt	a-mexp-281!	b31n055	b31n055	b31n055
+Bordetella pertussis	Bpe346	Bpe346	whole_organism	whole_organism	genomic_DNA	Bpe346	Bpe346	Bpe346	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe346	Other US post-vaccine	b31n056.txt	b31n056_norm.txt	a-mexp-281!	b31n056	b31n056	b31n056
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe346	Other US post-vaccine	b31n056.txt	b31n056_norm.txt	a-mexp-281!	b31n056	b31n056	b31n056
+Bordetella pertussis	Bpe347	Bpe347	whole_organism	whole_organism	genomic_DNA	Bpe347	Bpe347	Bpe347	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe347	Other US post-vaccine	b31n057.txt	b31n057_norm.txt	a-mexp-281!	b31n057	b31n057	b31n057
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe347	Other US post-vaccine	b31n057.txt	b31n057_norm.txt	a-mexp-281!	b31n057	b31n057	b31n057
+Bordetella pertussis	Bpe348	Bpe348	whole_organism	whole_organism	genomic_DNA	Bpe348	Bpe348	Bpe348	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe348	Other US post-vaccine	b31n058.txt	b31n058_norm.txt	a-mexp-281!	b31n058	b31n058	b31n058
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe348	Other US post-vaccine	b31n058.txt	b31n058_norm.txt	a-mexp-281!	b31n058	b31n058	b31n058
+Bordetella pertussis	Bpe349	Bpe349	whole_organism	whole_organism	genomic_DNA	Bpe349	Bpe349	Bpe349	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe349	Other US post-vaccine	b31n059.txt	b31n059_norm.txt	a-mexp-281!	b31n059	b31n059	b31n059
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe349	Other US post-vaccine	b31n059.txt	b31n059_norm.txt	a-mexp-281!	b31n059	b31n059	b31n059
+Bordetella pertussis	Bpe350	Bpe350	whole_organism	whole_organism	genomic_DNA	Bpe350	Bpe350	Bpe350	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe350	Other US post-vaccine	b31n060.txt	b31n060_norm.txt	a-mexp-281!	b31n060	b31n060	b31n060
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe350	Other US post-vaccine	b31n060.txt	b31n060_norm.txt	a-mexp-281!	b31n060	b31n060	b31n060
+Bordetella pertussis	Bpe351	Bpe351	whole_organism	whole_organism	genomic_DNA	Bpe351	Bpe351	Bpe351	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe351	Other US post-vaccine	b31n061.txt	b31n061_norm.txt	a-mexp-281!	b31n061	b31n061	b31n061
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe351	Other US post-vaccine	b31n061.txt	b31n061_norm.txt	a-mexp-281!	b31n061	b31n061	b31n061
+Bordetella pertussis	Bpe352	Bpe352	whole_organism	whole_organism	genomic_DNA	Bpe352	Bpe352	Bpe352	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe352	Other US post-vaccine	b31n062.txt	b31n062_norm.txt	a-mexp-281!	b31n062	b31n062	b31n062
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe352	Other US post-vaccine	b31n062.txt	b31n062_norm.txt	a-mexp-281!	b31n062	b31n062	b31n062
+Bordetella pertussis	Bpe353	Bpe353	whole_organism	whole_organism	genomic_DNA	Bpe353	Bpe353	Bpe353	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe353	Other US post-vaccine	b31n063.txt	b31n063_norm.txt	a-mexp-281!	b31n063	b31n063	b31n063
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe353	Other US post-vaccine	b31n063.txt	b31n063_norm.txt	a-mexp-281!	b31n063	b31n063	b31n063
+Bordetella pertussis	Bpe159	Bpe159	whole_organism	whole_organism	genomic_DNA	Bpe159	Bpe159	Bpe159	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe159	Other US post-vaccine	b23n006.txt	b23n006_norm.txt	A-MEXP-234	b23n006	b23n006	b23n006
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe159	Other US post-vaccine	b23n006.txt	b23n006_norm.txt	A-MEXP-234	b23n006	b23n006	b23n006
+Bordetella pertussis	Bpe205	Bpe205	whole_organism	whole_organism	genomic_DNA	Bpe205	Bpe205	Bpe205	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe205	Dutch pre-vaccine	b23n083.txt	b23n083_norm.txt	A-MEXP-234	b23n083	b23n083	b23n083
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe205	Dutch pre-vaccine	b23n083.txt	b23n083_norm.txt	A-MEXP-234	b23n083	b23n083	b23n083
+Bordetella pertussis	Bpe186	Bpe186	whole_organism	whole_organism	genomic_DNA	Bpe186	Bpe186	Bpe186	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe186	Dutch pre-vaccine	b23n109.txt	b23n109_norm.txt	A-MEXP-234	b23n109	b23n109	b23n109
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe186	Dutch pre-vaccine	b23n109.txt	b23n109_norm.txt	A-MEXP-234	b23n109	b23n109	b23n109
+Bordetella pertussis	Bpe191	Bpe191	whole_organism	whole_organism	genomic_DNA	Bpe191	Bpe191	Bpe191	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe191	Dutch pre-vaccine	b23n114.txt	b23n114_norm.txt	A-MEXP-234	b23n114	b23n114	b23n114
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe191	Dutch pre-vaccine	b23n114.txt	b23n114_norm.txt	A-MEXP-234	b23n114	b23n114	b23n114
+Bordetella pertussis	Bpe202	Bpe202	whole_organism	whole_organism	genomic_DNA	Bpe202	Bpe202	Bpe202	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe202	Dutch pre-vaccine	b24n061.txt	b24n061_norm.txt	A-MEXP-234	b24n061	b24n061	b24n061
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe202	Dutch pre-vaccine	b24n061.txt	b24n061_norm.txt	A-MEXP-234	b24n061	b24n061	b24n061
+Bordetella pertussis	Bpe197	Bpe197	whole_organism	whole_organism	genomic_DNA	Bpe197	Bpe197	Bpe197	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe197	Dutch pre-vaccine	b24n056.txt	b24n056_norm.txt	A-MEXP-234	b24n056	b24n056	b24n056
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe197	Dutch pre-vaccine	b24n056.txt	b24n056_norm.txt	A-MEXP-234	b24n056	b24n056	b24n056
+Bordetella pertussis	Bpe182	Bpe182	whole_organism	whole_organism	genomic_DNA	Bpe182	Bpe182	Bpe182	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe182	Dutch pre-vaccine	b23n105.txt	b23n105_norm.txt	A-MEXP-234	b23n105	b23n105	b23n105
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe182	Dutch pre-vaccine	b23n105.txt	b23n105_norm.txt	A-MEXP-234	b23n105	b23n105	b23n105
+Bordetella pertussis	Bpe195	Bpe195	whole_organism	whole_organism	genomic_DNA	Bpe195	Bpe195	Bpe195	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe195	Dutch pre-vaccine	b23n118.txt	b23n118_norm.txt	A-MEXP-234	b23n118	b23n118	b23n118
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe195	Dutch pre-vaccine	b23n118.txt	b23n118_norm.txt	A-MEXP-234	b23n118	b23n118	b23n118
+Bordetella pertussis	Bpe196	Bpe196	whole_organism	whole_organism	genomic_DNA	Bpe196	Bpe196	Bpe196	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe196	Dutch pre-vaccine	b23n119.txt	b23n119_norm.txt	A-MEXP-234	b23n119	b23n119	b23n119
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe196	Dutch pre-vaccine	b23n119.txt	b23n119_norm.txt	A-MEXP-234	b23n119	b23n119	b23n119
+Bordetella pertussis	Bpe179	Bpe179	whole_organism	whole_organism	genomic_DNA	Bpe179	Bpe179	Bpe179	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe179	Dutch pre-vaccine	b23n101.txt	b23n101_norm.txt	A-MEXP-234	b23n101	b23n101	b23n101
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe179	Dutch pre-vaccine	b23n101.txt	b23n101_norm.txt	A-MEXP-234	b23n101	b23n101	b23n101
+Bordetella pertussis	Bpe1	Bpe1	whole_organism	whole_organism	genomic_DNA	Bpe1	Bpe1	Bpe1	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe1	Dutch post-vaccine	b29n101.txt	b29n101_norm.txt	A-MEXP-236	b29n101	b29n101	b29n101
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe1	Dutch post-vaccine	b29n101.txt	b29n101_norm.txt	A-MEXP-236	b29n101	b29n101	b29n101
+Bordetella pertussis	Bpe11	Bpe11	whole_organism	whole_organism	genomic_DNA	Bpe11	Bpe11	Bpe11	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe11	Dutch post-vaccine	b29n102.txt	b29n102_norm.txt	A-MEXP-236	b29n102	b29n102	b29n102
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe11	Dutch post-vaccine	b29n102.txt	b29n102_norm.txt	A-MEXP-236	b29n102	b29n102	b29n102
+Bordetella pertussis	Bpe14	Bpe14	whole_organism	whole_organism	genomic_DNA	Bpe14	Bpe14	Bpe14	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe14	Dutch post-vaccine	b29n103.txt	b29n103_norm.txt	A-MEXP-236	b29n103	b29n103	b29n103
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe14	Dutch post-vaccine	b29n103.txt	b29n103_norm.txt	A-MEXP-236	b29n103	b29n103	b29n103
+Bordetella pertussis	Bpe15	Bpe15	whole_organism	whole_organism	genomic_DNA	Bpe15	Bpe15	Bpe15	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe15	Dutch post-vaccine	b29n104.txt	b29n104_norm.txt	A-MEXP-236	b29n104	b29n104	b29n104
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe15	Dutch post-vaccine	b29n104.txt	b29n104_norm.txt	A-MEXP-236	b29n104	b29n104	b29n104
+Bordetella pertussis	Bpe206	Bpe206	whole_organism	whole_organism	genomic_DNA	Bpe206	Bpe206	Bpe206	synthetic_DNA	Cy5	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9967	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe206	Dutch post-vaccine	b23n085.txt	b23n085_norm.txt	A-MEXP-234	b23n085	b23n085	b23n085
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe206	Dutch post-vaccine	b23n085.txt	b23n085_norm.txt	A-MEXP-234	b23n085	b23n085	b23n085
+Bordetella pertussis	Bpe180	Bpe180	whole_organism	whole_organism	genomic_DNA	Bpe180	Bpe180	Bpe180	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe180	Dutch post-vaccine	b23n103.txt	b23n103_norm.txt	A-MEXP-234	b23n103	b23n103	b23n103
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe180	Dutch post-vaccine	b23n103.txt	b23n103_norm.txt	A-MEXP-234	b23n103	b23n103	b23n103
+Bordetella pertussis	Bpe181	Bpe181	whole_organism	whole_organism	genomic_DNA	Bpe181	Bpe181	Bpe181	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe181	Dutch post-vaccine	b23n104.txt	b23n104_norm.txt	A-MEXP-234	b23n104	b23n104	b23n104
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe181	Dutch post-vaccine	b23n104.txt	b23n104_norm.txt	A-MEXP-234	b23n104	b23n104	b23n104
+Bordetella pertussis	Bpe183	Bpe183	whole_organism	whole_organism	genomic_DNA	Bpe183	Bpe183	Bpe183	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe183	Dutch post-vaccine	b23n106.txt	b23n106_norm.txt	A-MEXP-234	b23n106	b23n106	b23n106
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe183	Dutch post-vaccine	b23n106.txt	b23n106_norm.txt	A-MEXP-234	b23n106	b23n106	b23n106
+Bordetella pertussis	Bpe184	Bpe184	whole_organism	whole_organism	genomic_DNA	Bpe184	Bpe184	Bpe184	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe184	Dutch post-vaccine	b23n107.txt	b23n107_norm.txt	A-MEXP-234	b23n107	b23n107	b23n107
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe184	Dutch post-vaccine	b23n107.txt	b23n107_norm.txt	A-MEXP-234	b23n107	b23n107	b23n107
+Bordetella pertussis	Bpe185	Bpe185	whole_organism	whole_organism	genomic_DNA	Bpe185	Bpe185	Bpe185	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe185	Dutch post-vaccine	b23n108.txt	b23n108_norm.txt	A-MEXP-234	b23n108	b23n108	b23n108
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe185	Dutch post-vaccine	b23n108.txt	b23n108_norm.txt	A-MEXP-234	b23n108	b23n108	b23n108
+Bordetella pertussis	Bpe187	Bpe187	whole_organism	whole_organism	genomic_DNA	Bpe187	Bpe187	Bpe187	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe187	Dutch post-vaccine	b23n110.txt	b23n110_norm.txt	A-MEXP-234	b23n110	b23n110	b23n110
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe187	Dutch post-vaccine	b23n110.txt	b23n110_norm.txt	A-MEXP-234	b23n110	b23n110	b23n110
+Bordetella pertussis	Bpe188	Bpe188	whole_organism	whole_organism	genomic_DNA	Bpe188	Bpe188	Bpe188	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe188	Dutch post-vaccine	b23n111.txt	b23n111_norm.txt	A-MEXP-234	b23n111	b23n111	b23n111
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe188	Dutch post-vaccine	b23n111.txt	b23n111_norm.txt	A-MEXP-234	b23n111	b23n111	b23n111
+Bordetella pertussis	Bpe189	Bpe189	whole_organism	whole_organism	genomic_DNA	Bpe189	Bpe189	Bpe189	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe189	Dutch post-vaccine	b23n112.txt	b23n112_norm.txt	A-MEXP-234	b23n112	b23n112	b23n112
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe189	Dutch post-vaccine	b23n112.txt	b23n112_norm.txt	A-MEXP-234	b23n112	b23n112	b23n112
+Bordetella pertussis	Bpe190	Bpe190	whole_organism	whole_organism	genomic_DNA	Bpe190	Bpe190	Bpe190	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe190	Dutch post-vaccine	b23n113.txt	b23n113_norm.txt	A-MEXP-234	b23n113	b23n113	b23n113
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe190	Dutch post-vaccine	b23n113.txt	b23n113_norm.txt	A-MEXP-234	b23n113	b23n113	b23n113
+Bordetella pertussis	Bpe192	Bpe192	whole_organism	whole_organism	genomic_DNA	Bpe192	Bpe192	Bpe192	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe192	Dutch post-vaccine	b23n115.txt	b23n115_norm.txt	A-MEXP-234	b23n115	b23n115	b23n115
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe192	Dutch post-vaccine	b23n115.txt	b23n115_norm.txt	A-MEXP-234	b23n115	b23n115	b23n115
+Bordetella pertussis	Bpe193	Bpe193	whole_organism	whole_organism	genomic_DNA	Bpe193	Bpe193	Bpe193	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe193	Dutch post-vaccine	b23n116.txt	b23n116_norm.txt	A-MEXP-234	b23n116	b23n116	b23n116
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe193	Dutch post-vaccine	b23n116.txt	b23n116_norm.txt	A-MEXP-234	b23n116	b23n116	b23n116
+Bordetella pertussis	Bpe194	Bpe194	whole_organism	whole_organism	genomic_DNA	Bpe194	Bpe194	Bpe194	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe194	Dutch post-vaccine	b23n117.txt	b23n117_norm.txt	A-MEXP-234	b23n117	b23n117	b23n117
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe194	Dutch post-vaccine	b23n117.txt	b23n117_norm.txt	A-MEXP-234	b23n117	b23n117	b23n117
+Bordetella pertussis	Bpe204	Bpe204	whole_organism	whole_organism	genomic_DNA	Bpe204	Bpe204	Bpe204	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe204	Dutch post-vaccine	b24n009.txt	b24n009_norm.txt	A-MEXP-234	b24n009	b24n009	b24n009
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe204	Dutch post-vaccine	b24n009.txt	b24n009_norm.txt	A-MEXP-234	b24n009	b24n009	b24n009
+Bordetella pertussis	Bpe198	Bpe198	whole_organism	whole_organism	genomic_DNA	Bpe198	Bpe198	Bpe198	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe198	Dutch post-vaccine	b24n057.txt	b24n057_norm.txt	A-MEXP-234	b24n057	b24n057	b24n057
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe198	Dutch post-vaccine	b24n057.txt	b24n057_norm.txt	A-MEXP-234	b24n057	b24n057	b24n057
+Bordetella pertussis	Bpe203	Bpe203	whole_organism	whole_organism	genomic_DNA	Bpe203	Bpe203	Bpe203	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe203	Dutch post-vaccine	b29n110.txt	b29n110_norm.txt	A-MEXP-236	b29n110	b29n110	b29n110
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe203	Dutch post-vaccine	b29n110.txt	b29n110_norm.txt	A-MEXP-236	b29n110	b29n110	b29n110
+Bordetella pertussis	Bpe199	Bpe199	whole_organism	whole_organism	genomic_DNA	Bpe199	Bpe199	Bpe199	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe199	Dutch post-vaccine	b24n058.txt	b24n058_norm.txt	A-MEXP-234	b24n058	b24n058	b24n058
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe199	Dutch post-vaccine	b24n058.txt	b24n058_norm.txt	A-MEXP-234	b24n058	b24n058	b24n058
+Bordetella pertussis	Bpe200	Bpe200	whole_organism	whole_organism	genomic_DNA	Bpe200	Bpe200	Bpe200	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965v	Bpe200	Dutch post-vaccine	b24n059.txt	b24n059_norm.txt	A-MEXP-234	b24n059	b24n059	b24n059
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe200	Dutch post-vaccine	b24n059.txt	b24n059_norm.txt	A-MEXP-234	b24n059	b24n059	b24n059
+Bordetella pertussis	WCH01	WCH01	whole_organism	whole_organism	genomic_DNA	WCH01	WCH01	WCH01	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH01	Australian	b23n086.txt	b23n086_norm.txt	A-MEXP-234	b23n086	b23n086	b23n086
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH01	Australian	b23n086.txt	b23n086_norm.txt	A-MEXP-234	b23n086	b23n086	b23n086
+Bordetella pertussis	WCH02	WCH02	whole_organism	whole_organism	genomic_DNA	WCH02	WCH02	WCH02	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH02	Australian	b23n087.txt	b23n087_norm.txt	A-MEXP-234	b23n087	b23n087	b23n087
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH02	Australian	b23n087.txt	b23n087_norm.txt	A-MEXP-234	b23n087	b23n087	b23n087
+Bordetella pertussis	WCH03	WCH03	whole_organism	whole_organism	genomic_DNA	WCH03	WCH03	WCH03	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH03	Australian	b23n088.txt	b23n088_norm.txt	A-MEXP-234	b23n088	b23n088	b23n088
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH03	Australian	b23n088.txt	b23n088_norm.txt	A-MEXP-234	b23n088	b23n088	b23n088
+Bordetella pertussis	WCH04	WCH04	whole_organism	whole_organism	genomic_DNA	WCH04	WCH04	WCH04	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH04	Australian	b23n089.txt	b23n089_norm.txt	A-MEXP-234	b23n089	b23n089	b23n089
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH04	Australian	b23n089.txt	b23n089_norm.txt	A-MEXP-234	b23n089	b23n089	b23n089
+Bordetella pertussis	WCH07	WCH07	whole_organism	whole_organism	genomic_DNA	WCH07	WCH07	WCH07	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH07	Australian	b23n090.txt	b23n090_norm.txt	A-MEXP-234	b23n090	b23n090	b23n090
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH07	Australian	b23n090.txt	b23n090_norm.txt	A-MEXP-234	b23n090	b23n090	b23n090
+Bordetella pertussis	WCH08	WCH08	whole_organism	whole_organism	genomic_DNA	WCH08	WCH08	WCH08	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH08	Australian	b23n091.txt	b23n091_norm.txt	A-MEXP-234	b23n091	b23n091	b23n091
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH08	Australian	b23n091.txt	b23n091_norm.txt	A-MEXP-234	b23n091	b23n091	b23n091
+Bordetella pertussis	WCH23	WCH23	whole_organism	whole_organism	genomic_DNA	WCH23	WCH23	WCH23	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH23	Australian	b23n092.txt	b23n092_norm.txt	A-MEXP-234	b23n092	b23n092	b23n092
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH23	Australian	b23n092.txt	b23n092_norm.txt	A-MEXP-234	b23n092	b23n092	b23n092
+Bordetella pertussis	WCH05	WCH05	whole_organism	whole_organism	genomic_DNA	WCH05	WCH05	WCH05	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH05	Australian	b24n085.txt	b24n085_norm.txt	A-MEXP-234	b24n085	b24n085	b24n085
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH05	Australian	b24n085.txt	b24n085_norm.txt	A-MEXP-234	b24n085	b24n085	b24n085
+Bordetella pertussis	WCH06	WCH06	whole_organism	whole_organism	genomic_DNA	WCH06	WCH06	WCH06	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH06	Australian	b24n086.txt	b24n086_norm.txt	A-MEXP-234	b24n086	b24n086	b24n086
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH06	Australian	b24n086.txt	b24n086_norm.txt	A-MEXP-234	b24n086	b24n086	b24n086
+Bordetella pertussis	WCH11	WCH11	whole_organism	whole_organism	genomic_DNA	WCH11	WCH11	WCH11	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH11	Australian	b24n087.txt	b24n087_norm.txt	A-MEXP-234	b24n087	b24n087	b24n087
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH11	Australian	b24n087.txt	b24n087_norm.txt	A-MEXP-234	b24n087	b24n087	b24n087
+Bordetella pertussis	WCH12	WCH12	whole_organism	whole_organism	genomic_DNA	WCH12	WCH12	WCH12	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH12	Australian	b24n088.txt	b24n088_norm.txt	A-MEXP-234	b24n088	b24n088	b24n088
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH12	Australian	b24n088.txt	b24n088_norm.txt	A-MEXP-234	b24n088	b24n088	b24n088
+Bordetella pertussis	WCH19	WCH19	whole_organism	whole_organism	genomic_DNA	WCH19	WCH19	WCH19	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH19	Australian	b24n089.txt	b24n089_norm.txt	A-MEXP-234	b24n089	b24n089	b24n089
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH19	Australian	b24n089.txt	b24n089_norm.txt	A-MEXP-234	b24n089	b24n089	b24n089
+Bordetella pertussis	WCH20	WCH20	whole_organism	whole_organism	genomic_DNA	WCH20	WCH20	WCH20	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH20	Australian	b24n090.txt	b24n090_norm.txt	A-MEXP-234	b24n090	b24n090	b24n090
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH20	Australian	b24n090.txt	b24n090_norm.txt	A-MEXP-234	b24n090	b24n090	b24n090
+Bordetella pertussis	WCH21	WCH21	whole_organism	whole_organism	genomic_DNA	WCH21	WCH21	WCH21	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH21	Australian	b24n091.txt	b24n091_norm.txt	A-MEXP-234	b24n091	b24n091	b24n091
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH21	Australian	b24n091.txt	b24n091_norm.txt	A-MEXP-234	b24n091	b24n091	b24n091
+Bordetella pertussis	WCH22	WCH22	whole_organism	whole_organism	genomic_DNA	WCH22	WCH22	WCH22	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH22	Australian	b24n092.txt	b24n092_norm.txt	A-MEXP-234	b24n092	b24n092	b24n092
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH22	Australian	b24n092.txt	b24n092_norm.txt	A-MEXP-234	b24n092	b24n092	b24n092
+Bordetella pertussis	WCH24	WCH24	whole_organism	whole_organism	genomic_DNA	WCH24	WCH24	WCH24	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH24	Australian	b24n093.txt	b24n093_norm.txt	A-MEXP-234	b24n093	b24n093	b24n093
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	WCH24	Australian	b24n093.txt	b24n093_norm.txt	A-MEXP-234	b24n093	b24n093	b24n093
+Bordetella pertussis	3588	3588	whole_organism	whole_organism	genomic_DNA	3588	3588	3588	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	3588	Italian (unvaccinated children)	b23n043.txt	b23n043_norm.txt	A-MEXP-234	b23n043	b23n043	b23n043
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	3588	Italian (unvaccinated children)	b23n043.txt	b23n043_norm.txt	A-MEXP-234	b23n043	b23n043	b23n043
+Bordetella pertussis	10641	10641	whole_organism	whole_organism	genomic_DNA	10641	10641	10641	synthetic_DvNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	10641	Italian (unvaccinated children)	b23n044.txt	b23n044_norm.txt	A-MEXP-234	b23n044	b23n044	b23n044
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	10641	Italian (unvaccinated children)	b23n044.txt	b23n044_norm.txt	A-MEXP-234	b23n044	b23n044	b23n044
+Bordetella pertussis	15550	15550	whole_organism	whole_organism	genomic_DNA	15550	15550	15550	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	15550	Italian (unvaccinated children)	b23n047.txt	b23n047_norm.txt	A-MEXP-234	b23n047	b23n047	b23n047
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	15550	Italian (unvaccinated children)	b23n047.txt	b23n047_norm.txt	A-MEXP-234	b23n047	b23n047	b23n047
+Bordetella pertussis	9859	9859	whole_organism	whole_organism	genomic_DNA	9859	9859	9859	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	9859	Italian (unvaccinated children)	b23n051.txt	b23n051_norm.txt	A-MEXP-234	b23n051	b23n051	b23n051
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	9859	Italian (unvaccinated children)	b23n051.txt	b23n051_norm.txt	A-MEXP-234	b23n051	b23n051	b23n051
+Bordetella pertussis	4558	4558	whole_organism	whole_organism	genomic_DNA	4558	4558	4558	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	4558	Italian (unvaccinated children)	b23n058.txt	b23n058_norm.txt	A-MEXP-234	b23n058	b23n058	b23n058
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	4558	Italian (unvaccinated children)	b23n058.txt	b23n058_norm.txt	A-MEXP-234	b23n058	b23n058	b23n058
+Bordetella pertussis	Bp01568	Bp01568	whole_organism	whole_organism	genomic_DNA	Bp01568	Bp01568	Bp01568	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bp01568	Italian (unvaccinated children)	b24n011.txt	b24n011_norm.txt	A-MEXP-234	b24n011	b24n011	b24n011
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bp01568	Italian (unvaccinated children)	b24n011.txt	b24n011_norm.txt	A-MEXP-234	b24n011	b24n011	b24n011
+Bordetella pertussis	Bp12892	Bp12892	whole_organism	whole_organism	genomic_DNA	Bp12892	Bp12892	Bp12892	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bp12892	Italian (unvaccinated children)	b24n012.txt	b24n012_norm.txt	A-MEXP-234	b24n012	b24n012	b24n012
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bp12892	Italian (unvaccinated children)	b24n012.txt	b24n012_norm.txt	A-MEXP-234	b24n012	b24n012	b24n012
+Bordetella pertussis	Bp05275	Bp05275	whole_organism	whole_organism	genomic_DNA	Bp05275	Bp05275	Bp05275	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bp05275	Italian (unvaccinated children)	b24n013.txt	b24n013_norm.txt	A-MEXP-234	b24n013	b24n013	b24n013
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bp05275	Italian (unvaccinated children)	b24n013.txt	b24n013_norm.txt	A-MEXP-234	b24n013	b24n013	b24n013
+Bordetella pertussis	Bp13732	Bp13732	whole_organism	whole_organism	genomic_DNA	Bp13732	Bp13732	Bp13732	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bp13732	Italian (unvaccinated children)	b24n080.txt	b24n080_norm.txt	A-MEXP-234	b24n080	b24n080	b24n080
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bp13732	Italian (unvaccinated children)	b24n080.txt	b24n080_norm.txt	A-MEXP-234	b24n080	b24n080	b24n080
+Bordetella pertussis	Bp12262	Bp12262	whole_organism	whole_organism	genomic_DNA	Bp12262	Bp12262	Bp12262	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bp12262	Italian (unvaccinated children)	b24n082.txt	b24n082_norm.txt	A-MEXP-234	b24n082	b24n082	b24n082
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bp12262	Italian (unvaccinated children)	b24n082.txt	b24n082_norm.txt	A-MEXP-234	b24n082	b24n082	b24n082
+Bordetella pertussis	Bp15622	Bp15622	whole_organism	whole_organism	genomic_DNA	Bp15622	Bp15622	Bp15622	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bp15622	Italian (unvaccinated children)	b24n083.txt	b24n083_norm.txt	A-MEXP-234	b24n083	b24n083	b24n083
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bp15622	Italian (unvaccinated children)	b24n083.txt	b24n083_norm.txt	A-MEXP-234	b24n083	b24n083	b24n083
+Bordetella pertussis	3520	3520	whole_organism	whole_organism	genomic_DNA	3520	3520	3520	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	3520	Italian (vaccinated children)	b23n041.txt	b23n041_norm.txt	A-MEXP-234	b23n041	b23n041	b23n041
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	3520	Italian (vaccinated children)	b23n041.txt	b23n041_norm.txt	A-MEXP-234	b23n041	b23n041	b23n041
+Bordetella pertussis	9878	9878	whole_organism	whole_organism	genomic_DNA	9878	9878	9878	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	9878	Italian (vaccinated children)	b23n042.txt	b23n042_norm.txt	A-MEXP-234	b23n042	b23n042	b23n042
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	9878	Italian (vaccinated children)	b23n042.txt	b23n042_norm.txt	A-MEXP-234	b23n042	b23n042	b23n042
+Bordetella pertussis	15637	15637	whole_organism	whole_organism	genomic_DNA	15637	15637	15637	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	15637	Italian (vaccinated children)	b23n050.txt	b23n050_norm.txt	A-MEXP-234	b23n050	b23n050	b23n050
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	15637	Italian (vaccinated children)	b23n050.txt	b23n050_norm.txt	A-MEXP-234	b23n050	b23n050	b23n050
+Bordetella pertussis	6453	6453	whole_organism	whole_organism	genomic_DNA	6453	6453	6453	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	6453	Italian (vaccinated children)	b23n052.txt	b23n052_norm.txt	A-MEXP-234	b23n052	b23n052	b23n052
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	6453	Italian (vaccinated children)	b23n052.txt	b23n052_norm.txt	A-MEXP-234	b23n052	b23n052	b23n052
+Bordetella pertussis	9585	9585	whole_organism	whole_organism	genomic_DNA	9585	9585	9585	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	9585	Italian (vaccinated children)	b23n056.txt	b23n056_norm.txt	A-MEXP-234	b23n056	b23n056	b23n056
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	9585	Italian (vaccinated children)	b23n056.txt	b23n056_norm.txt	A-MEXP-234	b23n056	b23n056	b23n056
+Bordetella pertussis	14782	14782	whole_organism	whole_organism	genomic_DNA	14782	14782	14782	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	14782	Italian (vaccinated children)	b23n057.txt	b23n057_norm.txt	A-MEXP-234	b23n057	b23n057	b23n057
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	14782	Italian (vaccinated children)	b23n057.txt	b23n057_norm.txt	A-MEXP-234	b23n057	b23n057	b23n057
+Bordetella pertussis	12464	12464	whole_organism	whole_organism	genomic_DNA	12464	12464	12464	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	12464	Italian (vaccinated children)	b23n059.txt	b23n059_norm.txt	A-MEXP-234	b23n059	b23n059	b23n059
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	12464	Italian (vaccinated children)	b23n059.txt	b23n059_norm.txt	A-MEXP-234	b23n059	b23n059	b23n059
+Bordetella pertussis	Bp01994	Bp01994	whole_organism	whole_organism	genomic_DNA	Bp01994	Bp01994	Bp01994	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bp01994	Italian (vaccinated children)	b24n076.txt	b24n076_norm.txt	A-MEXP-234	b24n076	b24n076	b24n076
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bp01994	Italian (vaccinated children)	b24n076.txt	b24n076_norm.txt	A-MEXP-234	b24n076	b24n076	b24n076
+Bordetella pertussis	Bp03975	Bp03975	whole_organism	whole_organism	genomic_DNA	Bp03975	Bp03975	Bp03975	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bp03975	Italian (vaccinated children)	b24n081.txt	b24n081_norm.txt	A-MEXP-234	b24n081	b24n081	b24n081
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bp03975	Italian (vaccinated children)	b24n081.txt	b24n081_norm.txt	A-MEXP-234	b24n081	b24n081	b24n081
+Bordetella pertussis	Bp04844	Bp04844	whole_organism	whole_organism	genomic_DNA	Bp04844	Bp04844	Bp04844	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bp04844	Italian (vaccinated children)	b24n084.txt	b24n084_norm.txt	A-MEXP-234	b24n084	b24n084	b24n084
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bp04844	Italian (vaccinated children)	b24n084.txt	b24n084_norm.txt	A-MEXP-234	b24n084	b24n084	b24n084
+Bordetella pertussis	Bpe53	Bpe53	whole_organism	whole_organism	genomic_DNA	Bpe53	Bpe53	Bpe53	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe53	18323 laboratory strain	b23n093.txt	b23n093_norm.txt	A-MEXP-234	b23n093	b23n093	b23n093
+B. bronchiseptica + B. parapertussis + B. pertussis	Bordetella reference strains	Bordetella reference strains	whole_organism	whole_organism	genomic_DNA	DNA reference	DNA reference	DNA reference	synthetic_DNA	Cy3	P-MEXP-9739	P-MEXP-9744	P-MEXP-9958	P-MEXP-9745	P-MEXP-9746	P-MEXP-9747	P-MEXP-9965	Bpe53	18323 laboratory strain	b23n093.txt	b23n093_norm.txt	A-MEXP-234	b23n093	b23n093	b23n093
diff --git a/examples/real/E-TABM-66.png b/examples/real/E-TABM-66.png
new file mode 100644
index 0000000..14689e0
Binary files /dev/null and b/examples/real/E-TABM-66.png differ
diff --git a/examples/real/E-TABM-66.txt b/examples/real/E-TABM-66.txt
new file mode 100755
index 0000000..9d60e7f
--- /dev/null
+++ b/examples/real/E-TABM-66.txt
@@ -0,0 +1,184 @@
+Experiment section	
+domain	ebi.ac.uk
+accession	E-TABM-66
+quality_control	
+experiment_design_type	cell_type_comparison_design
+name	Comparison of normal and malignant human breast epithelial cells
+description	"Multiple genome-wide microarrays have been used to analyse the transcriptomes of immunomagnetically separated normal human luminal epithelial and myoepithelial cells, as well as primary malignant breast epithelial cells."
+release_date	
+submission_date	
+submitter	Anita Grigoriadis
+organization	
+publication_title	
+authors	
+journal	
+volume	
+issue	
+pages																														
+year																														
+
+Protocol section																														
+accession	text	name	type	parameters																										
+P-MEXP-10843	"100 ug total RNA of 352,453,463,621,688,697,723,729,794,1098 were combined."	Myoepithelial pool	pool																											
+P-MEXP-10844	"100 ug total RNA of 723,729,697,822,1016,1031,1040,1053,1123,1130 were combined."	Luminal_epithelial_pool	pool																											
+P-MEXP-10845	"50ug total RNA of 489,552,593,695,725,842,845,896,983,1000,1033,1045,1052,1062,1063 were combined"	F19_breast_tumour_pool	pool																											
+P-TABM-AnitaG-10115	"1. One round of amplification through to dye coupling reaction performed according to Amino Allyl MessageAmp aRNA Kit protocol (Ambion, cat #1752   http://www.ambion.com/techlib/prot/fm_1752.pdf ). Cy3 monoreactive dye (Amersham, cat# PA23001) or Cy5  (Amersham, cat# PA25001)  used to label samples.<br>2. Rneasy Minelute Cleanup Kit (QIAGEN, cat #74204 http://www1.qiagen.com/literature/handbooks/PDF/RNACleanupAndConcentration/RNY_MinElute_Cleanup/1023760_HBRNYME0303W [...]
+P-MEXP-14985	"For detail see CodeLink Gene Expression System:Manual Labelled cRNA Target Preparation:<br><br>Assessment of total RNA<br><br>1.1 See Appendix 2 and 3 to assess the quantity and quality of total RNA starting material prior to use in .rst-strand cDNA synthesis reaction.<br><br>Synthesis of first-strand cDNA<br><br>2.1 Prepare a working solution of bacterial control mRNAs (Appendix 4).<br><br>2.2 Prepare each total RNA sample for manual target preparation: 0.2 .2 ug total RNA [...]
+P-MEXP-14987	"For details see CodeLink Gene Expression System: Single-Assay Bioarray Hybridization and Detection<br><br>Protocol for hybridization and detection<br><br>1<br><br>Fragmentation of cRNA<br><br>1.1 For each bioarray to be loaded, bring 10 ug of cRNA (from step 6.13 of CodeLink target preparation protocol) to a final volume of 20 ul with nuclease-free water in a<br><br>thin-walled microcentrifuge tube.<br><br>1.2 Add 5 ul of 5fragmentation buffer for each bioarray. Place tube  [...]
+P-MEXP-14988	"GenePix scanner, 5 micron resolution, 600PMT, 100%Power"	CodeLink Scanning	image_acquisition																											
+
+Hybridization section																														
+index	BioSource	BioSourceMaterial	BioMaterialCharacteristics[Organism]	BioMaterialCharacteristics[BioSourceProvider]	BioMaterialCharacteristics[DevelopmentalStage]	BioMaterialCharacteristics[Age]	BioMaterialCharacteristics[TimeUnit]	BioMaterialCharacteristics[OrganismPart]	BioMaterialCharacteristics[Sex]	BioMaterialCharacteristics[Individual]	BioMaterialCharacteristics[DiseaseState]	FactorValue[DiseaseState]	BioMaterialCharacteristics[CellType]	BioMaterialCharacteristics[ClinicalHistory] [...]
+23	SAMPLE_human_822	organism_part	Homo sapiens	"Royal Free Hospital, London,UK"	adult			mammary gland	female	Human_822	normal	normal	luminal epithelial	Reduction mammoplasties	luminal epithelial	P-MEXP-975	P-MEXP-972	P-MEXP-10844	E_luminal_pool	L_pool_cy3_agil1	P-TABM-AnitaG-10115	Cy3	L_pool_cy3_agil1	L_pool_cy3_agil1	251239114659.gpr			A-MEXP-218	P-MEXP-6162	P-MEXP-10145
+4	SAMPLE_human_621	organism_part	Homo sapiens	"Middlesex University Hospital, London,UK"	adult	38	years	mammary gland	female	Human_621	normal	normal	myoepithelial	Reduction mammoplasties	myoepithelial	P-MEXP-976	P-MEXP-972	P-MEXP-10843	E_myoepithelial_pool	M_pool_cy3_agil1	P-TABM-AnitaG-10115	Cy3	M_pool_cy3_agil1	M_pool_cy3_agil1	251239114660.gpr			A-MEXP-218	P-MEXP-6162	P-MEXP-10145
+42	SAMPLE_human_695	organism_part	Homo sapiens	"University College London Hospital, London,UK"	adult			mammary gland	female	Human_695	invasive lobular carcinoma	invasive lobular carcinoma	breast epithelial	"grad1,size42, 9/15 lymph node, ESR1 positive, progesteron receptor negative"	breast epithelial		P-MEXP-972	P-MEXP-10845	E_tumour_pool	T_pool_cy3_agil1	P-TABM-AnitaG-10115	Cy3	T_pool_cy3_agil1	T_pool_cy3_agil1	251239114661.gpr			A-MEXP-218	P-MEXP-6162	P-MEXP-10145
+26	SAMPLE_human_1040	organism_part	Homo sapiens	"Middlesex Hospital, London,UK"	adult	33	years	mammary gland	female	Human_1040	normal	normal	luminal epithelial	Reduction mammoplasties	luminal epithelial	P-MEXP-975	P-MEXP-972	P-MEXP-10844	E_luminal_pool	L_pool_cy5_agil1	P-TABM-AnitaG-10115	Cy5	L_pool_cy5_agil1	L_pool_cy5_agil1	251239119797.gpr			A-MEXP-218	P-MEXP-6162	P-MEXP-10145
+7	SAMPLE_human_723	organism_part	Homo sapiens	"St. George's University Hospital, London,UK"	adult	32	years	mammary gland	female	Human_723	normal	normal	myoepithelial	Reduction mammoplasties	myoepithelial	P-MEXP-976	P-MEXP-972	P-MEXP-10843	E_myoepithelial_pool	M_pool_cy5_agil1	P-TABM-AnitaG-10115	Cy5	M_pool_cy5_agil1	M_pool_cy5_agil1	251239119798.gpr			A-MEXP-218	P-MEXP-6162	P-MEXP-10145
+45	SAMPLE_human_845	organism_part	Homo sapiens	"University College London Hospital, London,UK"	adult			mammary gland	female	Human_845	invasive ductual carcinoma	invasive ductual carcinoma	breast epithelial	"grad3,size50,vascular invasion, 2/9 lymph node,ESR1 positive, progesteron receptor negative"	breast epithelial		P-MEXP-972	P-MEXP-10845	E_tumour_pool	T_pool_cy5_agil1	P-TABM-AnitaG-10115	Cy5	T_pool_cy5_agil1	T_pool_cy5_agil1	251239119800.gpr			A-MEXP-218	P-MEXP-6162	P-MEXP-10145
+27	SAMPLE_human_1053	organism_part	Homo sapiens	"Ashstead Hospital, London,UK"	adult	22	years	mammary gland	female	Human_1053	normal	normal	luminal epithelial	Reduction mammoplasties	luminal epithelial	P-MEXP-975	P-MEXP-972	P-MEXP-10844	E_luminal_pool	L_pool_cy5_agil2	P-TABM-AnitaG-10115	Cy5	L_pool_cy5_agil2	L_pool_cy5_agil2	251239125237.gpr			A-MEXP-218	P-MEXP-6162	P-MEXP-10145
+8	SAMPLE_human_729	organism_part	Homo sapiens	"St. George's Hospital, London,UK"	adult	31	years	mammary gland	female	Human_729	normal	normal	myoepithelial	Reduction mammoplasties	myoepithelial	P-MEXP-976	P-MEXP-972	P-MEXP-10843	E_myoepithelial_pool	M_pool_cy5_agil2	P-TABM-AnitaG-10115	Cy5	M_pool_cy5_agil2	M_pool_cy5_agil2	251239125238.gpr			A-MEXP-218	P-MEXP-6162	P-MEXP-10145
+46	SAMPLE_human_896	organism_part	Homo sapiens	"University College London Hospital, London,UK"	adult			mammary gland	female	Human_896	invasive ductual carcinoma	invasive ductual carcinoma	breast epithelial	"size45, 0/2 lymph node"	breast epithelial		P-MEXP-972	P-MEXP-10845	E_tumour_pool	T_pool_cy5_agil2	P-TABM-AnitaG-10115	Cy5	T_pool_cy5_agil2	T_pool_cy5_agil2	251239125239.gpr			A-MEXP-218	P-MEXP-6162	P-MEXP-10145
+9	SAMPLE_human_1098	organism_part	Homo sapiens	"Queen Victoria Hospital, London,UK"	adult	26	years	mammary gland	female	Human_1098	normal	normal	myoepithelial	Reduction mammoplasties	myoepithelial	P-MEXP-976	P-MEXP-972	P-MEXP-10843	E_myoepithelial_pool	M_pool_cy5_agil3	P-TABM-AnitaG-10115	Cy5	M_pool_cy5_agil3	M_pool_cy5_agil3	251239125240.gpr			A-MEXP-218	P-MEXP-6162	P-MEXP-10145
+47	SAMPLE_human_983	organism_part	Homo sapiens	"University College London Hospital, London,UK"	adult			mammary gland	female	Human_983	invasive ductual carcinoma	invasive ductual carcinoma	breast epithelial	"grad3,size15,vascular invasion, ESR1 negative, HER2 positive, progesteron receptor negative"	breast epithelial		P-MEXP-972	P-MEXP-10845	E_tumour_pool	T_pool_cy5_agil3	P-TABM-AnitaG-10115	Cy5	T_pool_cy5_agil3	T_pool_cy5_agil3	251239125243.gpr			A-MEXP-218	P-MEXP-6162	P-MEXP-10145
+24	SAMPLE_human_1016	organism_part	Homo sapiens	"St. Anthony's Hospital, London,UK"	adult	30	years	mammary gland	female	Human_1016	normal	normal	luminal epithelial	Reduction mammoplasties	luminal epithelial	P-MEXP-975	P-MEXP-972	P-MEXP-10844	E_luminal_pool	L_pool_cy3_agil2	P-TABM-AnitaG-10115	Cy3	L_pool_cy3_agil2	L_pool_cy3_agil2	251239125464.gpr			A-MEXP-218	P-MEXP-6162	P-MEXP-10145
+5	SAMPLE_human_688	organism_part	Homo sapiens	"St. George's Hospital, London,UK"	adult	26	years	mammary gland	female	Human_688	normal	normal	myoepithelial	Reduction mammoplasties	myoepithelial	P-MEXP-976	P-MEXP-972	P-MEXP-10843	E_myoepithelial_pool	M_pool_cy3_agil2	P-TABM-AnitaG-10115	Cy3	M_pool_cy3_agil2	M_pool_cy3_agil2	251239125465.gpr			A-MEXP-218	P-MEXP-6162	P-MEXP-10145
+28	SAMPLE_human_1123	organism_part	Homo sapiens	"University College London Hospital, London,UK"	adult	32	years	mammary gland	female	Human_1123	normal	normal	luminal epithelial	Reduction mammoplasties	luminal epithelial	P-MEXP-975	P-MEXP-972	P-MEXP-10844	E_luminal_pool	L_pool_cy5_agil3	P-TABM-AnitaG-10115	Cy5	L_pool_cy5_agil3	L_pool_cy5_agil3	251239125466.gpr			A-MEXP-218	P-MEXP-6162	P-MEXP-10145
+43	SAMPLE_human_725	organism_part	Homo sapiens	"University College London Hospital, London,UK"	adult			mammary gland	female	Human_725	invasive ductual carcinoma	invasive ductual carcinoma	breast epithelial	"grad3,size35,vascular invasion, 1/15 lymph node, ESR1 positive"	breast epithelial		P-MEXP-972	P-MEXP-10845	E_tumour_pool	T_pool_cy3_agil2	P-TABM-AnitaG-10115	Cy3	T_pool_cy3_agil2	T_pool_cy3_agil2	251239125467.gpr			A-MEXP-218	P-MEXP-6162	P-MEXP-10145
+25	SAMPLE_human_1031	organism_part	Homo sapiens	"St. Elisabeth Hospital, London,UK"	adult	33	years	mammary gland	female	Human_1031	normal	normal	luminal epithelial	Reduction mammoplasties	luminal epithelial	P-MEXP-975	P-MEXP-972	P-MEXP-10844	E_luminal_pool	L_pool_cy3_agil3	P-TABM-AnitaG-10115	Cy3	L_pool_cy3_agil3	L_pool_cy3_agil3	251239125951.gpr			A-MEXP-218	P-MEXP-6162	P-MEXP-10145
+6	SAMPLE_human_697	organism_part	Homo sapiens	"St. George's University Hospital, London,UK"	adult	29	years	mammary gland	female	Human_697	normal	normal	myoepithelial	Reduction mammoplasties	myoepithelial	P-MEXP-976	P-MEXP-972	P-MEXP-10843	E_myoepithelial_pool	M_pool_cy3_agil3	P-TABM-AnitaG-10115	Cy3	M_pool_cy3_agil3	M_pool_cy3_agil3	251239125953.gpr			A-MEXP-218	P-MEXP-6162	P-MEXP-10145
+44	SAMPLE_human_842	organism_part	Homo sapiens	"University College London Hospital, London,UK"	adult			mammary gland	female	Human_842	invasive ductual carcinoma	invasive ductual carcinoma	breast epithelial	"grad3,vascular invasion, 1/10 lymph node, ESR1 positive, progesteron receptor positive"	breast epithelial		P-MEXP-972	P-MEXP-10845	E_tumour_pool	T_pool_cy3_agil3	P-TABM-AnitaG-10115	Cy3	T_pool_cy3_agil3	T_pool_cy3_agil3	251239125954.gpr			A-MEXP-218	P-MEXP-6162	P-MEXP-10145
+32												normal			luminal epithelial		P-MEXP-972	P-MEXP-10844	E_luminal_pool	L_pool_cy5_breakthrough1	P-TABM-AnitaG-10115	Cy5	L_pool_cy5_breakthrough1	L_pool_cy5_breakthrough1	90000310.gpr			A-MEXP-259	P-MEXP-10140	P-MEXP-10145
+13												normal			myoepithelial		P-MEXP-972	P-MEXP-10843	E_myoepithelial_pool	M_pool_cy5_breakthrough1	P-TABM-AnitaG-10115	Cy5	M_pool_cy5_breakthrough1	M_pool_cy5_breakthrough1	90000311.gpr			A-MEXP-259	P-MEXP-10140	P-MEXP-10145
+51	SAMPLE_human_1052	organism_part	Homo sapiens	"University College London Hospital, London,UK"	adult			mammary gland	female	Human_1052	invasive ductual carcinoma	invasive ductual carcinoma	breast epithelial	"grad3,size35, ESR1 negative, progesteron receptor negative"	breast epithelial		P-MEXP-972	P-MEXP-10845	E_tumour_pool	T_pool_cy5_breakthrough1	P-TABM-AnitaG-10115	Cy5	T_pool_cy5_breakthrough1	T_pool_cy5_breakthrough1	90000312.gpr			A-MEXP-259	P-MEXP-10140	P-MEXP-10145
+29	SAMPLE_human_1130	organism_part	Homo sapiens	"University College London Hospital, London,UK"	adult	32	years	mammary gland	female	Human_1130	normal	normal	luminal epithelial	Reduction mammoplasties	luminal epithelial	P-MEXP-975	P-MEXP-972	P-MEXP-10844	E_luminal_pool	L_pool_cy3_breakthrough1	P-TABM-AnitaG-10115	Cy3	L_pool_cy3_breakthrough1	L_pool_cy3_breakthrough1	90000337.gpr			A-MEXP-259	P-MEXP-10140	P-MEXP-10145
+10	SAMPLE_human_794	organism_part	Homo sapiens	"Ashstead Hospital, London,UK"	adult	39	years	mammary gland	female	Human_794	normal	normal	myoepithelial	Reduction mammoplasties	myoepithelial	P-MEXP-976	P-MEXP-972	P-MEXP-10843	E_myoepithelial_pool	M_pool_cy3_breakthrough1	P-TABM-AnitaG-10115	Cy3	M_pool_cy3_breakthrough1	M_pool_cy3_breakthrough1	90000338.gpr			A-MEXP-259	P-MEXP-10140	P-MEXP-10145
+48	SAMPLE_human_1000	organism_part	Homo sapiens	"University College London Hospital, London,UK"	adult			mammary gland	female	Human_1000	invasive ductual carcinoma	invasive ductual carcinoma	breast epithelial		breast epithelial		P-MEXP-972	P-MEXP-10845	E_tumour_pool	T_pool_cy3_breakthrough1	P-TABM-AnitaG-10115	Cy3	T_pool_cy3_breakthrough1	T_pool_cy3_breakthrough1	90000339.gpr			A-MEXP-259	P-MEXP-10140	P-MEXP-10145
+33												normal			luminal epithelial		P-MEXP-972	P-MEXP-10844	E_luminal_pool	L_pool_cy5_breakthrough2	P-TABM-AnitaG-10115	Cy5	L_pool_cy5_breakthrough2	L_pool_cy5_breakthrough2	90000340.gpr			A-MEXP-259	P-MEXP-10140	P-MEXP-10145
+14												normal			myoepithelial		P-MEXP-972	P-MEXP-10843	E_myoepithelial_pool	M_pool_cy5_breakthrough2	P-TABM-AnitaG-10115	Cy5	M_pool_cy5_breakthrough2	M_pool_cy5_breakthrough2	90000341.gpr			A-MEXP-259	P-MEXP-10140	P-MEXP-10145
+52	SAMPLE_human_1062	organism_part	Homo sapiens	"University College London Hospital, London,UK"	adult			mammary gland	female	Human_1062	invasive ductual carcinoma	invasive ductual carcinoma	breast epithelial	"grad2,size20,vascular invasion, 1/25 lymph node, ESR1 positive, HER2 negative, progesteron receptor negative"	breast epithelial		P-MEXP-972	P-MEXP-10845	E_tumour_pool	T_pool_cy5_breakthrough2	P-TABM-AnitaG-10115	Cy5	T_pool_cy5_breakthrough2	T_pool_cy5_breakthrough2	90000342.gpr			A- [...]
+30												normal			luminal epithelial		P-MEXP-972	P-MEXP-10844	E_luminal_pool	L_pool_cy3_breakthrough2	P-TABM-AnitaG-10115	Cy3	L_pool_cy3_breakthrough2	L_pool_cy3_breakthrough2	90000343.gpr			A-MEXP-259	P-MEXP-10140	P-MEXP-10145
+11												normal			myoepithelial		P-MEXP-972	P-MEXP-10843	E_myoepithelial_pool	M_pool_cy3_breakthrough2	P-TABM-AnitaG-10115	Cy3	M_pool_cy3_breakthrough2	M_pool_cy3_breakthrough2	90000344.gpr			A-MEXP-259	P-MEXP-10140	P-MEXP-10145
+49	SAMPLE_human_1033	organism_part	Homo sapiens	"University College London Hospital, London,UK"	adult			mammary gland	female	Human_1033	invasive ductual carcinoma	invasive ductual carcinoma	breast epithelial	"grad3,size58, 1/15 lymph node, ESR1 negative, HER2 negative, progesteron receptor negative"	breast epithelial		P-MEXP-972	P-MEXP-10845	E_tumour_pool	T_pool_cy3_breakthrough2	P-TABM-AnitaG-10115	Cy3	T_pool_cy3_breakthrough2	T_pool_cy3_breakthrough2	90000345.gpr			A-MEXP-259	P-MEXP-10 [...]
+34												normal			luminal epithelial		P-MEXP-972	P-MEXP-10844	E_luminal_pool	L_pool_cy5_breakthrough3	P-TABM-AnitaG-10115	Cy5	L_pool_cy5_breakthrough3	L_pool_cy5_breakthrough3	90000418.gpr			A-MEXP-259	P-MEXP-10140	P-MEXP-10145
+15												normal			myoepithelial		P-MEXP-972	P-MEXP-10843	E_myoepithelial_pool	M_pool_cy5_breakthrough3	P-TABM-AnitaG-10115	Cy5	M_pool_cy5_breakthrough3	M_pool_cy5_breakthrough3	90000419.gpr			A-MEXP-259	P-MEXP-10140	P-MEXP-10145
+53	SAMPLE_human_1063	organism_part	Homo sapiens	"University College London Hospital, London,UK"	adult			mammary gland	female	Human_1063	invasive ductual carcinoma	invasive ductual carcinoma	breast epithelial	"grad3,size45,vascular invasion, ESR1 positive, HER2 positive, progesteron receptor positive"	breast epithelial		P-MEXP-972	P-MEXP-10845	E_tumour_pool	T_pool_cy5_breakthrough3	P-TABM-AnitaG-10115	Cy5	T_pool_cy5_breakthrough3	T_pool_cy5_breakthrough3	90000420.gpr			A-MEXP-259	P-MEXP-1 [...]
+31												normal			luminal epithelial		P-MEXP-972	P-MEXP-10844	E_luminal_pool	L_pool_cy3_breakthrough3	P-TABM-AnitaG-10115	Cy3	L_pool_cy3_breakthrough3	L_pool_cy3_breakthrough3	90000421.gpr			A-MEXP-259	P-MEXP-10140	P-MEXP-10145
+12												normal			myoepithelial		P-MEXP-972	P-MEXP-10843	E_myoepithelial_pool	M_pool_cy3_breakthrough3	P-TABM-AnitaG-10115	Cy3	M_pool_cy3_breakthrough3	M_pool_cy3_breakthrough3	90000422.gpr			A-MEXP-259	P-MEXP-10140	P-MEXP-10145
+50	SAMPLE_human_1045	organism_part	Homo sapiens	"University College London Hospital, London,UK"	adult			mammary gland	female	Human_1045	invasive ductual carcinoma	invasive ductual carcinoma	breast epithelial	"grad2,size50, ESR1 negative, HER2 negative"	breast epithelial		P-MEXP-972	P-MEXP-10845	E_tumour_pool	T_pool_cy3_breakthrough3	P-TABM-AnitaG-10115	Cy3	T_pool_cy3_breakthrough3	T_pool_cy3_breakthrough3	90000423.gpr			A-MEXP-259	P-MEXP-10140	P-MEXP-10145
+106												normal			luminal				E_luminal_pool	2nd_L_pool_L_cy3_breakthrough1	P-TABM-AnitaG-10115	Cy3	2nd_L_pool_cy3_breakthrough1	2nd_L_pool_cy3_breakthrough1	95000061.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+138	REF sources																		E_REF	2nd_REF_pool_L_cy5_breakthrough1	P-TABM-AnitaG-10115	Cy5	2nd_L_pool_cy3_breakthrough1	2nd_L_pool_cy3_breakthrough1	95000061.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+107												normal			luminal				E_luminal_pool	2nd_L_pool_L_cy3_breakthrough2	P-TABM-AnitaG-10115	Cy3	2nd_L_pool_cy3_breakthrough2	2nd_L_pool_cy3_breakthrough2	95000062.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+139	REF sources																		E_REF	2nd_REF_pool_L_cy5_breakthrough2	P-TABM-AnitaG-10115	Cy5	2nd_L_pool_cy3_breakthrough2	2nd_L_pool_cy3_breakthrough2	95000062.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+94												normal			myoepithelial				E_myoepithelial_pool	2nd_M_pool_L_cy3_breakthrough1	P-TABM-AnitaG-10115	Cy3	2nd_M_pool_cy3_breakthrough1	2nd_M_pool_cy3_breakthrough1	95000063.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+130	REF sources																		E_REF	2nd_REF_pool_L_cy5_breakthrough1	P-TABM-AnitaG-10115	Cy5	2nd_M_pool_cy3_breakthrough1	2nd_M_pool_cy3_breakthrough1	95000063.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+95												normal			myoepithelial				E_myoepithelial_pool	2nd_M_pool_L_cy3_breakthrough2	P-TABM-AnitaG-10115	Cy3	2nd_M_pool_cy3_breakthrough2	2nd_M_pool_cy3_breakthrough2	95000064.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+131	REF sources																		E_REF	2nd_REF_pool_L_cy5_breakthrough2	P-TABM-AnitaG-10115	Cy5	2nd_M_pool_cy3_breakthrough2	2nd_M_pool_cy3_breakthrough2	95000064.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+118												invasive ductual carcinoma			breast epithelial				E_tumour_pool	2nd_T_pool_L_cy3_breakthrough95000065	P-TABM-AnitaG-10115	Cy3	2nd_T_pool_cy3_breakthrough95000065	2nd_T_pool_cy3_breakthrough95000065	95000065.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+146	REF sources																		E_REF	2nd_REF_pool_L_cy5_breakthrough95000065	P-TABM-AnitaG-10115	Cy5	2nd_T_pool_cy3_breakthrough95000065	2nd_T_pool_cy3_breakthrough95000065	95000065.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+119												invasive ductual carcinoma			breast epithelial				E_tumour_pool	2nd_T_pool_L_cy3_breakthrough95000066	P-TABM-AnitaG-10115	Cy3	2nd_T_pool_cy3_breakthrough95000066	2nd_T_pool_cy3_breakthrough95000066	95000066.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+147	REF sources																		E_REF	2nd_REF_pool_L_cy5_breakthrough95000066	P-TABM-AnitaG-10115	Cy5	2nd_T_pool_cy3_breakthrough95000066	2nd_T_pool_cy3_breakthrough95000066	95000066.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+110												normal			luminal				E_luminal_pool	2nd_L_pool_L_cy5_breakthrough1	P-TABM-AnitaG-10115	Cy5	2nd_L_pool_cy5_breakthrough1	2nd_L_pool_cy5_breakthrough1	95000067.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+142	REF sources																		E_REF	2nd_REF_pool_L_cy3_breakthrough1	P-TABM-AnitaG-10115	Cy3	2nd_L_pool_cy5_breakthrough1	2nd_L_pool_cy5_breakthrough1	95000067.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+111												normal			luminal				E_luminal_pool	2nd_L_pool_L_cy5_breakthrough2	P-TABM-AnitaG-10115	Cy5	2nd_L_pool_cy5_breakthrough2	2nd_L_pool_cy5_breakthrough2	95000068.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+143	REF sources																		E_REF	2nd_REF_pool_L_cy3_breakthrough2	P-TABM-AnitaG-10115	Cy3	2nd_L_pool_cy5_breakthrough2	2nd_L_pool_cy5_breakthrough2	95000068.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+98												normal			myoepithelial				E_myoepithelial_pool	2nd_M_pool_L_cy5_breakthrough1	P-TABM-AnitaG-10115	Cy5	2nd_M_pool_cy5_breakthrough1	2nd_M_pool_cy5_breakthrough1	95000069.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+134	REF sources																		E_REF	2nd_REF_pool_L_cy3_breakthrough1	P-TABM-AnitaG-10115	Cy3	2nd_M_pool_cy5_breakthrough1	2nd_M_pool_cy5_breakthrough1	95000069.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+99												normal			myoepithelial				E_myoepithelial_pool	2nd_M_pool_L_cy5_breakthrough2	P-TABM-AnitaG-10115	Cy5	2nd_M_pool_cy5_breakthrough2	2nd_M_pool_cy5_breakthrough2	95000070.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+135	REF sources																		E_REF	2nd_REF_pool_L_cy3_breakthrough2	P-TABM-AnitaG-10115	Cy3	2nd_M_pool_cy5_breakthrough2	2nd_M_pool_cy5_breakthrough2	95000070.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+122												invasive ductual carcinoma			breast epithelial				E_tumour_pool	2nd_T_pool_L_cy5_breakthrough1	P-TABM-AnitaG-10115	Cy5	2nd_T_pool_cy5_breakthrough1	2nd_T_pool_cy5_breakthrough1	95000071.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+150	REF sources																		E_REF	2nd_REF_pool_L_cy3_breakthrough1	P-TABM-AnitaG-10115	Cy3	2nd_T_pool_cy5_breakthrough1	2nd_T_pool_cy5_breakthrough1	95000071.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+123												invasive ductual carcinoma			breast epithelial				E_tumour_pool	2nd_T_pool_L_cy5_breakthrough2	P-TABM-AnitaG-10115	Cy5	2nd_T_pool_cy5_breakthrough2	2nd_T_pool_cy5_breakthrough2	95000072.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+151	REF sources																		E_REF	2nd_REF_pool_L_cy3_breakthrough2	P-TABM-AnitaG-10115	Cy3	2nd_T_pool_cy5_breakthrough2	2nd_T_pool_cy5_breakthrough2	95000072.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+112												normal			luminal				E_luminal_pool	2nd_L_pool_L_cy5_breakthrough3	P-TABM-AnitaG-10115	Cy5	2nd_L_pool_cy5_breakthrough3	2nd_L_pool_cy5_breakthrough3	95000073.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+144	REF sources																		E_REF	2nd_REF_pool_L_cy3_breakthrough3	P-TABM-AnitaG-10115	Cy3	2nd_L_pool_cy5_breakthrough3	2nd_L_pool_cy5_breakthrough3	95000073.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+113												normal			luminal				E_luminal_pool	2nd_L_pool_L_cy5_breakthrough4	P-TABM-AnitaG-10115	Cy5	2nd_L_pool_cy5_breakthrough4	2nd_L_pool_cy5_breakthrough4	95000074.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+145	REF sources																		E_REF	2nd_REF_pool_L_cy3_breakthrough4	P-TABM-AnitaG-10115	Cy3	2nd_L_pool_cy5_breakthrough4	2nd_L_pool_cy5_breakthrough4	95000074.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+100												normal			myoepithelial				E_myoepithelial_pool	2nd_M_pool_L_cy5_breakthrough3	P-TABM-AnitaG-10115	Cy5	2nd_M_pool_cy5_breakthrough3	2nd_M_pool_cy5_breakthrough3	95000075.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+136	REF sources																		E_REF	2nd_REF_pool_L_cy3_breakthrough3	P-TABM-AnitaG-10115	Cy3	2nd_M_pool_cy5_breakthrough3	2nd_M_pool_cy5_breakthrough3	95000075.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+101												normal			myoepithelial				E_myoepithelial_pool	2nd_M_pool_L_cy5_breakthrough4	P-TABM-AnitaG-10115	Cy5	2nd_M_pool_cy5_breakthrough4	2nd_M_pool_cy5_breakthrough4	95000076.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+137	REF sources																		E_REF	2nd_REF_pool_L_cy3_breakthrough4	P-TABM-AnitaG-10115	Cy3	2nd_M_pool_cy5_breakthrough4	2nd_M_pool_cy5_breakthrough4	95000076.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+124												invasive ductual carcinoma			breast epithelial				E_tumour_pool	2nd_T_pool_L_cy5_breakthrough3	P-TABM-AnitaG-10115	Cy5	2nd_T_pool_cy5_breakthrough3	2nd_T_pool_cy5_breakthrough3	95000077.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+152	REF sources																		E_REF	2nd_REF_pool_L_cy3_breakthrough3	P-TABM-AnitaG-10115	Cy3	2nd_T_pool_cy5_breakthrough3	2nd_T_pool_cy5_breakthrough3	95000077.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+125												invasive ductual carcinoma			breast epithelial				E_tumour_pool	2nd_T_pool_L_cy5_breakthrough4	P-TABM-AnitaG-10115	Cy5	2nd_T_pool_cy5_breakthrough4	2nd_T_pool_cy5_breakthrough4	95000078.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+153	REF sources																		E_REF	2nd_REF_pool_L_cy3_breakthrough4	P-TABM-AnitaG-10115	Cy3	2nd_T_pool_cy5_breakthrough4	2nd_T_pool_cy5_breakthrough4	95000078.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+108												normal			luminal				E_luminal_pool	2nd_L_pool_L_cy3_breakthrough3	P-TABM-AnitaG-10115	Cy3	2nd_L_pool_cy3_breakthrough3	2nd_L_pool_cy3_breakthrough3	95000079.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+140	REF sources																		E_REF	2nd_REF_pool_L_cy5_breakthrough3	P-TABM-AnitaG-10115	Cy5	2nd_L_pool_cy3_breakthrough3	2nd_L_pool_cy3_breakthrough3	95000079.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+109												normal			luminal				E_luminal_pool	2nd_L_pool_L_cy3_breakthrough4	P-TABM-AnitaG-10115	Cy3	2nd_L_pool_cy3_breakthrough4	2nd_L_pool_cy3_breakthrough4	95000080.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+141	REF sources																		E_REF	2nd_REF_pool_L_cy5_breakthrough4	P-TABM-AnitaG-10115	Cy5	2nd_L_pool_cy3_breakthrough4	2nd_L_pool_cy3_breakthrough4	95000080.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+96												normal			myoepithelial				E_myoepithelial_pool	2nd_M_pool_L_cy3_breakthrough3	P-TABM-AnitaG-10115	Cy3	2nd_M_pool_cy3_breakthrough3	2nd_M_pool_cy3_breakthrough3	95000081.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+132	REF sources																		E_REF	2nd_REF_pool_L_cy5_breakthrough3	P-TABM-AnitaG-10115	Cy5	2nd_M_pool_cy3_breakthrough3	2nd_M_pool_cy3_breakthrough3	95000081.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+97												normal			myoepithelial				E_myoepithelial_pool	2nd_M_pool_L_cy3_breakthrough4	P-TABM-AnitaG-10115	Cy3	2nd_M_pool_cy3_breakthrough4	2nd_M_pool_cy3_breakthrough4	95000082.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+133	REF sources																		E_REF	2nd_REF_pool_L_cy5_breakthrough4	P-TABM-AnitaG-10115	Cy5	2nd_M_pool_cy3_breakthrough4	2nd_M_pool_cy3_breakthrough4	95000082.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+120												invasive ductual carcinoma			breast epithelial				E_tumour_pool	2nd_T_pool_L_cy3_breakthrough95000083	P-TABM-AnitaG-10115	Cy3	2nd_T_pool_cy3_breakthrough95000083	2nd_T_pool_cy3_breakthrough95000083	95000083.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+148	REF sources																		E_REF	2nd_REF_pool_L_cy5_breakthrough95000083	P-TABM-AnitaG-10115	Cy5	2nd_T_pool_cy3_breakthrough95000083	2nd_T_pool_cy3_breakthrough95000083	95000083.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+121												invasive ductual carcinoma			breast epithelial				E_tumour_pool	2nd_T_pool_L_cy3_breakthrough95000084	P-TABM-AnitaG-10115	Cy3	2nd_T_pool_cy3_breakthrough95000084	2nd_T_pool_cy3_breakthrough95000084	95000084.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+149	REF sources																		E_REF	2nd_REF_pool_L_cy5_breakthrough95000084	P-TABM-AnitaG-10115	Cy5	2nd_T_pool_cy3_breakthrough95000084	2nd_T_pool_cy3_breakthrough95000084	95000084.gpr			A-MEXP-339	P-MEXP-10140	P-MEXP-10145
+20	SAMPLE_human_723L	organism_part	Homo sapiens	"St. George's Hospital, London,UK"	adult	32	years	mammary gland	female	Human_723L	normal	normal	luminal epithelial	Reduction mammoplasties	luminal epithelial	P-MEXP-975	P-MEXP-972	P-MEXP-10844	E_luminal_pool	L_pool_affy1	P-AFFY-2	biotin	L_pool_affy1	L_pool_affy1	L1.CEL		L1.EXP	A-AFFY-44		
+21	SAMPLE_human_729L	organism_part	Homo sapiens	"St. George's Hospital, London,UK"	adult	31	years	mammary gland	female	Human_729L	normal	normal	luminal epithelial	Reduction mammoplasties	luminal epithelial	P-MEXP-975	P-MEXP-972	P-MEXP-10844	E_luminal_pool	L_pool_affy2	P-AFFY-2	biotin	L_pool_affy2	L_pool_affy2	L2.CEL		L2.EXP	A-AFFY-44		
+22	SAMPLE_human_697L	organism_part	Homo sapiens	"St. George's Hospital, London,UK"	adult	29	years	mammary gland	female	Human_697L	normal	normal	luminal epithelial	Reduction mammoplasties	luminal epithelial	P-MEXP-975	P-MEXP-972	P-MEXP-10844	E_luminal_pool	L_pool_affy3	P-AFFY-2	biotin	L_pool_affy3	L_pool_affy3	L3.CEL		L3.EXP	A-AFFY-44		
+1	SAMPLE_human_352	organism_part	Homo sapiens	"Queen Mary's University Hospital, London,UK"	adult	30	years	mammary gland	female	Human_352	normal	normal	myoepithelial	Reduction mammoplasties	myoepithelial	P-MEXP-976	P-MEXP-972	P-MEXP-10843	E_myoepithelial_pool	M_pool_affy1	P-AFFY-2	biotin	M_pool_affy1	M_pool_affy1	M1.CEL		M1.EXP	A-AFFY-44		
+2	SAMPLE_human_453	organism_part	Homo sapiens	"Queen Mary's University Hospital, London,UK"	adult	22	years	mammary gland	female	Human_453	normal	normal	myoepithelial	Reduction mammoplasties	myoepithelial	P-MEXP-976	P-MEXP-972	P-MEXP-10843	E_myoepithelial_pool	M_pool_affy2	P-AFFY-2	biotin	M_pool_affy2	M_pool_affy2	M2.CEL		M2.EXP	A-AFFY-44		
+3	SAMPLE_human_463	organism_part	Homo sapiens	"Queen Mary's University Hospital, London,UK"	adult	31	years	mammary gland	female	Human_463	normal	normal	myoepithelial	Reduction mammoplasties	myoepithelial	P-MEXP-976	P-MEXP-972	P-MEXP-10843	E_myoepithelial_pool	M_pool_affy3	P-AFFY-2	biotin	M_pool_affy3	M_pool_affy3	M3.CEL		M3.EXP	A-AFFY-44		
+39	SAMPLE_human_489	organism_part	Homo sapiens	"University College London Hospital, London,UK"	adult			mammary gland	female	Human_489	invasive ductual carcinoma	invasive ductual carcinoma	breast epithelial	"grad3,size25,vascular invasion, ESR1 positive, HER2 negative"	breast epithelial		P-MEXP-972	P-MEXP-10845	E_tumour_pool	T_pool_affy1	P-AFFY-2	biotin	T_pool_affy1	T_pool_affy1	T1.CEL		T1.EXP	A-AFFY-44		
+40	SAMPLE_human_552	organism_part	Homo sapiens	"University College London Hospital, London,UK"	adult			mammary gland	female	Human_552	invasive ductual carcinoma	invasive ductual carcinoma	breast epithelial	"grad3,size40,vascular invasion, 8/9 lymph node, ESR1 negative, HER2 negative"	breast epithelial		P-MEXP-972	P-MEXP-10845	E_tumour_pool	T_pool_affy2	P-AFFY-2	biotin	T_pool_affy2	T_pool_affy2	T2.CEL		T2.EXP	A-AFFY-44		
+41	SAMPLE_human_593	organism_part	Homo sapiens	"University College London Hospital, London,UK"	adult			mammary gland	female	Human_593	invasive lobular carcinoma	invasive lobular carcinoma	breast epithelial	"grad2,size35, 1/9 lymph node, ESR1 positive"	breast epithelial		P-MEXP-972	P-MEXP-10845	E_tumour_pool	T_pool_affy3	P-AFFY-2	biotin	T_pool_affy3	T_pool_affy3	T3.CEL		T3.EXP	A-AFFY-44		
+116												normal			luminal				E_luminal_pool	2nd_L_pool_amersham3	P-MEXP-14985	Cy5	2nd_L_pool_amersham3	2nd_L_pool_amersham3		T00250228.txt		A-GEHB-1	P-MEXP-14987	P-MEXP-14988
+117												normal			luminal				E_luminal_pool	2nd_L_pool_amersham4	P-MEXP-14985	Cy5	2nd_L_pool_amersham4	2nd_L_pool_amersham4		T00250229.txt		A-GEHB-1	P-MEXP-14987	P-MEXP-14988
+104												normal			myoepithelial				E_myoepithelial_pool	2nd_M_pool_amersham3	P-MEXP-14985	Cy5	2nd_M_pool_amersham3	2nd_M_pool_amersham3		T00250230.txt		A-GEHB-1	P-MEXP-14987	P-MEXP-14988
+105												normal			myoepithelial				E_myoepithelial_pool	2nd_M_pool_amersham4	P-MEXP-14985	Cy5	2nd_M_pool_amersham4	2nd_M_pool_amersham4		T00250231.txt		A-GEHB-1	P-MEXP-14987	P-MEXP-14988
+128												invasive ductual carcinoma			breast epithelial				E_tumour_pool	2nd_T_pool_amersham3	P-MEXP-14985	Cy5	2nd_T_pool_amersham3	2nd_T_pool_amersham3		T00250232.txt		A-GEHB-1	P-MEXP-14987	P-MEXP-14988
+129												invasive ductual carcinoma			breast epithelial				E_tumour_pool	2nd_T_pool_amersham4	P-MEXP-14985	Cy5	2nd_T_pool_amersham4	2nd_T_pool_amersham4		T00250233.txt		A-GEHB-1	P-MEXP-14987	P-MEXP-14988
+114												normal			luminal				E_luminal_pool	2nd_L_pool_amersham1	P-MEXP-14985	Cy5	2nd_L_pool_amersham1	2nd_L_pool_amersham1		T00253433.txt		A-GEHB-1	P-MEXP-14987	P-MEXP-14988
+115												normal			luminal				E_luminal_pool	2nd_L_pool_amersham2	P-MEXP-14985	Cy5	2nd_L_pool_amersham2	2nd_L_pool_amersham2		T00253434.txt		A-GEHB-1	P-MEXP-14987	P-MEXP-14988
+102												normal			myoepithelial				E_myoepithelial_pool	2nd_M_pool_amersham1	P-MEXP-14985	Cy5	2nd_M_pool_amersham1	2nd_M_pool_amersham1		T00253435.txt		A-GEHB-1	P-MEXP-14987	P-MEXP-14988
+103												normal			myoepithelial				E_myoepithelial_pool	2nd_M_pool_amersham2	P-MEXP-14985	Cy5	2nd_M_pool_amersham2	2nd_M_pool_amersham2		T00253436.txt		A-GEHB-1	P-MEXP-14987	P-MEXP-14988
+126												invasive ductual carcinoma			breast epithelial				E_tumour_pool	2nd_T_pool_amersham1	P-MEXP-14985	Cy5	2nd_T_pool_amersham1	2nd_T_pool_amersham1		T00253437.txt		A-GEHB-1	P-MEXP-14987	P-MEXP-14988
+127												invasive ductual carcinoma			breast epithelial				E_tumour_pool	2nd_T_pool_amersham2	P-MEXP-14985	Cy5	2nd_T_pool_amersham2	2nd_T_pool_amersham2		T00253438.txt		A-GEHB-1	P-MEXP-14987	P-MEXP-14988
+35												normal			luminal epithelial		P-MEXP-972	P-MEXP-10844	E_luminal_pool	L_pool_amersham1	P-MEXP-14985	Cy5	L_pool_amersham1	L_pool_amersham1		T00297797.txt		A-GEHB-1	P-MEXP-14987	P-MEXP-14988
+36												normal			luminal epithelial		P-MEXP-972	P-MEXP-10844	E_luminal_pool	L_pool_amersham2	P-MEXP-14985	Cy5	L_pool_amersham2	L_pool_amersham2		T00297799.txt		A-GEHB-1	P-MEXP-14987	P-MEXP-14988
+37												normal			luminal epithelial		P-MEXP-972	P-MEXP-10844	E_luminal_pool	L_pool_amersham3	P-MEXP-14985	Cy5	L_pool_amersham3	L_pool_amersham3		T00297800.txt		A-GEHB-1	P-MEXP-14987	P-MEXP-14988
+38												normal			luminal epithelial		P-MEXP-972	P-MEXP-10844	E_luminal_pool	L_pool_amersham4	P-MEXP-14985	Cy5	L_pool_amersham4	L_pool_amersham4		T00297801.txt		A-GEHB-1	P-MEXP-14987	P-MEXP-14988
+16												normal			myoepithelial		P-MEXP-972	P-MEXP-10843	E_myoepithelial_pool	M_pool_amersham1	P-MEXP-14985	Cy5	M_pool_amersham1	M_pool_amersham1		T00297802.txt		A-GEHB-1	P-MEXP-14987	P-MEXP-14988
+17												normal			myoepithelial		P-MEXP-972	P-MEXP-10843	E_myoepithelial_pool	M_pool_amersham2	P-MEXP-14985	Cy5	M_pool_amersham2	M_pool_amersham2		T00297803.txt		A-GEHB-1	P-MEXP-14987	P-MEXP-14988
+18												normal			myoepithelial		P-MEXP-972	P-MEXP-10843	E_myoepithelial_pool	M_pool_amersham3	P-MEXP-14985	Cy5	M_pool_amersham3	M_pool_amersham3		T00297804.txt		A-GEHB-1	P-MEXP-14987	P-MEXP-14988
+19												normal			myoepithelial		P-MEXP-972	P-MEXP-10843	E_myoepithelial_pool	M_pool_amersham4	P-MEXP-14985	Cy5	M_pool_amersham4	M_pool_amersham4		T00297805.txt		A-GEHB-1	P-MEXP-14987	P-MEXP-14988
+54												invasive ductual carcinoma			breast epithelial		P-MEXP-972	P-MEXP-10845	E_tumour_pool	T_pool_amersham1	P-MEXP-14985	Cy5	T_pool_amersham1	T_pool_amersham1		T00297808.txt		A-GEHB-1	P-MEXP-14987	P-MEXP-14988
+55												invasive ductual carcinoma			breast epithelial		P-MEXP-972	P-MEXP-10845	E_tumour_pool	T_pool_amersham2	P-MEXP-14985	Cy5	T_pool_amersham2	T_pool_amersham2		T00297810.txt		A-GEHB-1	P-MEXP-14987	P-MEXP-14988
+56												invasive ductual carcinoma			breast epithelial		P-MEXP-972	P-MEXP-10845	E_tumour_pool	T_pool_amersham3	P-MEXP-14985	Cy5	T_pool_amersham3	T_pool_amersham3		T00297811.txt		A-GEHB-1	P-MEXP-14987	P-MEXP-14988
+57												invasive ductual carcinoma			breast epithelial		P-MEXP-972	P-MEXP-10845	E_tumour_pool	T_pool_amersham4	P-MEXP-14985	Cy5	T_pool_amersham4	T_pool_amersham4		T00297812.txt		A-GEHB-1	P-MEXP-14987	P-MEXP-14988
+58	REF sources																		E_REF	REF_pool_cy5_agil1	P-TABM-AnitaG-10115	Cy5	M_pool_cy3_agil1	M_pool_cy3_agil1						
+59	REF sources																		E_REF	REF_pool_cy5_agil2	P-TABM-AnitaG-10115	Cy5	M_pool_cy3_agil2	M_pool_cy3_agil2						
+60	REF sources																		E_REF	REF_pool_cy5_agil3	P-TABM-AnitaG-10115	Cy5	M_pool_cy3_agil3	M_pool_cy3_agil3						
+61	REF sources																		E_REF	REF_pool_cy3_agil1	P-TABM-AnitaG-10115	Cy3	M_pool_cy5_agil1	M_pool_cy5_agil1						
+62	REF sources																		E_REF	REF_pool_cy3_agil2	P-TABM-AnitaG-10115	Cy3	M_pool_cy5_agil2	M_pool_cy5_agil2						
+63	REF sources																		E_REF	REF_pool_cy3_agil3	P-TABM-AnitaG-10115	Cy3	M_pool_cy5_agil3	M_pool_cy5_agil3						
+64	REF sources																		E_REF	REF_pool_cy5_breakthrough1	P-TABM-AnitaG-10115	Cy5	M_pool_cy3_breakthrough1	M_pool_cy3_breakthrough1						
+65	REF sources																		E_REF	REF_pool_cy5_breakthrough2	P-TABM-AnitaG-10115	Cy5	M_pool_cy3_breakthrough2	M_pool_cy3_breakthrough2						
+66	REF sources																		E_REF	REF_pool_cy5_breakthrough3	P-TABM-AnitaG-10115	Cy5	M_pool_cy3_breakthrough3	M_pool_cy3_breakthrough3						
+67	REF sources																		E_REF	REF_pool_cy3_breakthrough1	P-TABM-AnitaG-10115	Cy3	M_pool_cy5_breakthrough1	M_pool_cy5_breakthrough1						
+68	REF sources																		E_REF	REF_pool_cy3_breakthrough2	P-TABM-AnitaG-10115	Cy3	M_pool_cy5_breakthrough2	M_pool_cy5_breakthrough2						
+69	REF sources																		E_REF	REF_pool_cy3_breakthrough3	P-TABM-AnitaG-10115	Cy3	M_pool_cy5_breakthrough3	M_pool_cy5_breakthrough3						
+70	REF sources																		E_REF	REF_pool_cy5_agil1	P-TABM-AnitaG-10115	Cy5	L_pool_cy3_agil1	L_pool_cy3_agil1						
+71	REF sources																		E_REF	REF_pool_cy5_agil2	P-TABM-AnitaG-10115	Cy5	L_pool_cy3_agil2	L_pool_cy3_agil2						
+72	REF sources																		E_REF	REF_pool_cy5_agil3	P-TABM-AnitaG-10115	Cy5	L_pool_cy3_agil3	L_pool_cy3_agil3						
+73	REF sources																		E_REF	REF_pool_cy3_agil1	P-TABM-AnitaG-10115	Cy3	L_pool_cy5_agil1	L_pool_cy5_agil1						
+74	REF sources																		E_REF	REF_pool_cy3_agil2	P-TABM-AnitaG-10115	Cy3	L_pool_cy5_agil2	L_pool_cy5_agil2						
+75	REF sources																		E_REF	REF_pool_cy3_agil3	P-TABM-AnitaG-10115	Cy3	L_pool_cy5_agil3	L_pool_cy5_agil3						
+76	REF sources																		E_REF	REF_pool_cy5_breakthrough1	P-TABM-AnitaG-10115	Cy5	L_pool_cy3_breakthrough1	L_pool_cy3_breakthrough1						
+77	REF sources																		E_REF	REF_pool_cy5_breakthrough2	P-TABM-AnitaG-10115	Cy5	L_pool_cy3_breakthrough2	L_pool_cy3_breakthrough2						
+78	REF sources																		E_REF	REF_pool_cy5_breakthrough3	P-TABM-AnitaG-10115	Cy5	L_pool_cy3_breakthrough3	L_pool_cy3_breakthrough3						
+79	REF sources																		E_REF	REF_pool_cy3_breakthrough1	P-TABM-AnitaG-10115	Cy3	L_pool_cy5_breakthrough1	L_pool_cy5_breakthrough1						
+80	REF sources																		E_REF	REF_pool_cy3_breakthrough2	P-TABM-AnitaG-10115	Cy3	L_pool_cy5_breakthrough2	L_pool_cy5_breakthrough2						
+81	REF sources																		E_REF	REF_pool_cy3_breakthrough3	P-TABM-AnitaG-10115	Cy3	L_pool_cy5_breakthrough3	L_pool_cy5_breakthrough3						
+82	REF sources																		E_REF	REF_pool_cy5_agil1	P-TABM-AnitaG-10115	Cy5	T_pool_cy3_agil1	T_pool_cy3_agil1						
+83	REF sources																		E_REF	REF_pool_cy5_agil2	P-TABM-AnitaG-10115	Cy5	T_pool_cy3_agil2	T_pool_cy3_agil2						
+84	REF sources																		E_REF	REF_pool_cy5_agil3	P-TABM-AnitaG-10115	Cy5	T_pool_cy3_agil3	T_pool_cy3_agil3						
+85	REF sources																		E_REF	REF_pool_cy3_agil1	P-TABM-AnitaG-10115	Cy3	T_pool_cy5_agil1	T_pool_cy5_agil1						
+86	REF sources																		E_REF	REF_pool_cy3_agil2	P-TABM-AnitaG-10115	Cy3	T_pool_cy5_agil2	T_pool_cy5_agil2						
+87	REF sources																		E_REF	REF_pool_cy3_agil3	P-TABM-AnitaG-10115	Cy3	T_pool_cy5_agil3	T_pool_cy5_agil3						
+88	REF sources																		E_REF	REF_pool_cy5_breakthrough1	P-TABM-AnitaG-10115	Cy5	T_pool_cy3_breakthrough1	T_pool_cy3_breakthrough1						
+89	REF sources																		E_REF	REF_pool_cy5_breakthrough2	P-TABM-AnitaG-10115	Cy5	T_pool_cy3_breakthrough2	T_pool_cy3_breakthrough2						
+90	REF sources																		E_REF	REF_pool_cy5_breakthrough3	P-TABM-AnitaG-10115	Cy5	T_pool_cy3_breakthrough3	T_pool_cy3_breakthrough3						
+91	REF sources																		E_REF	REF_pool_cy3_breakthrough1	P-TABM-AnitaG-10115	Cy3	T_pool_cy5_breakthrough1	T_pool_cy5_breakthrough1						
+92	REF sources																		E_REF	REF_pool_cy3_breakthrough2	P-TABM-AnitaG-10115	Cy3	T_pool_cy5_breakthrough2	T_pool_cy5_breakthrough2						
+93	REF sources																		E_REF	REF_pool_cy3_breakthrough3	P-TABM-AnitaG-10115	Cy3	T_pool_cy5_breakthrough3	T_pool_cy5_breakthrough3						
diff --git a/examples/real/E-TABM-70.png b/examples/real/E-TABM-70.png
new file mode 100644
index 0000000..a847c87
Binary files /dev/null and b/examples/real/E-TABM-70.png differ
diff --git a/examples/real/E-TABM-70.txt b/examples/real/E-TABM-70.txt
new file mode 100755
index 0000000..3f5443c
--- /dev/null
+++ b/examples/real/E-TABM-70.txt
@@ -0,0 +1,101 @@
+Experiment section	
+domain	ebi.ac.uk
+accession	E-TABM-70
+quality_control	dye_swap_quality_control
+experiment_design_type	compound_treatment_design
+name	IGR_PLOIDY_STUDY_GK
+description	"During the oncogenic process, tetraploidy is a candidate intermediate stage leading from diploidy to aneuploidy. The aim of our experiment was to establish tetraploid clones and to characterize their transcriptome."
+release_date	2006-01-23
+submission_date	2005-12-19
+submitter	Philippe Dessen
+submitter_email	dessen at igr.fr
+publication_title	Apoptosis regulation in tetraploid cells
+authors	Maria Castedo; Arud Coquelle; Sonia Vivet; Audrey Kauffmann; Marie O. Pequignot; Noelia Casares; Alexander Valent; Shahul Mouhamad; Elise Schmitt; Nazanine Modjtahedi; William Vainchenker; Laurence Zitvogel; Vladimir Lazar; Carmen Garrido; Guido Kroemer
+address	"Centre Natiol de la Recherche Scientifique, UMR8125,�; Unit� de G�nomique Fonctionnelle, Institut Gustave Roussy, 39 rue Camille-Desmoulins, F-94805 Villejuif, France; INSERM U-517, Faculty of Medicine and Pharmacy, 7 Boulevard Jeanne d;Arc, 21033 Dijon, France�; INSERM U362,Institut Gustave Roussy, 39 rue Camille-Desmoulins, F-94805 Villejuif, France; Unit� d�Immunologie, ERM0208 INSERM, IFR54, Institut Gustave Roussy, Villejuif, France"
+journal	EMBO J
+volume	revision
+issue			
+pages			
+year	2006		
+
+Protocol section			
+accession	type	text	parameters
+P_IGR_SAMPLE_04_TETRA_1	compound_based_treatment	"RKO cells were grown in McCoy' 5A medium supplemented with 10%FCS. In order to generate tetraploid and diploid clones, the cell line RKO containing ~5% tetraploid cells was subcloned by limiting dilution into diploid and tetraploid clones. One diploid clone was transfected with a cDNA encoding H2B-GFP, selected in blasticidine (20 �g/ml), FACS-separated into subsets of cells enriched in a diploid or tetraploid DNA content to generate dipl [...]
+P_IGR_SAMPLE_04_TETRA_2	compound_based_treatment	"Tetraploid HCT116 clones were generated upon treatment with cytochalasin D (0.6 �g/ml, 48h) or nocodazole (100nM, 48h).  For microarray assay, established diploid and tetraploid clones were collected during exponential growth with a cell scrapper in 1ml RLT lysis buffer with beta-mercaptoethanol (10ml/ml)."	
+P_IGR_EXTRACT_04	nucleic_acid_extraction	"Total RNA was isolated from using Rneasy micro elute kit (Qiagen, Courtaboeuf, France) according to manufacturer instructions. Briefly Cells was mixed with 350 ul RLT buffer with Beta Mercaptoethanol 1%. Then add 350 ul ethanol 70% and mixed thoroughly by"	
+P_IGR_POOL_04A	pool	"Pools of RNA (Pool1 or Pool2), obtained by mixing equal amounts (500 ng) of total RNA from each of the 8 cell line samples, constituted the reference in each group. "	
+P_IGR_POOL_04B	pool	"Pools of RNA (Pool1 or Pool2), obtained by mixing equal amounts (500 ng) of total RNA from each of the 8 cell line samples, constituted the reference in each group. "	
+P_IGR_LABEL_CY3_04	labeling	"(500 ng, Amplification=RNA polymerases). Agilent oligo Cy5 or Cy3 probes labeling protocol. Kit used for probe labeling: Agilent Fluorescent Low input Linear Amplification kit (G2554A) adapted for small amount of total RNA (500 ng total RNA per reaction)."	
+P_IGR_LABEL_CY5_04	labeling	"(500 ng, Amplification=RNA polymerases). Agilent oligo Cy5 or Cy3 probes labeling protocol. Kit used for probe labeling: Agilent Fluorescent Low input Linear Amplification kit (G2554A) adapted for small amount of total RNA (500 ng total RNA per reaction)."	
+P_IGR_HYBRID_04	hybridization	 Agilent Hybridization Protocol (Chamber type: Agilent SureHyb Chamber; Quantity of labelled extract: 1 ug; duration: 17 hours; volume: 650 ul ; Temperature in �C: 60). Add the following components in clean 1.5 ml tubes. 1 ug linearly amplified cRNA la	
+P_IGR_SCANNING_04	image_acquisition	"Scanning was performed with a Agilent 2565 AA DNA Microarray scanner using defaults parameters (100� PMT, 10 um resolution, at 20�C in low ozone concentration environment. Microarray images were alysed by using Feature Extraction software version A.7.5.1 from Agilent technologies. Defaults settings were used."	
+P_IGR_TRANSFORM_04	Transformation protocol	"Raw data files from the Agilent Feature Extraction software A.6.1.1 for image alysis were then imported into Resolver(TM) system for gene expression data alysis from Rosetta Inpharmatics. Then combined experiments were generated, to obtain average values from the replicates and dye swap experiments in order to avoid dye incorporation bias. Microarray data were processed and combined using the Rosetta Resolver system described in Stoughton and D [...]
+
+
+
+Hybridization section																											
+File[raw]	Array[accession]	Array[serial]	Protocol[treatment]	Protocol[extraction]	Protocol[pool]	Protocol[labeling]	Protocol[hybridization]	Protocol[scanning]	Protocol[transformation]	BioSource	Sample	Extract	LabeledExtract	Dye	Hybridization	File[transformed]	TransformationType	BioSourceMaterial	ExtractMaterial	LabeledExtractMaterial	FactorValue[CellLine]	FactorValue[ploidy]	BioMaterialCharacteristics[Organism]	BioMaterialCharacteristics[Provider]	BioMaterialCharacteristics[CellLine]	Bio [...]
+US14702370_251239119523_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	DW	DW sample	DW extract	DW Cy5	Cy5	DW Cy5_Pool1 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO	diploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO	clone selection as diploid	diploid
+US14702370_251239119523_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool1	Pool1 sample	Pool1 extract	Pool1 Cy3	Cy3	DW Cy5_Pool1 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO		
+US14702370_251239119541_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	DW	DW sample	DW extract	DW Cy3	Cy3	DW Cy3_Pool1 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO	diploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO	clone selection as diploid	diploid
+US14702370_251239119541_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool1	Pool1 sample	Pool1 extract	Pool1 Cy5	Cy5	DW Cy3_Pool1 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO		
+US14702370_251239119609_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	DX	DX sample	DX extract	DX Cy5	Cy5	DX Cy5_Pool1 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO	diploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO	clone selection as diploid	diploid
+US14702370_251239119609_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool1	Pool1 sample	Pool1 extract	Pool1 Cy3	Cy3	DX Cy5_Pool1 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO		
+US14702370_251239119524_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	DX	DX sample	DX extract	DX Cy3	Cy3	DX Cy3_Pool1 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO	diploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO	clone selection as diploid	diploid
+US14702370_251239119524_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool1	Pool1 sample	Pool1 extract	Pool1 Cy5	Cy5	DX Cy3_Pool1 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO		
+US14702370_251239119530_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	DY	DY sample	DY extract	DY Cy5	Cy5	DY Cy5_Pool1 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO	diploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO	clone selection as diploid	diploid
+US14702370_251239119530_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool1	Pool1 sample	Pool1 extract	Pool1 Cy3	Cy3	DY Cy5_Pool1 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO		
+US14702370_251239119525_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	DY	DY sample	DY extract	DY Cy3	Cy3	DY Cy3_Pool1 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO	diploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO	clone selection as diploid	diploid
+US14702370_251239119525_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool1	Pool1 sample	Pool1 extract	Pool1 Cy5	Cy5	DY Cy3_Pool1 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO		
+US14702370_251239119531_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	DZ	DZ sample	DZ extract	DZ Cy5	Cy5	DZ Cy5_Pool1 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO	diploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO	clone selection as diploid	diploid
+US14702370_251239119531_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool1	Pool1 sample	Pool1 extract	Pool1 Cy3	Cy3	DZ Cy5_Pool1 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO		
+US14702370_251239119614_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	DZ	DZ sample	DZ extract	DZ Cy3	Cy3	DZ Cy3_Pool1 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO	diploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO	clone selection as diploid	diploid
+US14702370_251239119614_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool1	Pool1 sample	Pool1 extract	Pool1 Cy5	Cy5	DZ Cy3_Pool1 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO		
+US14702370_251239119519_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	TA	TA sample	TA extract	TA Cy5	Cy5	TA Cy5_Pool1 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO	tetraploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO	clone selection as tetraploid	tetraploid
+US14702370_251239119519_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool1	Pool1 sample	Pool1 extract	Pool1 Cy3	Cy3	TA Cy5_Pool1 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO		
+US14702370_251239119487_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	TA	TA sample	TA extract	TA Cy3	Cy3	TA Cy3_Pool1 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO	tetraploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO	clone selection as tetraploid	tetraploid
+US14702370_251239119487_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool1	Pool1 sample	Pool1 extract	Pool1 Cy5	Cy5	TA Cy3_Pool1 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO		
+US14702370_251239119520_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	TB	TB sample	TB extract	TB Cy5	Cy5	TB Cy5_Pool1 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO	tetraploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO	clone selection as tetraploid	tetraploid
+US14702370_251239119520_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool1	Pool1 sample	Pool1 extract	Pool1 Cy3	Cy3	TB Cy5_Pool1 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO		
+US14702370_251239119489_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	TB	TB sample	TB extract	TB Cy3	Cy3	TB Cy3_Pool1 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO	tetraploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO	clone selection as tetraploid	tetraploid
+US14702370_251239119489_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool1	Pool1 sample	Pool1 extract	Pool1 Cy5	Cy5	TB Cy3_Pool1 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO		
+US14702370_251239119608_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	TC	TC sample	TC extract	TC Cy5	Cy5	TC Cy5_Pool1 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO	tetraploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO	clone selection as tetraploid	tetraploid
+US14702370_251239119608_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool1	Pool1 sample	Pool1 extract	Pool1 Cy3	Cy3	TC Cy5_Pool1 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO		
+US14702370_251239119538_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	TC	TC sample	TC extract	TC Cy3	Cy3	TC Cy3_Pool1 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO	tetraploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO	clone selection as tetraploid	tetraploid
+US14702370_251239119538_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool1	Pool1 sample	Pool1 extract	Pool1 Cy5	Cy5	TC Cy3_Pool1 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO		
+US14702370_251239119522_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	TD	TD sample	TD extract	TD Cy5	Cy5	TD Cy5_Pool1 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO	tetraploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO	clone selection as tetraploid	tetraploid
+US14702370_251239119522_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool1	Pool1 sample	Pool1 extract	Pool1 Cy3	Cy3	TD Cy5_Pool1 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO		
+US14702370_251239119540_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	TD	TD sample	TD extract	TD Cy3	Cy3	TD Cy3_Pool1 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO	tetraploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO	clone selection as tetraploid	tetraploid
+US14702370_251239119540_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_1	P_IGR_EXTRACT_04	P_IGR_POOL_04A	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool1	Pool1 sample	Pool1 extract	Pool1 Cy5	Cy5	TD Cy3_Pool1 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	colon RKO		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	colon RKO		
+US14702370_251239124865_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Co3	Co3 sample	Co3 extract	Co3 Cy5	Cy5	Co3 Cy5-Pool2 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116	diploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116		diploid
+US14702370_251239124865_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool2	Pool2 sample	Pool2 extract	Pool2 Cy5	Cy5	Co3 Cy5-Pool2 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116		
+US14702370_251239124928_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Co3	Co3 sample	Co3 extract	Co3 Cy3	Cy3	Co3 Cy3-Pool2 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116	diploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116		diploid
+US14702370_251239124928_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool2	Pool2 sample	Pool2 extract	Pool2 Cy3	Cy3	Co3 Cy3-Pool2 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116		
+US14702370_251239124866_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Co4	Co4 sample	Co4 extract	Co4 Cy5	Cy5	Co4 Cy5-Pool2 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116	diploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116		diploid
+US14702370_251239124866_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool2	Pool2 sample	Pool2 extract	Pool2 Cy5	Cy5	Co4 Cy5-Pool2 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116		
+US14702370_251239124929_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Co4	Co4 sample	Co4 extract	Co4 Cy3	Cy3	Co4 Cy3-Pool2 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116	diploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116		diploid
+US14702370_251239124929_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool2	Pool2 sample	Pool2 extract	Pool2 Cy3	Cy3	Co4 Cy3-Pool2 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116		
+US14702370_251239124869_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Co1	Co1 sample	Co1 extract	Co1 Cy5	Cy5	Co1 Cy5-Pool2 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116	diploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116		diploid
+US14702370_251239124869_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool2	Pool2 sample	Pool2 extract	Pool2 Cy5	Cy5	Co1 Cy5-Pool2 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116		
+US14702370_251239124932_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Co1	Co1 sample	Co1 extract	Co1 Cy3	Cy3	Co1 Cy3-Pool2 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116	diploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116		diploid
+US14702370_251239124932_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool2	Pool2 sample	Pool2 extract	Pool2 Cy3	Cy3	Co1 Cy3-Pool2 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116		
+US14702370_251239124925_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Co2	Co2 sample	Co2 extract	Co2 Cy5	Cy5	Co2 Cy5-Pool2 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116	diploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116		diploid
+US14702370_251239124925_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool2	Pool2 sample	Pool2 extract	Pool2 Cy5	Cy5	Co2 Cy5-Pool2 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116		
+US14702370_251239124933_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Co2	Co2 sample	Co2 extract	Co2 Cy3	Cy3	Co2 Cy3-Pool2 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116	diploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116		diploid
+US14702370_251239124933_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool2	Pool2 sample	Pool2 extract	Pool2 Cy3	Cy3	Co2 Cy3-Pool2 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116		
+US14702370_251239124926_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	C1	C1 sample	C1 extract	C1 Cy5	Cy5	C1 Cy5-Pool2 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116	tetraploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116	cytochalasin D 	tetraploid
+US14702370_251239124926_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool2	Pool2 sample	Pool2 extract	Pool2 Cy5	Cy5	C1 Cy5-Pool2 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116		
+US14702370_251239124934_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	C1	C1 sample	C1 extract	C1 Cy3	Cy3	C1 Cy3-Pool2 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116	tetraploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116	cytochalasin D 	tetraploid
+US14702370_251239124934_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool2	Pool2 sample	Pool2 extract	Pool2 Cy3	Cy3	C1 Cy3-Pool2 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116		
+US14702370_251239124927_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	C2	C2 sample	C2 extract	C2 Cy5	Cy5	C2 Cy5-Pool2 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116	tetraploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116	cytochalasin D 	tetraploid
+US14702370_251239124927_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool2	Pool2 sample	Pool2 extract	Pool2 Cy5	Cy5	C2 Cy5-Pool2 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116		
+US14702370_251239124940_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	C2	C2 sample	C2 extract	C2 Cy3	Cy3	C2 Cy3-Pool2 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116	tetraploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116	cytochalasin D 	tetraploid
+US14702370_251239124940_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool2	Pool2 sample	Pool2 extract	Pool2 Cy3	Cy3	C2 Cy3-Pool2 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116		
+US14702370_251239124867_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	N1	N1 sample	N1 extract	N1 Cy5	Cy5	N1 Cy5-Pool2 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116	tetraploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116	nocodazole	tetraploid
+US14702370_251239124867_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool2	Pool2 sample	Pool2 extract	Pool2 Cy5	Cy5	N1 Cy5-Pool2 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116		
+US14702370_251239124930_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	N1	N1 sample	N1 extract	N1 Cy3	Cy3	N1 Cy3-Pool2 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116	tetraploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116	nocodazole	tetraploid
+US14702370_251239124930_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool2	Pool2 sample	Pool2 extract	Pool2 Cy3	Cy3	N1 Cy3-Pool2 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116		
+US14702370_251239124868_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	N2	N2 sample	N2 extract	N2 Cy5	Cy5	N2 Cy5-Pool2 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116	tetraploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116	nocodazole	tetraploid
+US14702370_251239124868_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY5_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool2	Pool2 sample	Pool2 extract	Pool2 Cy5	Cy5	N2 Cy5-Pool2 Cy5 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116		
+US14702370_251239124931_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	N2	N2 sample	N2 extract	N2 Cy3	Cy3	N2 Cy3-Pool2 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116	tetraploid	Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116	nocodazole	tetraploid
+US14702370_251239124931_S01_A01.txt	A-MEXP-304	2.51E+11	P_IGR_SAMPLE_04_TETRA_2	P_IGR_EXTRACT_04	P_IGR_POOL_04B	P_IGR_LABEL_CY3_04	P_IGR_HYBRID_04	P_IGR_SCANNING_04	P_IGR_TRANSFORM_04	Pool2	Pool2 sample	Pool2 extract	Pool2 Cy3	Cy3	N2 Cy3-Pool2 Cy3 hyb	igr_ploidy_gk_all_seqs_all_exps_hp.txt	lowess	cell line	total_RNA	synthetic_RNA	HCT116		Homo sapiens	"CNRS-UMR8125,  Institut Gustave Roussy, 94805 Villejuif, France"	HCT116		
diff --git a/examples/reference/Data1.txt b/examples/reference/Data1.txt
new file mode 100644
index 0000000..157be04
--- /dev/null
+++ b/examples/reference/Data1.txt
@@ -0,0 +1,37 @@
+ATF	1.0
+24	43    
+"Type=GenePix Results 1.2"
+"DateTime=2003/01/23 11:27:59"
+"Settings=E:\settings files\array.gps"
+"GalFile="
+"Scanner=GenePix 4000A"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm	532 nm"
+"FileName=\\Server\Data1.tif"
+"PMTVolts=600	590"
+"NormalizationFactor:RatioOfMedians=1.90064"
+"NormalizationFactor:RatioOfMeans=1.90384"
+"NormalizationFactor:MedianOfRatios=1.86554"
+"NormalizationFactor:MeanOfRatios=1.23878"
+"NormalizationFactor:RegressionRatio=2.45261"
+"JpegImage=\\Server\Data1.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=1240, 6320"
+"JpegOrigin=1840, 7160"
+"Creator=GenePix Pro 3.0.0.98"
+"Temperature=1.41243"
+"LaserPower=2.19092	0.863674"
+"LaserOnTime=86000	85977"
+"Block"	"Column"	"Row"	"Name"	"ID"	"X"	"Y"	"Dia."	"F635 Median"	"F635 Mean"	"F635 SD"	"B635 Median"	"B635 Mean"	"B635 SD"	"% > B635+1SD"	"% > B635+2SD"	"F635 % Sat."	"F532 Median"	"F532 Mean"	"F532 SD"	"B532 Median"	"B532 Mean"	"B532 SD"	"% > B532+1SD"	"% > B532+2SD"	"F532 % Sat."	"Ratio of Medians"	"Ratio of Means"	"Median of Ratios"	"Mean of Ratios"	"Ratios SD"	"Rgn Ratio"	"Rgn R�"	"F Pixels"	"B Pixels"	"Sum of Medians"	"Sum of Means"	"Log Ratio"	"F635 Median - B635"	"F532 Median - B53 [...]
+1	1	1			2100	7300	90	240	240	54	40	44	21	100	100	0	502	497	98	81	86	32	100	100	0	0.475	0.481	0.494	0.491	0.129	0.413	0.731	52	376	621	616	-1.074	200	421	200	416	0
+1	2	1			2410	7300	120	694	671	209	40	41	11	98	98	0	1406	1354	413	81	81	20	100	100	0	0.494	0.496	0.500	0.599	0.409	0.433	0.777	120	778	1979	1904	-1.019	654	1325	631	1273	0
+1	3	1			2690	7320	120	501	531	267	41	41	10	99	99	0	998	1080	498	79	82	21	99	98	0	0.501	0.490	0.493	0.641	1.280	0.460	0.837	120	810	1379	1491	-0.998	460	919	490	1001	0
+1	4	1			2980	7310	120	783	770	284	40	41	10	99	96	0	1034	1014	331	81	83	22	100	99	0	0.780	0.782	0.819	1.372	3.252	0.675	0.722	120	796	1696	1663	-0.359	743	953	730	933	0
+1	5	1			3260	7320	130	457	434	219	40	41	10	100	99	0	998	938	493	81	83	21	100	98	0	0.455	0.460	0.450	0.600	0.632	0.382	0.751	120	930	1334	1251	-1.137	417	917	394	857	0
+1	6	1			3570	7310	130	166	172	67	40	41	10	94	94	0	382	388	112	82	83	20	99	98	0	0.420	0.431	0.473	0.696	2.121	0.362	0.589	120	914	426	438	-1.252	126	300	132	306	0
+1	7	1			3860	7320	100	63	65	17	40	41	10	76	55	0	182	188	53	79	80	20	100	96	0	0.223	0.229	0.226	0.313	0.289	0.160	0.283	80	552	126	134	-2.163	23	103	25	109	0
+1	8	1			4130	7330	120	415	392	122	40	41	10	96	95	0	793	779	215	79	81	20	98	97	0	0.525	0.503	0.522	0.741	1.710	0.445	0.754	120	816	1089	1052	-0.929	375	714	352	700	0
+1	9	1			4430	7320	120	153	155	61	39	41	10	97	94	0	374	382	125	80	82	20	99	99	0	0.388	0.384	0.368	0.443	0.369	0.351	0.721	120	796	408	418	-1.367	114	294	116	302	0
+1	10	1			4730	7330	120	865	826	343	40	41	10	100	98	0	1011	941	389	79	81	21	99	98	0	0.885	0.912	0.923	1.477	3.031	0.751	0.698	120	810	1757	1648	-0.176	825	932	786	862	0
diff --git a/examples/reference/Data2.txt b/examples/reference/Data2.txt
new file mode 100644
index 0000000..f26132d
--- /dev/null
+++ b/examples/reference/Data2.txt
@@ -0,0 +1,40 @@
+ATF	1.0
+27	43    
+"Type=GenePix Results 1.4"
+"DateTime=2003/05/19 15:39:10"
+"Settings=E:\settings files\arrays.gps"
+"GalFile="
+"Scanner=GenePix 4000A [54807]"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm	532 nm"
+"FileName=\\Server\Data2.tif"
+"PMTVolts=680	610"
+"ScanPower=100	100"
+"FocusPosition=0"
+"NormalizationFactor:RatioOfMedians=0.93502"
+"NormalizationFactor:RatioOfMeans=0.917873"
+"NormalizationFactor:MedianOfRatios=0.933921"
+"NormalizationFactor:MeanOfRatios=0.954675"
+"NormalizationFactor:RegressionRatio=0.787143"
+"JpegImage=\\Server\Data2.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=560, 5360"
+"JpegOrigin=1650, 6810"
+"Creator=GenePix Pro 3.0.6.90"
+"Temperature=41.3353"
+"LaserPower=1.50975	1.13071"
+"LaserOnTime=41930	44902"
+"Supplier="
+"Block"	"Column"	"Row"	"Name"	"ID"	"X"	"Y"	"Dia."	"F635 Median"	"F635 Mean"	"F635 SD"	"B635 Median"	"B635 Mean"	"B635 SD"	"% > B635+1SD"	"% > B635+2SD"	"F635 % Sat."	"F532 Median"	"F532 Mean"	"F532 SD"	"B532 Median"	"B532 Mean"	"B532 SD"	"% > B532+1SD"	"% > B532+2SD"	"F532 % Sat."	"Ratio of Medians"	"Ratio of Means"	"Median of Ratios"	"Mean of Ratios"	"Ratios SD"	"Rgn Ratio"	"Rgn R�"	"F Pixels"	"B Pixels"	"Sum of Medians"	"Sum of Means"	"Log Ratio"	"F635 Median - B635"	"F532 Median - B53 [...]
+1	1	1	""	""	2040	7130	110	112	120	55	49	53	24	80	63	0	191	200	78	50	51	13	100	100	0	0.447	0.473	0.502	0.428	2.467	0.472	0.332	80	656	204	221	-1.162	63	141	71	150	0
+1	2	1	""	""	2330	7120	120	412	435	197	51	55	26	95	95	0	731	843	375	50	51	11	99	99	0	0.530	0.484	0.477	0.503	3.009	0.418	0.611	120	820	1042	1177	-0.916	361	681	384	793	0
+1	3	1	""	""	2610	7130	120	297	367	232	52	56	25	95	90	0	417	587	397	51	52	12	97	95	0	0.669	0.588	0.592	0.604	3.170	0.511	0.592	120	820	611	851	-0.579	245	366	315	536	0
+1	4	1	""	""	2900	7120	120	214	297	230	50	54	25	92	88	0	272	412	327	51	52	12	94	93	0	0.742	0.684	0.755	0.675	3.312	0.576	0.412	120	820	385	608	-0.430	164	221	247	361	0
+1	5	1	""	""	3200	7130	120	355	386	203	52	56	26	92	91	0	683	701	245	51	52	12	100	100	0	0.479	0.514	0.506	0.502	2.251	0.507	0.593	120	820	935	984	-1.061	303	632	334	650	0
+1	6	1	""	""	3500	7110	120	134	135	56	52	55	26	82	71	0	213	209	61	51	52	14	95	93	0	0.506	0.525	0.546	0.488	3.047	0.532	0.398	120	810	244	241	-0.982	82	162	83	158	0
+1	7	1	""	""	3770	7120	80	79	80	33	50	54	27	51	26	0	100	100	26	51	52	11	98	82	0	0.592	0.612	1.025	0.705	2.930	1.819	0.071	52	340	78	79	-0.757	29	49	30	49	0
+1	8	1	""	""	4070	7120	120	332	339	152	55	59	27	92	90	0	609	613	227	52	52	12	95	95	0	0.497	0.506	0.543	0.522	3.195	0.482	0.645	120	820	834	845	-1.008	277	557	284	561	0
+1	9	1	""	""	4370	7090	110	167	179	82	53	57	26	91	86	0	240	254	102	52	54	15	97	96	0	0.606	0.624	0.669	0.614	2.841	0.577	0.414	80	616	302	328	-0.722	114	188	126	202	0
+1	10	1	""	""	4650	7110	130	241	291	200	54	57	25	90	85	0	371	522	320	51	52	12	99	99	0	0.584	0.503	0.457	0.508	2.361	0.481	0.581	120	930	507	708	-0.775	187	320	237	471	0
diff --git a/examples/reference/Data3.txt b/examples/reference/Data3.txt
new file mode 100644
index 0000000..c07040f
--- /dev/null
+++ b/examples/reference/Data3.txt
@@ -0,0 +1,11 @@
+MetaColumn	MetaRow	Column	Row	Name	ID	X	Y	Dia.	F635 Median	F635 Mean	F635 SD	B635 Median	B635 Mean	B635 SD	% > B635+1SD	% > B635+2SD	F635 % Sat.	F532 Median	F532 Mean	F532 SD	B532 Median	B532 Mean	B532 SD	% > B532+1SD	% > B532+2SD	F532 % Sat.	Ratio of Medians (635/532)	Ratio of Means (635/532)	Median of Ratios (635/532)	Mean of Ratios (635/532)	Ratios SD (635/532)	Rgn Ratio (635/532)	Rgn R2 (635/532)	F Pixels	B Pixels	Sum of Medians (635/532)	Sum of Means (635/532)	Log Ratio (635/532)	F6 [...]
+1	1	1	1	iYAR047C-1	8	2310	13480	130	2247	2246	594	59	64	55	100	100	0	1545	1594	427	69	73	43	100	100	0	1.482	1.434	1.458	1.431	1.368	1.453	0.926	120	930	3664	3712	0.568	2188	1476	2187	1525	0
+1	1	2	1	iYARCdelta8-1	16	2580	13480	130	4636	4620	697	59	82	190	100	100	0	3868	3838	726	68	95	209	100	100	0	1.204	1.21	1.215	1.226	1.188	1.181	0.961	120	930	8377	8331	0.268	4577	3800	4561	3770	0
+1	1	3	1	iYAR060C	24	2860	13480	130	2696	2751	499	59	84	202	100	100	0	2228	2284	440	67	97	211	100	100	0	1.22	1.214	1.218	1.217	1.115	1.21	0.951	120	930	4798	4909	0.287	2637	2161	2692	2217	0
+1	1	4	1	iYBL106C	104	3130	13480	130	926	895	171	57	72	104	100	100	0	739	735	136	68	89	152	100	97	0	1.295	1.256	1.279	1.255	1.249	1.249	0.932	120	930	1540	1505	0.373	869	671	838	667	0
+1	1	5	1	iYBL102W	112	3440	13480	110	424	440	171	55	60	23	100	98	0	312	330	111	68	70	21	100	100	0	1.512	1.469	1.449	1.417	1.574	1.554	0.826	80	648	613	647	0.597	369	244	385	262	0
+1	1	6	1	iYBL099W	120	3680	13480	130	1260	1258	176	53	62	71	100	100	0	947	951	128	66	76	76	100	100	0	1.37	1.362	1.349	1.363	1.166	1.347	0.94	120	916	2088	2090	0.454	1207	881	1205	885	0
+1	1	7	1	iYBL081W	200	3960	13490	120	459	462	102	53	53	14	100	100	0	378	386	98	64	66	17	100	100	0	1.293	1.27	1.278	1.3	1.361	1.251	0.886	120	820	720	731	0.371	406	314	409	322	0
+1	1	8	1	iYBL076C	208	4250	13490	130	597	592	108	52	55	25	100	100	0	458	472	91	63	66	22	100	100	0	1.38	1.32	1.332	1.325	1.216	1.346	0.897	120	924	940	949	0.464	545	395	540	409	0
+1	1	9	1	iYBL072C	216	4510	13490	140	1377	1442	565	51	53	16	100	100	0	972	1019	418	62	64	18	100	100	0	1.457	1.454	1.484	1.471	1.377	1.447	0.9	156	1098	2236	2348	0.543	1326	910	1391	957	0
+1	1	10	1	iYBL052C	296	4800	13490	120	356	377	155	50	52	15	100	100	0	286	291	101	63	65	17	100	100	0	1.372	1.434	1.46	1.409	1.498	1.516	0.832	120	814	529	555	0.456	306	223	327	228	0
diff --git a/examples/reference/reference.png b/examples/reference/reference.png
new file mode 100644
index 0000000..59a924e
Binary files /dev/null and b/examples/reference/reference.png differ
diff --git a/examples/reference/reference.txt b/examples/reference/reference.txt
new file mode 100644
index 0000000..6ae99ef
--- /dev/null
+++ b/examples/reference/reference.txt
@@ -0,0 +1,42 @@
+# Simple example summary spreadsheet for tab2mage.pl script.																				
+# This file illustrates how pooling of samples into a reference channel may be accomplished.																				
+# Lines starting with a '#' are treated as comments and are ignored.																				
+																				
+Experiment section																				
+domain	ebi.ac.uk																			
+accession	E-EXML-5																			
+quality_control	dye_swap_quality_control																			
+experiment_design_type	strain_or_line_design																			
+name	Transcriptome analysis of invasive verses non-invasive strains of budding yeast 																			
+description	An experiment was performed to...																			
+release_date	2004-08-30																			
+submission_date	2004-07-28																			
+submitter	John Falstaff																			
+# The publication details below are fictitious																				
+publication_title	Comparison of the transcriptomes of invasive verses non-invasive strains of budding yeast 																			
+authors	John Falstaff; Robin Goodfellow																			
+journal	Nature Genetics																			
+volume	12																			
+issue	4																			
+pages	123-456																			
+year	2004																			
+																				
+# Protocol text may be formatted using standard HTML tags																				
+Protocol section																				
+accession	text	name																		
+P-EXML-1	Cells were grown in YPD (1% yeast extract/2% peptone/2% glucose) to an OD600 of approximately 0.8	Yeast growth																		
+P-EXML-2	Your protocol text here	Yeast cell harvesting																		
+P-EXML-3	Your protocol text here	Cell lysis and RNA prep																		
+P-EXML-4	Your protocol text here	cDNA labeling																		
+P-EXML-5	Your protocol text here	Hybridization																		
+P-EXML-6	Your protocol text here	Scanning																		
+P-EXML-7	Your protocol text here	Extract pooling																		
+																				
+Hybridization section																				
+BioMaterialCharacteristics[Organism]	BioMaterialCharacteristics[StrainOrLine]	BioMaterialCharacteristics[Sex]	BioSource	BioSourceMaterial	SampleMaterial	ExtractMaterial	Extract	LabeledExtractMaterial	Dye	Protocol[grow]	Protocol[treatment]	Protocol[extraction]	Protocol[pool]	Protocol[labeling]	Protocol[hybridization]	Protocol[scanning]	FactorValue[Time(h)]	File[raw]	Array[accession]	Array[serial]
+Saccharomyces cerevisiae	S288C	mating_type_a	Time1	whole_organism	whole_organism	total_RNA	Time1	synthetic_DNA	Cy3	P-EXML-1	P-EXML-2	P-EXML-3		P-EXML-4	P-EXML-5	P-EXML-6	1	Data1.txt	A-EXML-1	244531
+Saccharomyces cerevisiae	S288C	mating_type_a	Time1	whole_organism	whole_organism	total_RNA	Reference extract	synthetic_DNA	Cy5	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-7	P-EXML-4	P-EXML-5	P-EXML-6		Data1.txt	A-EXML-1	244531
+Saccharomyces cerevisiae	S288C	mating_type_a	Time2	whole_organism	whole_organism	total_RNA	Time2	synthetic_DNA	Cy3	P-EXML-1	P-EXML-2	P-EXML-3		P-EXML-4	P-EXML-5	P-EXML-6	2	Data2.txt	A-EXML-1	244532
+Saccharomyces cerevisiae	S288C	mating_type_a	Time2	whole_organism	whole_organism	total_RNA	Reference extract	synthetic_DNA	Cy5	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-7	P-EXML-4	P-EXML-5	P-EXML-6		Data2.txt	A-EXML-1	244532
+Saccharomyces cerevisiae	S288C	mating_type_a	Time3	whole_organism	whole_organism	total_RNA	Time3	synthetic_DNA	Cy3	P-EXML-1	P-EXML-2	P-EXML-3		P-EXML-4	P-EXML-5	P-EXML-6	3	Data3.txt	A-EXML-1	244533
+Saccharomyces cerevisiae	S288C	mating_type_a	Time3	whole_organism	whole_organism	total_RNA	Reference extract	synthetic_DNA	Cy5	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-7	P-EXML-4	P-EXML-5	P-EXML-6		Data3.txt	A-EXML-1	244533
diff --git a/examples/two_color/Data1.txt b/examples/two_color/Data1.txt
new file mode 100644
index 0000000..157be04
--- /dev/null
+++ b/examples/two_color/Data1.txt
@@ -0,0 +1,37 @@
+ATF	1.0
+24	43    
+"Type=GenePix Results 1.2"
+"DateTime=2003/01/23 11:27:59"
+"Settings=E:\settings files\array.gps"
+"GalFile="
+"Scanner=GenePix 4000A"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm	532 nm"
+"FileName=\\Server\Data1.tif"
+"PMTVolts=600	590"
+"NormalizationFactor:RatioOfMedians=1.90064"
+"NormalizationFactor:RatioOfMeans=1.90384"
+"NormalizationFactor:MedianOfRatios=1.86554"
+"NormalizationFactor:MeanOfRatios=1.23878"
+"NormalizationFactor:RegressionRatio=2.45261"
+"JpegImage=\\Server\Data1.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=1240, 6320"
+"JpegOrigin=1840, 7160"
+"Creator=GenePix Pro 3.0.0.98"
+"Temperature=1.41243"
+"LaserPower=2.19092	0.863674"
+"LaserOnTime=86000	85977"
+"Block"	"Column"	"Row"	"Name"	"ID"	"X"	"Y"	"Dia."	"F635 Median"	"F635 Mean"	"F635 SD"	"B635 Median"	"B635 Mean"	"B635 SD"	"% > B635+1SD"	"% > B635+2SD"	"F635 % Sat."	"F532 Median"	"F532 Mean"	"F532 SD"	"B532 Median"	"B532 Mean"	"B532 SD"	"% > B532+1SD"	"% > B532+2SD"	"F532 % Sat."	"Ratio of Medians"	"Ratio of Means"	"Median of Ratios"	"Mean of Ratios"	"Ratios SD"	"Rgn Ratio"	"Rgn R�"	"F Pixels"	"B Pixels"	"Sum of Medians"	"Sum of Means"	"Log Ratio"	"F635 Median - B635"	"F532 Median - B53 [...]
+1	1	1			2100	7300	90	240	240	54	40	44	21	100	100	0	502	497	98	81	86	32	100	100	0	0.475	0.481	0.494	0.491	0.129	0.413	0.731	52	376	621	616	-1.074	200	421	200	416	0
+1	2	1			2410	7300	120	694	671	209	40	41	11	98	98	0	1406	1354	413	81	81	20	100	100	0	0.494	0.496	0.500	0.599	0.409	0.433	0.777	120	778	1979	1904	-1.019	654	1325	631	1273	0
+1	3	1			2690	7320	120	501	531	267	41	41	10	99	99	0	998	1080	498	79	82	21	99	98	0	0.501	0.490	0.493	0.641	1.280	0.460	0.837	120	810	1379	1491	-0.998	460	919	490	1001	0
+1	4	1			2980	7310	120	783	770	284	40	41	10	99	96	0	1034	1014	331	81	83	22	100	99	0	0.780	0.782	0.819	1.372	3.252	0.675	0.722	120	796	1696	1663	-0.359	743	953	730	933	0
+1	5	1			3260	7320	130	457	434	219	40	41	10	100	99	0	998	938	493	81	83	21	100	98	0	0.455	0.460	0.450	0.600	0.632	0.382	0.751	120	930	1334	1251	-1.137	417	917	394	857	0
+1	6	1			3570	7310	130	166	172	67	40	41	10	94	94	0	382	388	112	82	83	20	99	98	0	0.420	0.431	0.473	0.696	2.121	0.362	0.589	120	914	426	438	-1.252	126	300	132	306	0
+1	7	1			3860	7320	100	63	65	17	40	41	10	76	55	0	182	188	53	79	80	20	100	96	0	0.223	0.229	0.226	0.313	0.289	0.160	0.283	80	552	126	134	-2.163	23	103	25	109	0
+1	8	1			4130	7330	120	415	392	122	40	41	10	96	95	0	793	779	215	79	81	20	98	97	0	0.525	0.503	0.522	0.741	1.710	0.445	0.754	120	816	1089	1052	-0.929	375	714	352	700	0
+1	9	1			4430	7320	120	153	155	61	39	41	10	97	94	0	374	382	125	80	82	20	99	99	0	0.388	0.384	0.368	0.443	0.369	0.351	0.721	120	796	408	418	-1.367	114	294	116	302	0
+1	10	1			4730	7330	120	865	826	343	40	41	10	100	98	0	1011	941	389	79	81	21	99	98	0	0.885	0.912	0.923	1.477	3.031	0.751	0.698	120	810	1757	1648	-0.176	825	932	786	862	0
diff --git a/examples/two_color/Data2.txt b/examples/two_color/Data2.txt
new file mode 100644
index 0000000..f26132d
--- /dev/null
+++ b/examples/two_color/Data2.txt
@@ -0,0 +1,40 @@
+ATF	1.0
+27	43    
+"Type=GenePix Results 1.4"
+"DateTime=2003/05/19 15:39:10"
+"Settings=E:\settings files\arrays.gps"
+"GalFile="
+"Scanner=GenePix 4000A [54807]"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm	532 nm"
+"FileName=\\Server\Data2.tif"
+"PMTVolts=680	610"
+"ScanPower=100	100"
+"FocusPosition=0"
+"NormalizationFactor:RatioOfMedians=0.93502"
+"NormalizationFactor:RatioOfMeans=0.917873"
+"NormalizationFactor:MedianOfRatios=0.933921"
+"NormalizationFactor:MeanOfRatios=0.954675"
+"NormalizationFactor:RegressionRatio=0.787143"
+"JpegImage=\\Server\Data2.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=560, 5360"
+"JpegOrigin=1650, 6810"
+"Creator=GenePix Pro 3.0.6.90"
+"Temperature=41.3353"
+"LaserPower=1.50975	1.13071"
+"LaserOnTime=41930	44902"
+"Supplier="
+"Block"	"Column"	"Row"	"Name"	"ID"	"X"	"Y"	"Dia."	"F635 Median"	"F635 Mean"	"F635 SD"	"B635 Median"	"B635 Mean"	"B635 SD"	"% > B635+1SD"	"% > B635+2SD"	"F635 % Sat."	"F532 Median"	"F532 Mean"	"F532 SD"	"B532 Median"	"B532 Mean"	"B532 SD"	"% > B532+1SD"	"% > B532+2SD"	"F532 % Sat."	"Ratio of Medians"	"Ratio of Means"	"Median of Ratios"	"Mean of Ratios"	"Ratios SD"	"Rgn Ratio"	"Rgn R�"	"F Pixels"	"B Pixels"	"Sum of Medians"	"Sum of Means"	"Log Ratio"	"F635 Median - B635"	"F532 Median - B53 [...]
+1	1	1	""	""	2040	7130	110	112	120	55	49	53	24	80	63	0	191	200	78	50	51	13	100	100	0	0.447	0.473	0.502	0.428	2.467	0.472	0.332	80	656	204	221	-1.162	63	141	71	150	0
+1	2	1	""	""	2330	7120	120	412	435	197	51	55	26	95	95	0	731	843	375	50	51	11	99	99	0	0.530	0.484	0.477	0.503	3.009	0.418	0.611	120	820	1042	1177	-0.916	361	681	384	793	0
+1	3	1	""	""	2610	7130	120	297	367	232	52	56	25	95	90	0	417	587	397	51	52	12	97	95	0	0.669	0.588	0.592	0.604	3.170	0.511	0.592	120	820	611	851	-0.579	245	366	315	536	0
+1	4	1	""	""	2900	7120	120	214	297	230	50	54	25	92	88	0	272	412	327	51	52	12	94	93	0	0.742	0.684	0.755	0.675	3.312	0.576	0.412	120	820	385	608	-0.430	164	221	247	361	0
+1	5	1	""	""	3200	7130	120	355	386	203	52	56	26	92	91	0	683	701	245	51	52	12	100	100	0	0.479	0.514	0.506	0.502	2.251	0.507	0.593	120	820	935	984	-1.061	303	632	334	650	0
+1	6	1	""	""	3500	7110	120	134	135	56	52	55	26	82	71	0	213	209	61	51	52	14	95	93	0	0.506	0.525	0.546	0.488	3.047	0.532	0.398	120	810	244	241	-0.982	82	162	83	158	0
+1	7	1	""	""	3770	7120	80	79	80	33	50	54	27	51	26	0	100	100	26	51	52	11	98	82	0	0.592	0.612	1.025	0.705	2.930	1.819	0.071	52	340	78	79	-0.757	29	49	30	49	0
+1	8	1	""	""	4070	7120	120	332	339	152	55	59	27	92	90	0	609	613	227	52	52	12	95	95	0	0.497	0.506	0.543	0.522	3.195	0.482	0.645	120	820	834	845	-1.008	277	557	284	561	0
+1	9	1	""	""	4370	7090	110	167	179	82	53	57	26	91	86	0	240	254	102	52	54	15	97	96	0	0.606	0.624	0.669	0.614	2.841	0.577	0.414	80	616	302	328	-0.722	114	188	126	202	0
+1	10	1	""	""	4650	7110	130	241	291	200	54	57	25	90	85	0	371	522	320	51	52	12	99	99	0	0.584	0.503	0.457	0.508	2.361	0.481	0.581	120	930	507	708	-0.775	187	320	237	471	0
diff --git a/examples/two_color/two_color.png b/examples/two_color/two_color.png
new file mode 100644
index 0000000..df1137c
Binary files /dev/null and b/examples/two_color/two_color.png differ
diff --git a/examples/two_color/two_color.txt b/examples/two_color/two_color.txt
new file mode 100644
index 0000000..23a547c
--- /dev/null
+++ b/examples/two_color/two_color.txt
@@ -0,0 +1,45 @@
+# Simple example summary spreadsheet for tab2mage.pl script.																						
+# This file illustrates how channels should be combined when describing a two-color hybridization (e.g.  Cy3/Cy5).																						
+# Lines starting with a '#' are treated as comments and are ignored.																						
+																						
+Experiment section																						
+domain	ebi.ac.uk																					
+accession	E-EXML-1																					
+quality_control	dye_swap_quality_control																					
+experiment_design_type	strain_or_line_design;co-expression_design																					
+name	Transcriptome analysis of invasive verses non-invasive strains of budding yeast 																					
+description	An experiment was performed to...																					
+release_date	2004-08-30																					
+submission_date	2004-07-28																					
+submitter	John Falstaff																					
+submitter_email	jfalstaff at wagglespike.com																					
+investigator	Bill Shakespeare																					
+investigator_email	bills at wagglespike.com																					
+organization	Windsor Laboratories																					
+address	Windsor, Ontario, Canada																					
+# The publication details below are fictitious																						
+publication_title	Comparison of the transcriptomes of invasive verses non-invasive strains of budding yeast 																					
+authors	John Falstaff; Robin Goodfellow; Bill Shakespeare																					
+journal	Nature Genetics																					
+volume	12																					
+issue	4																					
+pages	123-456																					
+year	2004																					
+pubmed_id	12345678																					
+																						
+# Protocol text may be formatted using standard HTML tags																						
+Protocol section																						
+accession	text	name																			
+P-EXML-1	Cells were grown in YPD (1% yeast extract/2% peptone/2% glucose) to an OD600 of approximately 0.8	Yeast growth																			
+P-EXML-2	Your protocol text here	Yeast cell harvesting																			
+P-EXML-3	Your protocol text here	Cell lysis and RNA prep																			
+P-EXML-4	Your protocol text here	cDNA labeling																			
+P-EXML-5	Your protocol text here	Hybridization																			
+P-EXML-6	Your protocol text here	Scanning																			
+																						
+Hybridization section																						
+File[raw]	Array[accession]	Array[serial]	Protocol[grow]	Protocol[treatment]	Protocol[extraction]	Protocol[labeling]	Protocol[hybridization]	Protocol[scanning]	BioSource	Sample	Extract	LabeledExtract	Hybridization	BioSourceMaterial	SampleMaterial	ExtractMaterial	LabeledExtractMaterial	Dye	FactorValue[StrainOrLine]	BioMaterialCharacteristics[Organism]	BioMaterialCharacteristics[StrainOrLine]	BioMaterialCharacteristics[Sex]
+Data1.txt	A-EXML-1	244532	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	S288C	S288C	S288C	S288C Cy5	Dye Swap 1	whole_organism	whole_organism	total_RNA	synthetic_DNA	Cy5	S288C	Saccharomyces cerevisiae	S288C	mating_type_a
+Data1.txt	A-EXML-1	244532	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	Sigma1278b	Sigma1278b	Sigma1278b	Sigma1278b Cy3	Dye Swap 1	whole_organism	whole_organism	total_RNA	synthetic_DNA	Cy3	Sigma1278b	Saccharomyces cerevisiae	Sigma1278b	mating_type_a
+Data2.txt	A-EXML-1	244533	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	S288C	S288C	S288C	S288C Cy3	Dye Swap 2	whole_organism	whole_organism	total_RNA	synthetic_DNA	Cy3	S288C	Saccharomyces cerevisiae	S288C	mating_type_a
+Data2.txt	A-EXML-1	244533	P-EXML-1	P-EXML-2	P-EXML-3	P-EXML-4	P-EXML-5	P-EXML-6	Sigma1278b	Sigma1278b	Sigma1278b	Sigma1278b Cy5	Dye Swap 2	whole_organism	whole_organism	total_RNA	synthetic_DNA	Cy5	Sigma1278b	Saccharomyces cerevisiae	Sigma1278b	mating_type_a
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..714345a
--- /dev/null
+++ b/index.html
@@ -0,0 +1,356 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+  <head>
+    <title>Tab2MAGE</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+    <link rel="stylesheet" href="docs/style.css">
+  </head>
+
+  <body>
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="270"><img src="docs/T2M_logo.png" border="0" height="50" alt="Tab2MAGE logo"> <img src="docs/MAGETAB_logo.png" border="0" height="50" alt="MAGE-TAB logo"></td>
+	    <td class="pagetitle">Home page</td>
+	  </tr>
+      </table>
+    </div>
+
+    <div CLASS="section">
+      <table class="layout">
+	<tr>
+	  <td class="layout">
+	    
+	    <ul>
+	      <li><a href="#intro">Introduction</a></li>
+	      <li><a href="#tab2mage">Tab2MAGE MAGE-ML generation</a></li>
+	      <li><a href="#magetab">MAGE-TAB MAGE-ML generation</a></li>
+	      <li><a href="#expt_check">Experiment checking script</a></li>
+	      <li><a href="#visualize">Experiment visualization</a></li>
+	      <li><a href="#other_packages">Additional packages</a></li>
+	      <li><a href="#contact">Contact details</a></li>
+	    </ul>
+		  
+	    <ul>
+	      <li><a href="docs/spreadsheet.html"><b>How to create a Tab2MAGE spreadsheet</b></a> (<a href="docs/spreadsheet.html#realexamples">examples</a>)</li>
+	      <li><a href="docs/aesubs.html"><b>Tab2MAGE submission to ArrayExpress</b></a></li>
+	      <li><a href="docs/magetab_subs.html"><b>MAGE-TAB submission to ArrayExpress</b></a></li>
+	      <li><a href="docs/index.html">Full Documentation (spreadsheets, usage, APIs)</a></li>
+	      <li><a href="http://sourceforge.net/projects/tab2mage/">Tab2MAGE SourceForge summary</a></li>
+	      <li><a href="http://sourceforge.net/project/showfiles.php?group_id=120325">Tab2MAGE downloads</a></li>
+	    </ul>
+
+	  </td>
+	  <td class="layout" valign="top">
+	    <div CLASS="imghead">
+	      <a href="http://www.ebi.ac.uk/arrayexpress/">
+		<img src="docs/aelogo.png" border="0" alt="ArrayExpress logo">
+	      </a>
+	    </div>
+	  </td>
+	</tr>
+      </table>
+    </div>
+
+    <div CLASS="section">
+
+      <div CLASS="sectionhead"><a name="intro"></a>Introduction</div>
+      
+      <p CLASS="text">Tab2MAGE is a software package written and
+      supported by the <a
+      href="http://www.ebi.ac.uk/arrayexpress/">ArrayExpress</a>
+      curation team, which aims to ease the process of submitting
+      large microarray experiment datasets to our public repository
+      database. To this end, Tab2MAGE currently includes two tools,
+      the <a href="#tab2mage">tab2mage.pl</a> script itself, and a
+      data file checking script, <a
+      href="#expt_check">expt_check.pl</a>. With these scripts it is
+      possible to perform an initial data file validation against an
+      array design (e.g., in the form of an "Array Description File"
+      or ADF), and then to generate MAGE-ML using these data files
+      alongside a separate spreadsheet providing MIAME-compliant
+      sample annotation.</p>
+
+      <p class="text">As of version 1.9.9, the package also includes
+      tools to <a href="#expt_check">validate MAGE-TAB documents</a>
+      and <a href="#magetab">convert them into MAGE-ML</a>.</p>
+
+      <p CLASS="sectionfoot">[ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div CLASS="section">
+
+      <div CLASS="sectionhead"><a name="tab2mage"></a>Tab2MAGE MAGE-ML generation</div>
+      
+      <p CLASS="text">The tab2mage.pl script is currently under active
+      development. The latest version released on SourceForge is aimed
+      at the moderately experienced user. For the time being it is
+      still recommended that small to medium-sized experiment data
+      submissions be provided via the <a
+      href="http://www.ebi.ac.uk/miamexpress/">MIAMExpress</a> web
+      interface. The primary aim of the Tab2MAGE project is to provide
+      an easy-to-use method for submitting large datasets (e.g.,
+      greater than around 50 hybridizations) to
+      ArrayExpress.</p>
+
+      <p CLASS="text">This script is currently capable of parsing data
+      files in a number of formats. These formats include
+      tab-delimited text files such as those produced by GenePix,
+      BlueFuse, ScanAlyze, ScanArray or Agilent software, and also
+      the data files produced by Affymetrix scanning software. The
+      generic MetaColumn/MetaRow format favoured by ArrayExpress is of
+      course also supported. As of version 1.9.4, Tab2MAGE now also
+      supports most data files produced by the <a
+      href="http://www.illumina.com/">Illumina BeadStudio</a>
+      software package.</p>
+
+      <p CLASS="text">To use the script a sample annotation
+      spreadsheet must be provided. This experiment summary file has a
+      flexible format based around a set of predefined column
+      headings. Here is an <a
+      href="examples/two_color/two_color.txt">example</a> of the
+      format of this file, which corresponds to the experiment design
+      <a href="examples/two_color/two_color.png">depicted
+      here</a>. The spreadsheet consists of three sections,
+      separated by one or more blank lines: <em>Experiment</em>,
+      <em>Protocol</em> and <em>Hybridization</em>.</p>
+      
+      <p CLASS="text"><a class="subhead" href="docs/spreadsheet.html#experiment">Experiment
+      section</a><br> This section contains top-level information
+      about the experiment, such as the title, description and
+      accession number, organized by row.</p>
+
+      <p CLASS="text"><a class="subhead" href="docs/spreadsheet.html#protocol">Protocol
+      section</a><br>Protocols are defined as needed in this
+      section, with one protocol per row. If all of the protocols used
+      in the experiment have previously been loaded into ArrayExpress
+      and given accession numbers, this whole section can be
+      omitted.</p>
+
+      <p CLASS="text"><a class="subhead" href="docs/spreadsheet.html#hybridization">Hybridization
+      section</a><br>This section contains the bulk of the
+      experiment information. At its simplest, each row describes the
+      route taken from BioSource to output data file. Multichannel
+      (e.g., two-colour) data can be described by entering each
+      channel as a separate line. Pooling can be described at multiple
+      levels by using as many lines as necessary to describe all the
+      relationships between upstream and downstream samples.  In a
+      sense this table can be compared to an SQL database table in the
+      way that it provides links between MAGE objects.</p>
+
+      <p CLASS="text">Further <a
+      href="docs/spreadsheet.html#examples">examples</a> of the
+      Tab2MAGE spreadsheet format are included in the <a
+      href="docs/index.html">online package documentation</a>,
+      alongside detailed information on <a
+      href="docs/ArrayExpress/Curator/MAGE/Definitions.html#supported_row_and_column_headings">allowed column
+      and row names</a>. A set of <a
+      href="docs/usage.html">tab2mage.pl usage notes</a> is also
+      included, giving examples of how to invoke the script.</p>
+
+      <p CLASS="sectionfoot">[ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div CLASS="section">
+
+      <div CLASS="sectionhead"><a name="magetab"></a>MAGE-TAB MAGE-ML generation</div>
+
+      <p class="text">New in version 1.9.7, the Tab2MAGE package now
+      includes a script, <a href="docs/magetab.html">magetab.pl</a>,
+      for generating MAGE-ML from a MAGE-TAB document. Most, but not
+      all, of the requirements from the specification have now been
+      implemented. Please see the MAGETAB.txt file included in the
+      package for implementation notes and a discussion of current
+      limitations. The script is typically be invoked as follows:</p>
+
+      <pre>magetab.pl -i <IDF file> -n <experiment accession> -t <output directory></pre>
+
+      <p class="text">The MAGE-TAB parser is currently based on the
+      version 1.0 specification for MAGE-TAB, available for download
+      <a
+      href="http://www.ebi.ac.uk/systems-srv/mp/file-exchange/MAGE-TABv1.0.tar.gz">from
+      the EBI website</a>. See these <a
+      href="docs/magetab_subs.html">MAGE-TAB help notes</a> for
+      information on submitting MAGE-TAB documents to
+      ArrayExpress.</p>
+
+      <p class="text">See also the <a
+      href="#other_packages">Additional packages</a> section for
+      information on converting from Tab2MAGE to MAGE-TAB format.</p>
+      
+      <p CLASS="sectionfoot">[ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div CLASS="section">
+
+      <div CLASS="sectionhead"><a name="expt_check"></a>Experiment checker script</div>
+      
+      <p CLASS="text">The expt_check.pl script can be used to validate microarray 
+	experiment data files and associated MIAME metadata for common errors. 
+	The script can be used in any of four different modes, depending on 
+	your needs: Tab2MAGE, MAGE-TAB, MIAMExpress or full stand-alone mode. <a
+	  href="docs/expt_check.html">Full documentation</a> is
+	available; in summary, the four forms are shown here:</p>
+
+      <p class="subhead">Tab2MAGE mode</p>
+      <pre>expt_check.pl -e <Tab2MAGE spreadsheet file></pre>
+
+      <p class="subhead">MAGE-TAB mode</p>
+      <pre>expt_check.pl -i <MAGE-TAB IDF file></pre>
+
+      <p class="text">Unless the "-s" ("stand-alone") option is used, 
+	the script will attempt to connect to the ArrayExpress database to
+	retrieve information on the array design(s) used in the experiment.
+	The array information is used to check the data files for consistency. 
+	This behaviour may also be supressed by supplying the name of
+	an ADF file to the script, using the -a option (see below).</p>
+
+      <p class="subhead">MIAMExpress mode (local installations only)</p>
+      <pre>expt_check.pl -l <login username> -t <experiment title></pre>
+
+      <p class="text">Note that if you wish to use the expt_check.pl
+      script with a local installation of MIAMExpress, you will need
+      to edit the database connection parameters in the included <a
+      href="docs/ArrayExpress/Curator/Config.html">ArrayExpress::Curator::Config
+      module</a>.</p>
+
+      <p class="subhead">Full stand-alone mode</p>
+      <pre>expt_check.pl -s -a <ADF file> <list of data files></pre>
+
+      <p CLASS="text">If an ADF is
+      supplied to the script in stand-alone mode, the data files will be checked for
+      features missing from that ADF (please see the <a
+      href="http://www.ebi.ac.uk/miamexpress/help/adf/">MIAMExpress ADF
+      help notes</a> for information on the ADF format). A full list
+      of the tests performed by the experiment checker script is
+      available in the documentation.</p>
+
+      <p CLASS="sectionfoot">[ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div CLASS="section">
+
+      <div CLASS="sectionhead"><a name="other_packages"></a>Additional packages</div>
+
+      <p CLASS="text">There are several additional packages related to
+      Tab2MAGE which are available for download from the <a
+      href="http://sourceforge.net/project/showfiles.php?group_id=120325">SourceForge
+      project pages</a>:</p>
+
+      <p class="text"><a name="visualize"
+      href="http://sourceforge.net/project/showfiles.php?group_id=120325&package_id=163063"
+      class="subhead">MAGE-ML Visualize</a><br/>A popular download,
+      this MAGE-ML visualization script developed by Anna Farne at the
+      EBI will read any MAGE-ML file and generate a graph showing the
+      links between BioMaterials all the way through to BioAssays, and
+      include information on sample characteristics and experimental
+      factor values in the output. Here is <a
+      href="examples/final_data_matrix/E-EXML-7.xml.dot.jpg">an
+      example of a MAGE-ML Visualize graph</a>.
+      </p>
+
+      <p class="text">Alternatively, the <a
+      href="#expt_check">expt_check.pl</a> script may be used to
+      visualize either Tab2MAGE or MAGE-TAB encoded experiments. A
+      command-line option, "-x" has been provided for omission of data
+      file checking, allowing for quick visualization of the graph
+      contained in a spreadsheet. The output from this script is a
+      more minimalistic version of that given by MAGE-ML Visualize (<a
+      href="examples/final_data_matrix/final_data_matrix.png">expt_check.pl
+      graph example</a>). </p>
+
+      <p class="text">Both these scripts use the Graphviz software
+      package to generate the graph output. Please see the <a
+      href="http://www.graphviz.org/">Graphviz website</a> for
+      installation instructions.</p>
+
+      <p class="text"><a name="adfchecker"
+      href="http://sourceforge.net/project/showfiles.php?group_id=120325&package_id=201790"
+      class="subhead">ADF Checker</a><br/>The ADF checker script, also
+      developed by Anna Farne, can be used to validate the format and
+      content of an <a
+      href="http://www.ebi.ac.uk/miamexpress/help/adf/index.html">Array Description Format
+      file</a> (used to describe an array design in tabular
+      format). An online version of this tool is also available on the
+      <a
+      href="http://www.ebi.ac.uk/cgi-bin/microarray/ADF_checker_page.pl">EBI
+      website</a>.</p>
+
+      <p class="text"><a name="tabconverter"
+      href="http://sourceforge.net/project/showfiles.php?group_id=120325&package_id=226041"
+      class="subhead">Tab2MAGE to MAGE-TAB converter</a><br/>Faisal
+      Ibne Rezwan at the EBI has written this script to convert
+      Tab2MAGE spreadsheets to MAGE-TAB IDF and SDRF component
+      documents.</p>
+
+      <p class="text"><a name="geoimport"
+      href="http://sourceforge.net/project/showfiles.php?group_id=120325&package_id=243607"
+      class="subhead">GEOImport</a><br/>Faisal Ibne Rezwan has also
+      written this package in collaboration with Margus Lukk (also at
+      the EBI) to import experiments from the <a
+      href="http://www.ncbi.nlm.nih.gov/geo/">NCBI GEO database</a>
+      into Tab2MAGE format. These scripts are being used to facilitate
+      data transfer between GEO and ArrayExpress. At the time of
+      writing, over 250 experiments have been processed using this
+      package. Currently only Affymetrix-based experiments are
+      supported, but we hope to add support for other platforms
+      shortly.</p>
+
+      <p class="text"><a name="oe2magetab"
+      href="http://sourceforge.net/project/showfiles.php?group_id=120325&package_id=281911"
+      class="subhead">OE2magetab</a><br/>A MAGE-TAB post-hoc ontology matching
+      utility. Xiangqun Zheng Bradley at the EBI has created this
+      package to match sample annotation terms from an input MAGE-TAB
+      document to an ontology supplied in either OBO or flat-file
+      format. This script generates an output MAGE-TAB document
+      containing the appropriate "Term Source REF" and "Term Source
+      Accession" values.</p>
+
+      <p class="text"><a name="mage2tab"
+      href="http://sourceforge.net/project/showfiles.php?group_id=120325&package_id=219611"
+      class="subhead">mage2tab</a><br/>The mage2tab project is
+      maintained by Junmin Liu at <a
+      href="http://www.cbil.upenn.edu/">CBIL</a>. Its aim is to
+      provide a simple means to convert MAGE-ML documents to MAGE-TAB
+      format. Further information on mage2tab is available on the <a
+      href="https://www.cbil.upenn.edu/magewiki/index.php/mage2tab">CBIL
+      wiki page</a>.</p>
+
+      <p CLASS="sectionfoot">[ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <div CLASS="section">
+
+      <div CLASS="sectionhead"><a name="contact"></a>Contact details</div>
+      
+      <p CLASS="text">Please contact the 
+	<script type="text/javascript">
+	  <!--
+	  var aname = "miamexpress@ebi.ac.uk";
+	  document.write("<a href='" + "mail" + "to:" + aname + "'>ArrayExpress curators<" + "/a>")
+	  //-->
+	</script> for help with data submission, or for more information on local installation and use of Tab2MAGE.
+      </p>
+
+      <p CLASS="sectionfoot">[ <a href="#top">Top of page</a> ]</p>
+
+    </div>
+
+    <hr>
+
+    <a href="http://sourceforge.net">
+      <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" />
+    </a>
+    <br>
+<!-- Created: Wed Sep 29 09:26:35 BST 2004 -->
+<!-- hhmts start -->
+Last modified: Mon Apr 21 12:21:19 BST 2008
+<!-- hhmts end -->
+  </body>
+</html>
diff --git a/lib/ArrayExpress/ArrayMAGE.pm b/lib/ArrayExpress/ArrayMAGE.pm
new file mode 100644
index 0000000..28ed571
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE.pm
@@ -0,0 +1,658 @@
+#!/usr/bin/env perl
+#
+# $Id: ArrayMAGE.pm 1938 2008-02-10 18:34:20Z tfrayner $
+
+use strict;
+use warnings;
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../index.html">
+                <img src="../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: ArrayMAGE.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::ArrayMAGE.pm - an OO module providing methods
+for writing MAGE-ML for array designs.
+
+=head1 SYNOPSIS
+
+ # Import all the ArrayMAGE classes.
+ use ArrayExpress::ArrayMAGE qw(:ALL);
+ 
+ # Instantiate the objects we require.
+ my @classes = qw(
+     Feature
+     Reporter
+     BioSequence
+     FeatureReporterMap
+     ArrayDesign
+     Zone
+ );
+ my %object;
+ foreach my $type (@classes) {
+     if ( $type eq 'ArrayDesign' ) {
+ 
+         # ArrayDesign requires an accession.
+         $object{$type} = "ArrayExpress::ArrayMAGE::$type"->new({
+	     accession => $acc,
+	 });
+     }
+     else {
+	 $object{$type} = "ArrayExpress::ArrayMAGE::$type"->new();
+     }
+ }
+ 
+ # ReporterGroup and CompositeGroup are typically handled
+ # slightly differently, because multiple groups may be
+ # instantiated during a run (see below).
+ my ( %reporter_group, %composite_group );
+ 
+ # Add objects, e.g. while scanning through an ADF or CDF file.
+ foreach my $row_hashref ( @list_of_lines ) {
+    $object{Feature}->add({
+        metacolumn => $row->{'MetaColumn'},
+        metarow    => $row->{'MetaRow'},
+        column     => $row->{'Column'},
+        row        => $row->{'Row'},
+    });
+ 
+    # ReporterGroups and CompositeGroups are handled in similar
+    # ways (only ReporterGroup is shown here):
+    while ( my ( $rname, $rvalue ) = each %$row ) {
+       if ( my ($type) = ( $rname =~ m!ReporterGroup\[(.*?)\]!i ) ) {
+           my $key = "$type.$rvalue";
+           $reporter_group{$key}
+               ||= ArrayExpress::ArrayMAGE::ReporterGroup->new({
+               identifier => $key,
+               tag        => $rvalue,
+               is_species => ( $type =~ m!species!i ) || 0,
+           });
+           $reporter_group{$key}->add({
+               identifier => $row->{'Reporter Identifier'},
+           });
+       }
+    }
+ }
+ 
+ # Write out the MAGE-ML to STDOUT.
+ ArrayExpress::ArrayMAGE->combine_parts(
+     \%objects,
+     \%reporter_group,
+     \%composite_group,
+     \*STDOUT,
+ );
+
+=head1 DESCRIPTION
+
+This is a module designed to allow the creation of MAGE-ML for array
+designs in a memory-efficient fashion. This module is the abstract
+superclass for a series of MAGE-like classes representing parts of the
+MAGE-ML document to be written. These classes, when instantiated, all
+point to temporary filehandles into which is written the XML
+describing MAGE object instances. Each subclass implements an 'add'
+method in addition to those inherited from this base
+class. Instantiating the object with new() opens up the XML tags for a
+list of objects, the 'add' adds objects based on the values it is
+passed (no surprise there) and finally the XML tags are closed and the
+MAGE-ML written out by calling the class method combine_parts() in this
+top-level class.
+
+=head1 PUBLIC METHODS
+
+=over 2
+
+=item C<combine_parts( \%obj, \%rg, \%cg, $fh )>
+
+Class method. Takes: a hashref of ArrayMAGE objects, a hashref of
+ReporterGroups, a hashref of CompositeGroups, and an open output
+filehandle, and writes the completed MAGE-ML to that filehandle.
+
+=item C<set_namespace("namespace")>
+
+Class method. Allows the user to set the identifier namespace for the
+output MAGE objects.
+
+=item C<set_design("design")>
+
+Class method. Allows the user to set the design name used in
+identifiers for the output MAGE objects. Typically this will be the
+same as the array accession, but e.g. for Affymetrix arrays will
+correspond to the design name.
+
+=item C<set_separator("separator")>
+
+Class method. Allows the user to set the identifier separator for the
+output MAGE objects. Usually this will be q{:} or q{.}.
+
+=item C<set_programname("programname")>
+
+Class method. Allows the user to set the name of the program (this is
+not particularly useful).
+
+=back
+
+=head1 SUBCLASSES
+
+=head2 ArrayExpress::ArrayMAGE::ArrayDesign
+
+=over 2
+
+=item new() required attribute: C<accession>
+
+The accession attribute becomes the MAGE PhysicalArrayDesign
+identifier.
+
+=item add() is not implemented for this class.
+
+=back
+
+=head2 ArrayExpress::ArrayMAGE::BioSequence
+
+=over 2
+
+=item new() requires no attributes.
+
+=item add() required attribute: C<identifier>
+
+This identifier is combined with the class namespace to create the
+BioSequence identifier.
+
+=item add() optional attribute: C<dbrefs>
+
+A hashref with db accessions as keys, and db tags as values
+(e.g. C<{ A12345 =E<gt> 'embl' }> ). Typically used for Reporter
+BioSequence Database Entry ADF columns.
+
+=back
+
+=head2 ArrayExpress::ArrayMAGE::CompositeGroup
+
+=over 2
+
+=item new() required attribute: C<identifier>
+
+This identifier is combined with the class namespace to create a
+CompositeGroup identifier.
+
+=item new() required attribute: C<tag>
+
+This tag is used as the CompositeGroup name. If C<is_species> is set,
+then it will also be used in a Species_assn element as the species
+name.
+
+=item new() optional attribute: C<is_species>
+
+A flag indicating whether this is a grouping based on species or not.
+
+=item add() required attribute: C<id_ref>
+
+This identifier is combined with the class namespace to create a
+CompositeSequence_ref identifier.
+
+=back
+
+=head2 ArrayExpress::ArrayMAGE::CompositeSequence
+
+=over 2
+
+=item new() requires no attributes.
+
+=item add() required attribute: C<identifier>
+
+This identifier is combined with the class namespace to create the
+CompositeSequence identifier.
+
+=item add() optional attribute: C<name>
+
+The name of the CompositeSequence.
+
+=item add() optional attribute: C<biosequences>
+
+An arrayref of BioSequence identifiers to be attached to this
+CompositeSequence.
+
+=item add() optional attribute: C<dbrefs>
+
+A hashref with db accessions as keys, and db tags as values
+(e.g. C<{ A12345 =E<gt> 'embl' }> ). Typically used for CompositeSequence
+Description Database Entry ADF columns.
+
+=item add() optional attribute: C<controltype>
+
+A ControlType ontology term to be attached to this CompositeSequence.
+
+=item add() optional attribute: C<comment>
+
+A comment to be added to this CompositeSequence as a Description_assn.
+
+=item add() optional attribute: C<cs_only>
+
+A flag indicating that we are not creating Reporters (and can
+therefore skip ReporterCompositeMap creation).
+
+=back
+
+=head2 ArrayExpress::ArrayMAGE::Database
+
+=over 2
+
+=item new() requires no attributes.
+
+=item add() required attribute: C<tag>
+
+This tag is combined with the class namespace to create the Database
+identifier. Typically this will be a standard database tag,
+e.g. C<embl>.
+
+=back
+
+=head2 ArrayExpress::ArrayMAGE::Feature
+
+=over 2
+
+=item new() requires no attributes.
+
+=item add() required attribute: C<metacolumn>
+
+=item add() required attribute: C<metarow>
+
+=item add() required attribute: C<column>
+
+=item add() required attribute: C<row>
+
+Feature coordinates, used in the Feature and Zone_ref identifiers and
+in FeatureLocation.
+
+=back
+
+=head2 ArrayExpress::ArrayMAGE::FeatureReporterMap
+
+=over 2
+
+=item new() requires no attributes.
+
+=item add() required attribute: C<identifier>
+
+The Reporter identifier to which Features should be mapped.
+
+=item add() required attribute: C<features>
+
+An arrayref of Feature identifiers mapped to the Reporter.
+
+=item add() optional attribute: C<mismatch_info>
+
+A hashref of hashrefs, keyed by Feature identifier and then by
+C<start_coord>, C<new_sequence> and C<replaced_length>, used to create
+a MismatchInformation_assnlist (useful for Affymetrix array designs).
+
+=back
+
+=head2 ArrayExpress::ArrayMAGE::Reporter
+
+=over 2
+
+=item new() requires no attributes.
+
+=item add() required attribute: C<identifier>
+
+This identifier is combined with the class namespace to create the
+Reporter identifier.
+
+=item add() optional attribute: C<name>
+
+The name of the Reporter.
+
+=item add() optional attribute: C<biosequences>
+
+An arrayref of BioSequence identifiers to be attached to this
+Reporter.
+
+=item add() optional attribute: C<dbrefs>
+
+A hashref with db accessions as keys, and db tags as values
+(e.g. C<{ A12345 =E<gt> 'embl' }> ). Typically used for Reporter
+Description Database Entry ADF columns.
+
+=item add() optional attribute: C<controltype>
+
+A ControlType ontology term to be attached to this Reporter.
+
+=item add() optional attribute: C<warningtype>
+
+A WarningType ontology term to be attached to this Reporter.
+
+=item add() optional attribute: C<failtype>
+
+A FailType ontology term to be attached to this Reporter.
+
+=item add() optional attribute: C<comment>
+
+A comment to be added to this Reporter as a Description_assn.
+
+=back
+
+=head2 ArrayExpress::ArrayMAGE::ReporterCompositeMap
+
+=over 2
+
+=item new() requires no attributes.
+
+=item add() required attribute: C<identifier>
+
+The CompositeSequence identifier to which Reporters should be mapped.
+
+=item add() required attribute: C<reporters>
+
+An arrayref of Reporter identifiers mapped to the CompositeSequence.
+
+=back
+
+=head2 ArrayExpress::ArrayMAGE::ReporterGroup
+
+=over 2
+
+=item new() required attribute: C<identifier>
+
+This identifier is combined with the class namespace to create a
+ReporterGroup identifier.
+
+=item new() required attribute: C<tag>
+
+This tag is used as the ReporterGroup name. If C<is_species> is set,
+then it will also be used in a Species_assn element as the species
+name.
+
+=item new() optional attribute: C<is_species>
+
+A flag indicating whether this is a grouping based on species or not.
+
+=item add() required attribute: C<id_ref>
+
+This identifier is combined with the class namespace to create a
+Reporter_ref identifier.
+
+=back
+
+=head2 ArrayExpress::ArrayMAGE::Zone
+
+=over 2
+
+=item new() requires no attributes.
+
+=item add() required attribute: C<column>
+
+=item add() required attribute: C<row>
+
+Zone coordinates, used in the Zone identifier and in its row and
+column attributes.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2008.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments. 
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::ArrayMAGE;
+
+use IO::File;
+use Carp;
+use Class::Std;
+
+my %filehandle : ATTR( :set<filehandle>, :default<undef> );
+
+# These are class variables used to set global behaviour.
+my %ClassOptions = (
+    programname => q{ArrayMAGE},
+    separator   => q{.},
+    design      => undef,
+);
+
+$ClassOptions{namespace} = sprintf( "ebi.ac.uk:%s", $ClassOptions{programname} );
+
+# Define instance/class methods to set and get options.
+{
+    ## no critic ProhibitNoStrict
+    no strict qw(refs);
+    ## use critic ProhibitNoStrict
+
+    foreach my $method ( qw(programname namespace separator design) ) {
+	my $setter = "set_$method";
+	my $getter = "get_$method";
+
+	*{$setter} = sub {
+	    my ( $class, @args ) = @_;
+
+	    unless ( scalar @args && defined $args[0] ) {
+		croak("Error: no value passed to $method setter.\n");
+	    }
+
+	    $ClassOptions{$method} = $args[0];
+
+	    return;
+	};
+
+	*{$getter} = sub {
+	    
+	    my ( $class ) = @_;
+
+	    return $ClassOptions{$method};
+	}
+    }
+}
+
+# Convenience import method.
+sub import {
+
+    my ( $pkg, @tags ) = @_;
+
+    foreach (@tags) {
+        if ( $_ =~ m/ALL/i ) {
+            import_all();
+        }
+    }
+
+    return;
+}
+
+sub import_all {
+
+    eval {
+        require ArrayExpress::ArrayMAGE::ArrayDesign;
+        require ArrayExpress::ArrayMAGE::BioSequence;
+        require ArrayExpress::ArrayMAGE::CompositeGroup;
+        require ArrayExpress::ArrayMAGE::CompositeSequence;
+        require ArrayExpress::ArrayMAGE::Database;
+        require ArrayExpress::ArrayMAGE::Feature;
+        require ArrayExpress::ArrayMAGE::FeatureReporterMap;
+        require ArrayExpress::ArrayMAGE::Reporter;
+        require ArrayExpress::ArrayMAGE::ReporterCompositeMap;
+        require ArrayExpress::ArrayMAGE::ReporterGroup;
+        require ArrayExpress::ArrayMAGE::Zone;
+        require ArrayExpress::ArrayMAGE::ProgressBar;
+    };
+
+    if ($@) {
+        croak "ArrayMAGE load error: $@";
+    }
+
+    return;
+}
+
+sub combine_parts {
+
+    # Given hashrefs to a general set of ArrayMAGEs and the
+    # ReporterGroups ArrayMAGEs, merge everything with a bit of glue
+    # and output to the passed filehandle.
+
+    my ( $class, $objects, $reporter_group, $composite_group, $fh ) = @_;
+
+    # Close out the various XML tags.
+    foreach my $object ( values %{ $objects },
+			 values %{ $reporter_group },
+			 values %{ $composite_group } ) {
+	$object->end();
+    }
+
+    my $namespace = $class->get_namespace();
+    my $design    = $class->get_design();
+    my $separator = $class->get_separator();
+
+    print STDOUT ("\nCombining MAGE-ML parts into output file...\n");
+
+    print $fh <<"MAGE_OUT";
+<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
+<!DOCTYPE MAGE-ML PUBLIC "-//OMG//DTD MAGE-ML 1.1//EN"  "MAGE-ML.dtd">
+<MAGE-ML identifier="MAGE:$namespace:$design">
+MAGE_OUT
+
+    foreach my $type qw(Database BioSequence) {
+        $objects->{$type}->output($fh) if $objects->{$type};
+    }
+
+    print $fh <<"MAGE_OUT";
+  <DesignElement_package>
+MAGE_OUT
+
+    foreach my $type
+        qw(CompositeSequence Reporter ReporterCompositeMap FeatureReporterMap)
+    {
+        $objects->{$type}->output($fh) if $objects->{$type};
+    }
+
+    print $fh <<"MAGE_OUT";
+  </DesignElement_package>
+MAGE_OUT
+
+    print $fh <<"MAGE_OUT";
+  <ArrayDesign_package>
+MAGE_OUT
+
+    if ( scalar( grep { defined $_ } values %$reporter_group ) ) {
+	print $fh <<"MAGE_OUT";
+    <ReporterGroup_assnlist>
+MAGE_OUT
+
+	foreach my $group ( values %$reporter_group ) {
+	    $group->output($fh);
+	}
+	print $fh <<"MAGE_OUT";
+    </ReporterGroup_assnlist>
+MAGE_OUT
+    }
+
+    if ( scalar( grep { defined $_ } values %$composite_group ) ) {
+        print $fh <<"MAGE_OUT";
+    <CompositeGroup_assnlist>
+MAGE_OUT
+        foreach my $group ( values %$composite_group ) {
+            $group->output($fh);
+        }
+        print $fh <<"MAGE_OUT";
+    </CompositeGroup_assnlist>
+MAGE_OUT
+    }
+
+    foreach my $type qw(ArrayDesign Feature) {
+        $objects->{$type}->output($fh) if $objects->{$type};
+    }
+
+    if ( scalar( grep { defined $_ } values %$reporter_group ) ) {
+        print $fh <<"MAGE_OUT";
+        <ReporterGroups_assnreflist>
+MAGE_OUT
+        foreach my $key ( keys %$reporter_group ) {
+            print $fh <<"MAGE_OUT";
+          <ReporterGroup_ref identifier="$namespace:ReporterGroup:$design$separator$key"/>
+MAGE_OUT
+        }
+        print $fh <<"MAGE_OUT";
+        </ReporterGroups_assnreflist>
+MAGE_OUT
+    }
+
+    if ( scalar( grep { defined $_ } values %$composite_group ) ) {
+        print $fh <<"MAGE_OUT";
+        <CompositeGroups_assnreflist>
+MAGE_OUT
+        foreach my $key ( keys %$composite_group ) {
+            print $fh <<"MAGE_OUT";
+          <CompositeGroup_ref identifier="$namespace:CompositeGroup:$design$separator$key"/>
+MAGE_OUT
+        }
+        print $fh <<"MAGE_OUT";
+        </CompositeGroups_assnreflist>
+MAGE_OUT
+    }
+
+    foreach my $type qw(Zone) {
+        $objects->{$type}->output($fh) if $objects->{$type};
+    }
+
+    print $fh <<"MAGE_OUT";
+      </PhysicalArrayDesign>
+    </ArrayDesign_assnlist>
+  </ArrayDesign_package>
+</MAGE-ML>
+MAGE_OUT
+
+    return;
+}
+
+sub get_fh : RESTRICTED {
+
+    my ( $self ) = @_;
+
+    unless ( $filehandle{ident $self} ) {
+        $filehandle{ident $self} = IO::File->new_tmpfile();
+    }
+    return $filehandle{ident $self};
+}
+
+sub output : RESTRICTED {
+
+    my ( $self, $output_fh ) = @_;
+
+    my $fh = $self->get_fh();
+    $fh->isa('IO::File') or die("Error: object filehandle uninitialized.");
+    seek( $fh, 0, 0 );
+    while ( my $line = <$fh> ) {
+        print $output_fh $line;
+    }
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/ArrayMAGE/ArrayDesign.pm b/lib/ArrayExpress/ArrayMAGE/ArrayDesign.pm
new file mode 100644
index 0000000..de3d604
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE/ArrayDesign.pm
@@ -0,0 +1,42 @@
+#!/usr/bin/env perl
+#
+# $Id: ArrayDesign.pm 1937 2008-02-10 13:15:30Z tfrayner $
+
+use strict;
+use warnings;
+
+###############
+# ArrayDesign #
+###############
+package ArrayExpress::ArrayMAGE::ArrayDesign;
+use base 'ArrayExpress::ArrayMAGE';
+
+my %accession   : ATTR( :get<accession>, :init_arg<accession>, :default<undef> );
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    my $identifier = $self->get_accession();
+    defined( $identifier )
+        or croak("Error: ArrayDesign accession must be set");
+
+    print { $self->get_fh() } <<"MAGE_OUT";
+    <ArrayDesign_assnlist>
+      <PhysicalArrayDesign identifier="$identifier">
+MAGE_OUT
+
+    return;
+}
+
+sub end : RESTRICTED {
+
+    my $self = shift;
+
+    print { $self->get_fh() } <<"MAGE_OUT";
+MAGE_OUT
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/ArrayMAGE/BioSequence.pm b/lib/ArrayExpress/ArrayMAGE/BioSequence.pm
new file mode 100644
index 0000000..27ed7e4
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE/BioSequence.pm
@@ -0,0 +1,96 @@
+#!/usr/bin/env perl
+#
+# $Id: BioSequence.pm 1937 2008-02-10 13:15:30Z tfrayner $
+
+use strict;
+use warnings;
+
+###############
+# BioSequence #
+###############
+package ArrayExpress::ArrayMAGE::BioSequence;
+use base 'ArrayExpress::ArrayMAGE';
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    print { $self->get_fh() } <<"MAGE_OUT";
+  <BioSequence_package>
+    <BioSequence_assnlist>
+MAGE_OUT
+
+    return;
+}
+
+sub add {
+
+    my ( $self, $args ) = @_;
+
+    my $fh = $self->get_fh();
+
+    my $namespace = $self->get_namespace();
+    my $design    = $self->get_design();
+    my $separator = $self->get_separator();
+
+    defined $args->{identifier} or die;
+
+    print $fh
+        "      <BioSequence identifier=\"$namespace:BioSequence:$design$separator$args->{identifier}\"";
+    $args->{sequence} && do {
+        my $seqlen = length $args->{sequence};
+        print $fh
+            "\n                   sequence=\"$args->{sequence}\"\n                   length=\"$seqlen\"";
+    };
+    print $fh ">\n";
+
+    if ( $args->{dbrefs}
+	     && scalar( grep { defined $_ } values %{ $args->{dbrefs} } ) ) {
+        ref $args->{dbrefs} eq 'HASH' or die;
+        print $fh "        <SequenceDatabases_assnlist>\n";
+        while ( my ( $acc, $db ) = each %{ $args->{dbrefs} } ) {
+            print $fh <<"MAGE_OUT";
+          <DatabaseEntry accession="$acc">
+            <Database_assnref>
+              <Database_ref identifier="ebi.ac.uk:Database:$db"/>
+            </Database_assnref>
+          </DatabaseEntry>
+MAGE_OUT
+        }
+        print $fh "        </SequenceDatabases_assnlist>\n";
+        };
+
+    $args->{polymertype} && print $fh <<"MAGE_OUT";
+        <PolymerType_assn>
+          <OntologyEntry category="PolymerType"
+                         value="$args->{polymertype}">
+          </OntologyEntry>
+        </PolymerType_assn>
+MAGE_OUT
+
+    $args->{type} && print $fh <<"MAGE_OUT";
+        <Type_assn>
+          <OntologyEntry category="BioSequenceType"
+                         value="$args->{type}">
+          </OntologyEntry>
+        </Type_assn>
+MAGE_OUT
+
+    print $fh "      </BioSequence>\n";
+
+    return;
+}
+
+sub end : RESTRICTED {
+
+    my $self = shift;
+
+    print { $self->get_fh() } <<"MAGE_OUT";
+    </BioSequence_assnlist>
+  </BioSequence_package>
+MAGE_OUT
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/ArrayMAGE/CompositeGroup.pm b/lib/ArrayExpress/ArrayMAGE/CompositeGroup.pm
new file mode 100644
index 0000000..c91b19a
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE/CompositeGroup.pm
@@ -0,0 +1,84 @@
+#!/usr/bin/env perl
+#
+# $Id: CompositeGroup.pm 1938 2008-02-10 18:34:20Z tfrayner $
+
+use strict;
+use warnings;
+
+##################
+# CompositeGroup #
+##################
+package ArrayExpress::ArrayMAGE::CompositeGroup;
+use base 'ArrayExpress::ArrayMAGE';
+
+my %identifier   : ATTR( :get<identifier>, :init_arg<identifier>, :default<undef> );
+my %tag          : ATTR( :get<tag>,        :init_arg<tag>,        :default<undef> );
+my %is_species   : ATTR( :get<is_species>, :init_arg<is_species>, :default<undef> );
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    my $identifier = $self->get_identifier();
+    defined( $identifier )
+        or croak("Error: CompositeGroup identifier must be set");
+    my $tag = $self->get_tag();
+    defined( $tag )
+        or croak("Error: CompositeGroup tag must be set");
+
+    my $fh   = $self->get_fh();
+
+    my $namespace = $self->get_namespace();
+    my $design    = $self->get_design();
+    my $separator = $self->get_separator();
+
+    print $fh <<"MAGE_OUT";
+      <CompositeGroup identifier="$namespace:CompositeGroup:$design$separator$identifier"
+                     name="$tag">
+MAGE_OUT
+
+    $self->get_is_species() && print $fh <<"MAGE_OUT";
+        <Species_assn>
+          <OntologyEntry category="Organism"
+                         value="$tag">
+          </OntologyEntry>
+        </Species_assn>
+MAGE_OUT
+
+    print $fh <<"MAGE_OUT";
+        <CompositeSequences_assnreflist>
+MAGE_OUT
+
+    return;
+}
+
+sub add {
+
+    my ( $self, $args ) = @_;
+
+    my $namespace = $self->get_namespace();
+    my $design    = $self->get_design();
+    my $separator = $self->get_separator();
+
+    defined $args->{id_ref} or die;
+
+    print { $self->get_fh() } <<"MAGE_OUT";
+          <CompositeSequence_ref identifier="$namespace:CompositeSequence:$design$separator$args->{id_ref}"/>
+MAGE_OUT
+
+    return;
+}
+
+sub end : RESTRICTED {
+
+    my $self = shift;
+
+    print { $self->get_fh() } <<"MAGE_OUT";
+        </CompositeSequences_assnreflist>
+      </CompositeGroup>
+MAGE_OUT
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/ArrayMAGE/CompositeSequence.pm b/lib/ArrayExpress/ArrayMAGE/CompositeSequence.pm
new file mode 100644
index 0000000..1cdf34a
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE/CompositeSequence.pm
@@ -0,0 +1,119 @@
+#!/usr/bin/env perl
+#
+# $Id: CompositeSequence.pm 1937 2008-02-10 13:15:30Z tfrayner $
+
+use strict;
+use warnings;
+
+#####################
+# CompositeSequence #
+#####################
+package ArrayExpress::ArrayMAGE::CompositeSequence;
+use base 'ArrayExpress::ArrayMAGE';
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    print { $self->get_fh() } <<"MAGE_OUT";
+    <CompositeSequence_assnlist>
+MAGE_OUT
+
+    return;
+}
+
+sub add {
+
+    my ( $self, $args ) = @_;
+
+    my $fh = $self->get_fh();
+
+    my $namespace = $self->get_namespace();
+    my $design    = $self->get_design();
+    my $separator = $self->get_separator();
+
+    defined $args->{identifier} or die;
+
+    print $fh
+        "      <CompositeSequence identifier=\"$namespace:CompositeSequence:$design$separator$args->{identifier}\"";
+    $args->{name} && print $fh "\n                name=\"$args->{name}\"";
+    print $fh ">\n";
+
+    my $db_refs_provided =
+        $args->{dbrefs}
+        ? scalar( grep { defined $_ } values %{ $args->{dbrefs} } )
+        : undef;
+
+    ( $args->{comment} || $db_refs_provided ) && do {
+        print $fh "        <Descriptions_assnlist>\n          <Description";
+        $args->{comment} && print $fh " text=\"$args->{comment}\"";
+        print $fh ">\n";
+        $db_refs_provided && do {
+            print $fh "            <DatabaseReferences_assnlist>\n";
+            while ( my ( $acc, $db ) = each %{ $args->{dbrefs} } ) {
+                print $fh <<"MAGE_OUT";
+	      <DatabaseEntry accession="$acc">
+		<Database_assnref>
+		  <Database_ref identifier="ebi.ac.uk:Database:$db"/>
+		</Database_assnref>
+	      </DatabaseEntry>
+MAGE_OUT
+            }
+            print $fh "            </DatabaseReferences_assnlist>\n";
+        };
+        print $fh <<"MAGE_OUT";
+	  </Description>
+	</Descriptions_assnlist>
+MAGE_OUT
+    };
+
+    $args->{controltype} && print $fh <<"MAGE_OUT";
+        <ControlType_assn>
+          <OntologyEntry category="ControlType"
+                         value="$args->{controltype}">
+          </OntologyEntry>
+        </ControlType_assn>
+MAGE_OUT
+
+    ( $args->{biosequences} && @{ $args->{biosequences} } ) && do {
+        print $fh <<"MAGE_OUT";
+        <BiologicalCharacteristics_assnreflist>
+MAGE_OUT
+        foreach my $bsid ( @{ $args->{biosequences} } ) {
+            print $fh <<"MAGE_OUT";
+          <BioSequence_ref identifier="$namespace:BioSequence:$design$separator$bsid"/>
+MAGE_OUT
+        }
+        print $fh <<"MAGE_OUT";
+        </BiologicalCharacteristics_assnreflist>
+MAGE_OUT
+    };
+
+    # Skip RCM if we're only generating CSs:
+    unless ( $args->{cs_only} ) {
+
+	# assume this is 1:1 CompositeSequence:FRM for now (1:n FRM:Feature)
+	print $fh <<"MAGE_OUT";
+        <ReporterCompositeMaps_assnreflist>
+          <ReporterCompositeMap_ref identifier="$namespace:ReporterCompositeMap:$design$separator$args->{identifier}"/>
+        </ReporterCompositeMaps_assnreflist>
+MAGE_OUT
+    }
+
+    print $fh "      </CompositeSequence>\n";
+
+    return;
+}
+
+sub end : RESTRICTED {
+
+    my $self = shift;
+
+    print { $self->get_fh() } <<"MAGE_OUT";
+    </CompositeSequence_assnlist>
+MAGE_OUT
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/ArrayMAGE/Database.pm b/lib/ArrayExpress/ArrayMAGE/Database.pm
new file mode 100644
index 0000000..9a1c5a9
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE/Database.pm
@@ -0,0 +1,53 @@
+#!/usr/bin/env perl
+#
+# $Id: Database.pm 1937 2008-02-10 13:15:30Z tfrayner $
+
+use strict;
+use warnings;
+
+############
+# Database #
+############
+package ArrayExpress::ArrayMAGE::Database;
+use base 'ArrayExpress::ArrayMAGE';
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    print { $self->get_fh() } <<"MAGE_OUT";
+  <Description_package>
+    <Database_assnlist>
+MAGE_OUT
+
+    return;
+}
+
+sub add {
+
+    my ( $self, $args ) = @_;
+
+    defined $args->{tag} or die;
+
+    print { $self->get_fh() } <<"MAGE_OUT";
+      <Database identifier="ebi.ac.uk:Database:$args->{tag}"
+                name="$args->{tag}">
+      </Database>
+MAGE_OUT
+
+    return;
+}
+
+sub end : RESTRICTED {
+
+    my $self = shift;
+
+    print { $self->get_fh() } <<"MAGE_OUT";
+    </Database_assnlist>
+  </Description_package>
+MAGE_OUT
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/ArrayMAGE/Feature.pm b/lib/ArrayExpress/ArrayMAGE/Feature.pm
new file mode 100644
index 0000000..f7a0b91
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE/Feature.pm
@@ -0,0 +1,80 @@
+#!/usr/bin/env perl
+#
+# $Id: Feature.pm 1937 2008-02-10 13:15:30Z tfrayner $
+
+use strict;
+use warnings;
+
+###########
+# Feature #
+###########
+package ArrayExpress::ArrayMAGE::Feature;
+use base 'ArrayExpress::ArrayMAGE';
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    my $namespace = $self->get_namespace();
+    my $design    = $self->get_design();
+
+    # Omitting TechnologyType OE here FIXME
+    print { $self->get_fh() } <<"MAGE_OUT";
+        <FeatureGroups_assnlist>
+          <FeatureGroup identifier="$namespace:FeatureGroup:$design">
+            <Features_assnlist>
+MAGE_OUT
+
+    return;
+}
+
+sub add {
+
+    my ( $self, $args ) = @_;
+
+    my $fh = $self->get_fh();
+
+    defined( $args->{row} )        or die;
+    defined( $args->{column} )     or die;
+    defined( $args->{metarow} )    or die;
+    defined( $args->{metacolumn} ) or die;
+
+    my $namespace = $self->get_namespace();
+    my $design    = $self->get_design();
+    my $separator = $self->get_separator();
+
+    my $identifier = "$namespace:Feature:$design$separator";
+
+    $identifier .= $args->{identifier}
+        || "$args->{metacolumn}.$args->{metarow}.$args->{column}.$args->{row}";
+
+    print $fh <<"MAGE_OUT";
+              <Feature identifier="$identifier">
+                <Zone_assnref>
+                  <Zone_ref identifier="$namespace:Zone:$design$separator$args->{metacolumn}.$args->{metarow}"/>
+                </Zone_assnref>
+                <FeatureLocation_assn>
+                  <FeatureLocation column="$args->{column}"
+                                   row="$args->{row}">
+                  </FeatureLocation>
+                </FeatureLocation_assn>
+              </Feature>
+MAGE_OUT
+
+    return;
+}
+
+sub end : RESTRICTED {
+
+    my $self = shift;
+
+    print { $self->get_fh() } <<"MAGE_OUT";
+            </Features_assnlist>
+          </FeatureGroup>
+        </FeatureGroups_assnlist>
+MAGE_OUT
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/ArrayMAGE/FeatureReporterMap.pm b/lib/ArrayExpress/ArrayMAGE/FeatureReporterMap.pm
new file mode 100644
index 0000000..035e6fb
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE/FeatureReporterMap.pm
@@ -0,0 +1,88 @@
+#!/usr/bin/env perl
+#
+# $Id: FeatureReporterMap.pm 1937 2008-02-10 13:15:30Z tfrayner $
+
+use strict;
+use warnings;
+
+######################
+# FeatureReporterMap #
+######################
+package ArrayExpress::ArrayMAGE::FeatureReporterMap;
+use base 'ArrayExpress::ArrayMAGE';
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    print { $self->get_fh() } <<"MAGE_OUT";
+    <FeatureReporterMap_assnlist>
+MAGE_OUT
+
+    return;
+}
+
+sub add {
+
+    my ( $self, $args ) = @_;
+
+    my $fh = $self->get_fh();
+
+    defined $args->{identifier} or die;
+    defined $args->{features}   or die;
+
+    my $namespace = $self->get_namespace();
+    my $design    = $self->get_design();
+    my $separator = $self->get_separator();
+
+    print $fh <<"MAGE_OUT";
+      <FeatureReporterMap identifier="$namespace:FeatureReporterMap:$design$separator$args->{identifier}">
+        <Reporter_assnref>
+          <Reporter_ref identifier="$namespace:Reporter:$design$separator$args->{identifier}"/>
+        </Reporter_assnref>
+        <FeatureInformationSources_assnlist>
+MAGE_OUT
+
+    foreach my $identifier ( @{ $args->{features} } ) {
+        print $fh <<"MAGE_OUT";
+          <FeatureInformation>
+            <Feature_assnref>
+              <Feature_ref identifier="$namespace:Feature:$design$separator$identifier"/>
+            </Feature_assnref>
+MAGE_OUT
+
+        if ( $args->{mismatch_info} && $args->{mismatch_info}{$identifier} ) {
+            my %info = %{ $args->{mismatch_info}{$identifier} };
+            print $fh <<"MAGE_OUT";
+            <MismatchInformation_assnlist>
+              <MismatchInformation startCoord="$info{start_coord}"
+                                   newSequence="$info{new_sequence}"
+                                   replacedLength="$info{replaced_length}"/>
+            </MismatchInformation_assnlist>
+MAGE_OUT
+        }
+        print $fh <<"MAGE_OUT";
+          </FeatureInformation>
+MAGE_OUT
+    }
+
+    print $fh <<"MAGE_OUT";
+        </FeatureInformationSources_assnlist>
+      </FeatureReporterMap>
+MAGE_OUT
+
+    return;
+}
+
+sub end : RESTRICTED {
+
+    my $self = shift;
+
+    print { $self->get_fh() } <<"MAGE_OUT";
+    </FeatureReporterMap_assnlist>
+MAGE_OUT
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/ArrayMAGE/NullObject.pm b/lib/ArrayExpress/ArrayMAGE/NullObject.pm
new file mode 100644
index 0000000..909211f
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE/NullObject.pm
@@ -0,0 +1,25 @@
+#!/usr/bin/env perl
+#
+# $Id: NullObject.pm 2088 2008-06-25 13:23:09Z tfrayner $
+
+use strict;
+use warnings;
+
+######################
+# NullObject package #
+######################
+
+package ArrayExpress::ArrayMAGE::NullObject;
+
+use Class::Std;
+
+# This package provides empty objects which don't store information
+# when e.g. only exporting CompositeSequences for very large CDFs.
+
+sub add {}
+
+sub end {}
+
+sub output {}
+
+1;
diff --git a/lib/ArrayExpress/ArrayMAGE/ProgressBar.pm b/lib/ArrayExpress/ArrayMAGE/ProgressBar.pm
new file mode 100644
index 0000000..af02999
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE/ProgressBar.pm
@@ -0,0 +1,40 @@
+#!/usr/bin/env perl
+#
+# $Id: ProgressBar.pm 1858 2007-12-17 12:47:08Z tfrayner $
+
+use strict;
+use warnings;
+
+###############
+# ProgressBar #
+###############
+# Progress bar code
+# Credit to perlmonks user tmoertel (see PM node 396857)
+# TGM 2004-10-05
+
+package ArrayExpress::ArrayMAGE::ProgressBar;
+use Class::Struct;
+use List::Util qw( min );
+
+struct( width => '$', portion => '$', max_val => '$' );
+
+sub draw {
+
+    my ( $self, $x, $max_val ) = @_;
+
+    local $| = 1;
+
+    $max_val ||= $self->max_val;
+    my $old_portion = $self->portion || 0;
+    my $new_portion = int( $self->width * $x / $max_val + 0.5 );
+    print "\b" x ( 6 + $self->width - min( $new_portion, $old_portion ) )
+        if defined $self->portion;
+    print "*" x ( $new_portion - $old_portion ),
+        " " x ( $self->width - $new_portion ), sprintf " %3d%% ",
+        int( 100 * $x / $max_val + 0.5 );
+    $self->portion($new_portion);
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/ArrayMAGE/Reporter.pm b/lib/ArrayExpress/ArrayMAGE/Reporter.pm
new file mode 100644
index 0000000..39d3b27
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE/Reporter.pm
@@ -0,0 +1,131 @@
+#!/usr/bin/env perl
+#
+# $Id: Reporter.pm 1937 2008-02-10 13:15:30Z tfrayner $
+
+use strict;
+use warnings;
+
+############
+# Reporter #
+############
+package ArrayExpress::ArrayMAGE::Reporter;
+use base 'ArrayExpress::ArrayMAGE';
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    print { $self->get_fh() } <<"MAGE_OUT";
+    <Reporter_assnlist>
+MAGE_OUT
+
+    return;
+}
+
+sub add {
+
+    my ( $self, $args ) = @_;
+
+    my $fh = $self->get_fh();
+
+    my $namespace = $self->get_namespace();
+    my $design    = $self->get_design();
+    my $separator = $self->get_separator();
+
+    defined $args->{identifier} or die;
+
+    print $fh
+        "      <Reporter identifier=\"$namespace:Reporter:$design$separator$args->{identifier}\"";
+    $args->{name} && print $fh "\n                name=\"$args->{name}\"";
+    print $fh ">\n";
+
+    my $db_refs_provided =
+        $args->{dbrefs}
+        ? scalar( grep { defined $_ } values %{ $args->{dbrefs} } )
+        : undef;
+
+    ( $args->{comment} || $db_refs_provided ) && do {
+        print $fh "        <Descriptions_assnlist>\n          <Description";
+        $args->{comment} && print $fh " text=\"$args->{comment}\"";
+        print $fh ">\n";
+        $db_refs_provided && do {
+            print $fh "            <DatabaseReferences_assnlist>\n";
+            while ( my ( $acc, $db ) = each %{ $args->{dbrefs} } ) {
+                print $fh <<"MAGE_OUT";
+	      <DatabaseEntry accession="$acc">
+		<Database_assnref>
+		  <Database_ref identifier="ebi.ac.uk:Database:$db"/>
+		</Database_assnref>
+	      </DatabaseEntry>
+MAGE_OUT
+            }
+            print $fh "            </DatabaseReferences_assnlist>\n";
+        };
+        print $fh <<"MAGE_OUT";
+	  </Description>
+	</Descriptions_assnlist>
+MAGE_OUT
+    };
+
+    $args->{controltype} && print $fh <<"MAGE_OUT";
+        <ControlType_assn>
+          <OntologyEntry category="ControlType"
+                         value="$args->{controltype}">
+          </OntologyEntry>
+        </ControlType_assn>
+MAGE_OUT
+
+    ( $args->{biosequences} && @{ $args->{biosequences} } ) && do {
+        print $fh <<"MAGE_OUT";
+        <ImmobilizedCharacteristics_assnreflist>
+MAGE_OUT
+        foreach my $bsid ( @{ $args->{biosequences} } ) {
+            print $fh <<"MAGE_OUT";
+          <BioSequence_ref identifier="$namespace:BioSequence:$design$separator$bsid"/>
+MAGE_OUT
+        }
+        print $fh <<"MAGE_OUT";
+        </ImmobilizedCharacteristics_assnreflist>
+MAGE_OUT
+    };
+
+    $args->{warningtype} && print $fh <<"MAGE_OUT";
+        <WarningType_assn>
+          <OntologyEntry category="WarningType"
+                         value="$args->{warningtype}">
+          </OntologyEntry>
+        </WarningType_assn>
+MAGE_OUT
+
+    $args->{failtype} && print $fh <<"MAGE_OUT";
+        <FailTypes_assnlist>
+          <OntologyEntry category="FailType"
+                         value="$args->{failtype}">
+          </OntologyEntry>
+        </FailTypes_assnlist>
+MAGE_OUT
+
+    # assume this is 1:1 Reporter:FRM for now (1:n FRM:Feature)
+    print $fh <<"MAGE_OUT";
+        <FeatureReporterMaps_assnreflist>
+          <FeatureReporterMap_ref identifier="$namespace:FeatureReporterMap:$design$separator$args->{identifier}"/>
+        </FeatureReporterMaps_assnreflist>
+MAGE_OUT
+
+    print $fh "      </Reporter>\n";
+
+    return;
+}
+
+sub end : RESTRICTED {
+
+    my $self = shift;
+
+    print { $self->get_fh() } <<"MAGE_OUT";
+    </Reporter_assnlist>
+MAGE_OUT
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/ArrayMAGE/ReporterCompositeMap.pm b/lib/ArrayExpress/ArrayMAGE/ReporterCompositeMap.pm
new file mode 100644
index 0000000..30ede8e
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE/ReporterCompositeMap.pm
@@ -0,0 +1,75 @@
+#!/usr/bin/env perl
+#
+# $Id: ReporterCompositeMap.pm 1937 2008-02-10 13:15:30Z tfrayner $
+
+use strict;
+use warnings;
+
+########################
+# ReporterCompositeMap #
+########################
+package ArrayExpress::ArrayMAGE::ReporterCompositeMap;
+use base 'ArrayExpress::ArrayMAGE';
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    print { $self->get_fh() } <<"MAGE_OUT";
+    <ReporterCompositeMap_assnlist>
+MAGE_OUT
+
+    return;
+}
+
+sub add {
+
+    my ( $self, $args ) = @_;
+
+    my $fh = $self->get_fh();
+
+    defined $args->{identifier} or die;
+    defined $args->{reporters}  or die;
+
+    my $namespace = $self->get_namespace();
+    my $design    = $self->get_design();
+    my $separator = $self->get_separator();
+
+    print $fh <<"MAGE_OUT";
+      <ReporterCompositeMap identifier="$namespace:ReporterCompositeMap:$design$separator$args->{identifier}">
+        <CompositeSequence_assnref>
+          <CompositeSequence_ref identifier="$namespace:CompositeSequence:$design$separator$args->{identifier}"/>
+        </CompositeSequence_assnref>
+        <ReporterPositionSources_assnlist>
+MAGE_OUT
+
+    foreach my $reporter ( @{ $args->{reporters} } ) {
+        print $fh <<"MAGE_OUT";
+          <ReporterPosition>
+            <Reporter_assnref>
+              <Reporter_ref identifier="$namespace:Reporter:$design$separator$reporter"/>
+            </Reporter_assnref>
+          </ReporterPosition>
+MAGE_OUT
+    }
+
+    print $fh <<"MAGE_OUT";
+        </ReporterPositionSources_assnlist>
+      </ReporterCompositeMap>
+MAGE_OUT
+
+    return;
+}
+
+sub end : RESTRICTED {
+
+    my $self = shift;
+
+    print { $self->get_fh() } <<"MAGE_OUT";
+    </ReporterCompositeMap_assnlist>
+MAGE_OUT
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/ArrayMAGE/ReporterGroup.pm b/lib/ArrayExpress/ArrayMAGE/ReporterGroup.pm
new file mode 100644
index 0000000..27e3e7a
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE/ReporterGroup.pm
@@ -0,0 +1,84 @@
+#!/usr/bin/env perl
+#
+# $Id: ReporterGroup.pm 1938 2008-02-10 18:34:20Z tfrayner $
+
+use strict;
+use warnings;
+
+#################
+# ReporterGroup #
+#################
+package ArrayExpress::ArrayMAGE::ReporterGroup;
+use base 'ArrayExpress::ArrayMAGE';
+
+my %identifier   : ATTR( :get<identifier>, :init_arg<identifier>, :default<undef> );
+my %tag          : ATTR( :get<tag>,        :init_arg<tag>,        :default<undef> );
+my %is_species   : ATTR( :get<is_species>, :init_arg<is_species>, :default<undef> );
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    my $identifier = $self->get_identifier();
+    defined( $identifier )
+        or croak("Error: ReporterGroup identifier must be set");
+    my $tag = $self->get_tag();
+    defined( $tag )
+        or croak("Error: ReporterGroup tag must be set");
+
+    my $fh   = $self->get_fh();
+
+    my $namespace = $self->get_namespace();
+    my $design    = $self->get_design();
+    my $separator = $self->get_separator();
+
+    print $fh <<"MAGE_OUT";
+      <ReporterGroup identifier="$namespace:ReporterGroup:$design$separator$identifier"
+                     name="$tag">
+MAGE_OUT
+
+    $self->get_is_species() && print $fh <<"MAGE_OUT";
+        <Species_assn>
+          <OntologyEntry category="Organism"
+                         value="$tag">
+          </OntologyEntry>
+        </Species_assn>
+MAGE_OUT
+
+    print $fh <<"MAGE_OUT";
+        <Reporters_assnreflist>
+MAGE_OUT
+
+    return;
+}
+
+sub add {
+
+    my ( $self, $args ) = @_;
+
+    my $namespace = $self->get_namespace();
+    my $design    = $self->get_design();
+    my $separator = $self->get_separator();
+
+    defined $args->{id_ref} or die;
+
+    print { $self->get_fh() } <<"MAGE_OUT";
+          <Reporter_ref identifier="$namespace:Reporter:$design$separator$args->{id_ref}"/>
+MAGE_OUT
+
+    return;
+}
+
+sub end : RESTRICTED {
+
+    my $self = shift;
+
+    print { $self->get_fh() } <<"MAGE_OUT";
+        </Reporters_assnreflist>
+      </ReporterGroup>
+MAGE_OUT
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/ArrayMAGE/Zone.pm b/lib/ArrayExpress/ArrayMAGE/Zone.pm
new file mode 100644
index 0000000..6d29268
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE/Zone.pm
@@ -0,0 +1,62 @@
+#!/usr/bin/env perl
+#
+# $Id: Zone.pm 1937 2008-02-10 13:15:30Z tfrayner $
+
+use strict;
+use warnings;
+
+########
+# Zone #
+########
+package ArrayExpress::ArrayMAGE::Zone;
+use base 'ArrayExpress::ArrayMAGE';
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    # Missing zonesPerX, zonesPerY FIXME
+    print { $self->get_fh() } <<"MAGE_OUT";
+        <ZoneGroups_assnlist>
+          <ZoneGroup>
+            <ZoneLocations_assnlist>
+MAGE_OUT
+
+    return;
+}
+
+sub add {
+
+    my ( $self, $args ) = @_;
+
+    defined $args->{row}    or die;
+    defined $args->{column} or die;
+
+    my $namespace = $self->get_namespace();
+    my $design    = $self->get_design();
+    my $separator = $self->get_separator();
+
+    print { $self->get_fh() } <<"MAGE_OUT";
+              <Zone column="$args->{column}"
+                    row="$args->{row}"
+                    identifier="$namespace:Zone:$design$separator$args->{column}.$args->{row}">
+              </Zone>
+MAGE_OUT
+
+    return;
+}
+
+sub end : RESTRICTED {
+
+    my $self = shift;
+
+    print { $self->get_fh() } <<"MAGE_OUT";
+            </ZoneLocations_assnlist>
+          </ZoneGroup>
+        </ZoneGroups_assnlist>
+MAGE_OUT
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/Creator.pm b/lib/ArrayExpress/AutoSubmission/Creator.pm
new file mode 100644
index 0000000..4cf791b
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/Creator.pm
@@ -0,0 +1,514 @@
+#!/usr/bin/env perl
+#
+# Module used to create new submissions in the absence of a Webform.
+#
+# Tim Rayner 2007, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Creator.pm 2069 2008-06-04 14:33:52Z tfrayner $
+#
+
+package ArrayExpress::AutoSubmission::Creator;
+
+use strict;
+use warnings;
+
+use Carp;
+use Class::Std;
+use File::Copy;
+use File::Basename;
+use File::Spec;
+use English qw( -no_match_vars );
+
+use ArrayExpress::Curator::Common qw(date_now);
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+require ArrayExpress::AutoSubmission::DB::Experiment;
+require ArrayExpress::AutoSubmission::DB::User;
+require ArrayExpress::AutoSubmission::DB::Organism;
+require ArrayExpress::AutoSubmission::DB::DataFile;
+require ArrayExpress::AutoSubmission::DB::Spreadsheet;
+
+my %login             : ATTR(:name<login>,             :init_arg<login>,             :default<undef>);
+my %name              : ATTR(:name<name>,              :init_arg<name>,              :default<undef>);
+my %spreadsheet       : ATTR(:name<spreadsheet>,       :init_arg<spreadsheet>,       :default<undef>);
+my %data_files        : ATTR(:name<data_files>,        :init_arg<data_files>,        :default<[]>);
+my %accession         : ATTR(:name<accession>,         :init_arg<accession>,         :default<undef>);
+my %comment           : ATTR(:name<comment>,           :init_arg<comment>,           :default<undef>);
+my %experiment_type   : ATTR(:name<experiment_type>,   :init_arg<experiment_type>,   :default<undef>);
+my %clobber           : ATTR(:name<clobber>,           :init_arg<clobber>,           :default<0>);
+my %organisms         : ATTR(:name<organisms>,         :init_arg<organisms>,         :default<[]>);
+my %user              : ATTR();
+my %experiment        : ATTR();
+
+sub START {
+    my ( $self, $id, $args ) = @_;
+
+    # Basic usage validation.
+    croak("Error: no login set")
+	unless ( $self->get_login() );
+
+    croak("Error: no name set")
+	unless ( $self->get_name() );
+
+    croak("Error: no spreadsheet set")
+	unless ( $self->get_spreadsheet() );
+
+    croak("Error: no experiment_type set")
+	unless ( $self->get_experiment_type() );
+
+    croak("Error: data_files must be an array reference")
+	unless ( ref( $self->get_data_files() ) eq 'ARRAY' );
+
+    croak("Error: organisms must be an array reference")
+	unless ( ref( $self->get_organisms() ) eq 'ARRAY' );
+
+    return;
+}
+
+sub _get_user_approval : PRIVATE {
+
+    my ( $self, $message ) = @_;
+    
+    print STDERR $message;
+    chomp( my $response = lc <STDIN> );
+
+    if ($response eq 'y') { return 1 }
+
+    return;
+}
+
+{   # Create a closure over this array.
+    my @pass_chars = ( 'A'..'Z', 'a'..'z', 0..9 );
+
+    sub mk_passwd {
+
+	my $self = shift;
+
+	# Simply returns a random string suitable for use as a
+	# password. Can pass an optional length argument, otherwise
+	# defaults to eight chars.
+
+	my $length = shift || 8;
+
+	my $password = q{};
+	for ( 1..$length ) {
+	    $password .= $pass_chars[int rand(scalar @pass_chars)];
+	}
+	return $password;
+    }
+}
+
+sub insert_spreadsheet {
+
+    my $self = shift;
+
+    my $expt = $self->get_experiment();
+
+    my $spreadsheet = basename($self->get_spreadsheet());
+
+    my $target_file = File::Spec->catfile(
+	$expt->filesystem_directory(),
+	$spreadsheet,
+    );
+
+    # Ask what to do for preexisting files.
+    if ( -e $target_file ) {
+
+	if ( $self->get_clobber()
+	  || $self->_get_user_approval(
+	      "Warning: submission has pre-existing file $target_file. Overwrite [y/N] ? ") ) {
+
+	    unlink($target_file)
+		or die("Error removing old spreadsheet $target_file: $!");
+	}
+	else{
+	    printf STDERR ("Skipping insertion of spreadsheet $spreadsheet.\n");
+	    return;
+	}
+    }
+    # Make sure we don't end up with multiple spreadsheets mapped to
+    # the submission. Note that we don't clean up the filesystem, just
+    # the DB mapping.
+    my $ss_iterator = $expt->spreadsheets(is_deleted => 0);
+    while ( my $old_spreadsheet = $ss_iterator->next() ) {
+	$old_spreadsheet->set('is_deleted' => 1);
+	$old_spreadsheet->update();
+    }
+
+    # Write out the new spreadsheet, enter it into the DB and fix the
+    # permissions (no basename on copy).
+    copy($self->get_spreadsheet(), $target_file)
+	or die(
+	    sprintf(
+		"Error copying spreadsheet %s to submissions directory: %s",
+		$self->get_spreadsheet(),
+		$!,
+	    )
+	);
+    chmod(oct($CONFIG->get_FILE_PERMISSIONS()), $target_file)
+	or warn ("Warning: problem setting permissions on spreadsheet $target_file: $!");
+    my $db_spreadsheet = ArrayExpress::AutoSubmission::DB::Spreadsheet->insert({
+	experiment_id => $expt,
+	name          => $spreadsheet,
+	is_deleted    => 0,
+    });
+
+    # Make a backup as well.
+    my $backup = "$target_file.backup." . date_now();
+    copy($target_file, $backup)
+	or warn ("Error backing up spreadsheet $spreadsheet ($backup) in submissions directory: $!");
+    chmod(oct('0444'), $backup)
+	or warn ("Warning: problem setting permissions on backup $backup: $!");
+
+    return;
+}
+
+sub insert_data_files {
+
+    my $self = shift;
+
+    my $expt = $self->get_experiment();
+
+    DATAFILE:
+    foreach my $data_file ( @{ $self->get_data_files() } ) {
+
+	my $target_file = File::Spec->catfile(
+	    $expt->filesystem_directory(),
+	    basename($data_file),
+	);
+
+	# Ask what to do for preexisting files.
+	if ( -e $target_file ) {
+	    if ( $self->get_clobber()
+	      || $self->_get_user_approval(
+		  "Warning: submission has pre-existing file $target_file. Overwrite [y/N] ? ") ) {
+
+		unlink($target_file)
+		    or die("Error removing old data file $target_file: $!");
+	    }
+	    else {
+		print STDERR ("Skipping insertion of data file $data_file.\n");
+		next DATAFILE;
+	    }
+	}
+
+	# Write out the new data file, enter it into the DB and fix the permissions.
+	copy($data_file, $target_file)
+	    or die("Error copying data file $data_file to submissions directory: $!");
+	chmod (oct($CONFIG->get_FILE_PERMISSIONS()), $target_file)
+	    or warn("Warning: problem setting permissions on data archive $target_file: $!");
+	my $db_file = ArrayExpress::AutoSubmission::DB::DataFile->find_or_create(
+	    experiment_id => $expt,
+	    name          => basename($data_file),
+	    is_deleted    => 0,
+	);
+	$db_file->set(is_unpacked => 0);
+	$db_file->update();
+    }
+
+    return;
+}
+
+sub get_user {
+
+    my $self = shift;
+
+    # Return any previously assigned user object.
+    return $user{ident $self} if $user{ident $self};
+
+    # Sort out our user account, creating one if necessary.
+    $user{ident $self} = ArrayExpress::AutoSubmission::DB::User->retrieve(
+	login      => $self->get_login(),
+	is_deleted => 0,
+    );
+    unless ($user{ident $self}) {
+
+	if (  $self->get_clobber()
+	   || $self->_get_user_approval(
+	       sprintf("Warning: user %s does not yet exist. Create [y/N] ? ",
+		       $self->get_login()))) {
+
+	    print STDERR ("Creating user...\n");
+	    $user{ident $self} = ArrayExpress::AutoSubmission::DB::User->find_or_create(
+		login      => $self->get_login(),
+		name       => $self->get_login(),
+		email      => $CONFIG->get_AUTOSUBS_CURATOR_EMAIL(),
+		password   => $self->mk_passwd(),
+		created_at => date_now(),
+		is_deleted => 0,
+	    ) or die(
+		sprintf("Error creating user %s in database: %s",
+			$self->get_login(), $!)
+	    );
+	}
+	else {
+	    die("Cannot insert a submission without a user account. Exiting.\n");
+	}
+    }
+
+    return $user{ident $self};
+}
+
+sub get_experiment {
+
+    my $self = shift;
+
+    # Return any previously assigned experiment object.
+    return $experiment{ident $self} if $experiment{ident $self};
+
+    # Check pre-existing accession, update if needed.
+    if ( $self->get_accession() ) {
+	$experiment{ident $self} = ArrayExpress::AutoSubmission::DB::Experiment->retrieve(
+	    accession       => $self->get_accession(),
+	    is_deleted      => 0,
+	);
+
+	# Update pre-existing experiment.
+	if ( $experiment{ident $self} ) {
+
+	    # We're now allowing experiments to switch between types,
+	    # but we don't want duplicated accessions. As a result, if
+	    # we try and create an experiment with a pre-existing
+	    # accession, but of the wrong type, we want to catch that
+	    # here.
+	    unless ( $experiment{ident $self}->experiment_type()
+			 eq $self->get_experiment_type() ) {
+		croak(
+		    sprintf(
+			qq{Error: Pre-existing experiment in database is not of type "%s".\n},
+			$self->get_experiment_type(),
+		    ),
+		);
+	    }
+
+	    # Make sure it's okay to overwrite stuff.
+	    if ( $self->get_clobber()
+	      || $self->_get_user_approval(
+		  sprintf("Experiment with accession %s already exists. "
+			. "Update this experiment (this may overwrite an old spreadsheet) [y/N] ? ",
+			  $self->get_accession())
+	      ) ) {
+
+		# postpone checking until insertion complete.
+		$experiment{ident $self}->set(
+		    in_curation    => 0,
+		    date_submitted => date_now(),
+		    name           => $self->get_name(),
+		    user_id        => $self->get_user(),
+		);
+
+		# FIXME allow updates of e.g. organisms here.
+		$experiment{ident $self}->update();
+	    }
+	    else {
+		die(sprintf(
+		    "Will not create duplicate experiment %s. Quitting.\n",
+		    $self->get_accession(),
+		));
+	    }
+	}
+    }
+
+    # New experiment created here.
+    unless ( $experiment{ident $self} ) {
+
+	if ( ArrayExpress::AutoSubmission::DB::Experiment->retrieve(
+	    name       => $self->get_name(),
+	    is_deleted => 0,
+	) ) {
+	    die(qq{Error: pre-existing experiment in database with name "}
+		    . $self->get_name() . qq{". Exiting.\n});
+	}
+
+	print STDERR ("Creating experiment...\n");
+	$experiment{ident $self} = ArrayExpress::AutoSubmission::DB::Experiment->insert({
+	    name            => $self->get_name(),
+	    user_id         => $self->get_user(),
+	    accession       => $self->get_accession(),
+	    in_curation     => 0,    # Don't start checking just yet!
+	    num_submissions => 0,
+	    experiment_type => $self->get_experiment_type(),
+	    comment         => $self->get_comment(),
+	    date_submitted  => date_now(),
+	    is_deleted      => 0,
+	});
+
+	# Add any species that we know about.
+	foreach my $species_name ( @{ $self->get_organisms() } ) {
+	    if ( my @organisms
+		     = ArrayExpress::AutoSubmission::DB::Organism->search(
+			 scientific_name => $species_name,
+			 is_deleted      => 0, ) ) {
+
+		if ( scalar @organisms == 1 ) {
+		    $experiment{ident $self}->add_to_organism_instances({
+			experiment_id => $experiment{ident $self},
+			organism_id   => $organisms[0],
+		    });
+		}
+		else {
+		    croak(qq{Warning: Multiple organisms named "$species_name" found in database.\n});
+		}
+	    }
+	    else {
+		croak(qq{Error: Organism name "$species_name" not found in database.\n});
+	    }
+	}
+	$experiment{ident $self}->update();
+    }
+
+    return $experiment{ident $self};
+}
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../index.html">
+                <img src="../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: Creator.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::AutoSubmission::Creator - A class providing convenience
+methods for experiment object creation and insertion into the
+autosubmissions tracking database.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::AutoSubmission::Creator;
+ my $creator = ArrayExpress::AutoSubmission::Creator->new({
+    login           => $login,
+    name            => $expt_name,
+    spreadsheet     => $startfile,
+    data_files      => \@datafiles,
+    accession       => $accession,
+    experiment_type => 'MAGE-TAB',
+    comment         => 'Submission inserted manually',
+    clobber         => 0,
+ });
+
+ # Create the experiment and insert the spreadsheet.
+ my $expt = $creator->get_experiment();
+
+ # Copy the files to the submissions directory.
+ print STDERR ("Copying files...\n");
+ $creator->insert_spreadsheet();
+ $creator->insert_data_files();
+
+=head1 DESCRIPTION
+
+This module provides some basic convenience methods to automate error
+handling when inserting new experiments into the tracking database.
+
+=head1 METHODS
+
+=over 2
+
+=item C<new()>
+
+Object constructor. Takes a hashref of options, including the following:
+
+=over 4
+
+=item C<login>
+
+The login name of the user to which the experiment should be assigned.
+
+=item C<name>
+
+The name of the experiment
+
+=item C<spreadsheet>
+
+The path to spreadsheet file to be inserted.
+
+=item C<data_files>
+
+An arrayref listing the paths to the data files to be inserted.
+
+=item C<accession>
+
+The accession number for the experiment.
+
+=item C<comment>
+
+Any comments to be attached to the experiment.
+
+=item C<experiment_type>
+
+The experiment type. This should typically be one of the following:
+MAGE-TAB, Tab2MAGE, MIAMExpress, GEO, MUGEN, Unknown.
+
+=item C<clobber>
+
+A flag indicating whether to overwrite files without prompting the user.
+
+=item C<organisms>
+
+An arrayref of organism scientific names to associate with this
+experiment.
+
+=back
+
+=item C<mk_passwd>
+
+Returns a random string suitable for use as an account password. Can
+pass an optional length argument, otherwise defaults to eight characters.
+
+=item C<insert_spreadsheet>
+
+Copy the spreadsheet file into the appropriate filesystem location and
+insert a record into the database.
+
+=item C<insert_data_files>
+
+Copy the data files into the appropriate filesystem location and
+insert records into the database.
+
+=item C<get_user>
+
+Returns the appropriate Class::DBI user object as retrieved from (or
+inserted into) the database.
+
+=item C<get_experiment>
+
+Returns the appropriate Class::DBI experiment object as retrieved from
+(or inserted into) the database.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2008.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments. 
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB.pm b/lib/ArrayExpress/AutoSubmission/DB.pm
new file mode 100644
index 0000000..8ad8829
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB.pm
@@ -0,0 +1,3539 @@
+#!/usr/bin/env perl
+#
+# Module used to interact with the back-end submissions DB. The table
+# classes are defined in the DB/ subdirectory.
+#
+# Tim Rayner 2006, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: DB.pm 2069 2008-06-04 14:33:52Z tfrayner $
+#
+
+use strict;
+use warnings;
+use Class::DBI;
+
+package ArrayExpress::AutoSubmission::DB;
+use base 'Class::DBI';
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+# DUMMY used to fool our test scripts (connection requires a DSN).
+ArrayExpress::AutoSubmission::DB->connection(
+    ($CONFIG->get_AUTOSUBS_DSN() || 'DUMMY'),
+    $CONFIG->get_AUTOSUBS_USERNAME(),
+    $CONFIG->get_AUTOSUBS_PASSWORD(),
+    $CONFIG->get_AUTOSUBS_DBPARAMS(),
+);
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../index.html">
+                <img src="../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: DB.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::AutoSubmission::DB - Class::DBI based interface to the
+autosubmissions tracking database.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::AutoSubmission::DB::Experiment;
+ my $expt = ArrayExpress::AutoSubmission::DB::Experiment->retrieve( $id );
+
+=head1 DESCRIPTION
+
+This module is the abstract superclass for a set of Class::DBI - based
+modules, which are used as an object-relational mapping to the
+underlying submissions tracking database (currently implemented using
+MySQL). You should not use this class directly; rather, you should use
+the relevant table subclasses to query the database (see L</SYNOPSIS>
+for an example). Please refer to the Class::DBI documentation for
+information on query syntax.
+
+=head1 TABLES
+
+Included below are auto-generated descriptions of the MySQL
+tables. This listing was generated using the
+SQL::Translator::Producer::POD module.
+
+=head2 array_designs
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 miamexpress_subid
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 accession
+
+=over 4
+
+=item * varchar(255)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 name
+
+=over 4
+
+=item * varchar(255)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 miamexpress_login
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 status
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 data_warehouse_ready
+
+=over 4
+
+=item * char(15)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 date_last_processed
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 comment
+
+=over 4
+
+=item * text(65535)
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 miame_score
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 in_data_warehouse
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 annotation_source
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 annotation_version
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 biomart_table_name
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 release_date
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 is_released
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 UNIQUE
+
+=over 4
+
+=item * Fields = miamexpress_subid
+
+=back
+
+=head2 array_designs_experiments
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 array_design_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 experiment_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = array_design_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = experiment_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = array_design_id
+
+=item * Reference Table = L</array_designs>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = experiment_id
+
+=item * Reference Table = L</experiments>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 array_designs_organisms
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 organism_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 array_design_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = organism_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = array_design_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = organism_id
+
+=item * Reference Table = L</organisms>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = array_design_id
+
+=item * Reference Table = L</array_designs>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 categories
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 ontology_term
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 display_label
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 is_common
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 is_bmc
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 is_fv
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head2 categories_designs
+
+=head3 FIELDS
+
+=head4 category_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 design_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = category_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = design_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = category_id
+
+=item * Reference Table = L</categories>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = design_id
+
+=item * Reference Table = L</designs>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 categories_materials
+
+=head3 FIELDS
+
+=head4 category_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 material_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = category_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = material_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = category_id
+
+=item * Reference Table = L</categories>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = material_id
+
+=item * Reference Table = L</materials>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 categories_taxons
+
+=head3 FIELDS
+
+=head4 category_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 taxon_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = category_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = taxon_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = category_id
+
+=item * Reference Table = L</categories>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = taxon_id
+
+=item * Reference Table = L</taxons>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 data_files
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 experiment_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 name
+
+=over 4
+
+=item * varchar(255)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 is_unpacked
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = experiment_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = experiment_id
+
+=item * Reference Table = L</experiments>
+
+=item * Reference Fields = L</id>
+
+=back
+
+=head2 data_formats
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 name
+
+=over 4
+
+=item * varchar(50)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 UNIQUE
+
+=over 4
+
+=item * Fields = name
+
+=back
+
+=head2 design_instances
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 design_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 experiment_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = design_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = experiment_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = design_id
+
+=item * Reference Table = L</designs>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = experiment_id
+
+=item * Reference Table = L</experiments>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 designs
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 display_label
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 ontology_category
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 ontology_value
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 design_type
+
+=over 4
+
+=item * char(15)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head2 events
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 array_design_id
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 experiment_id
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 event_type
+
+=over 4
+
+=item * varchar(50)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 was_successful
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 source_db
+
+=over 4
+
+=item * varchar(30)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 target_db
+
+=over 4
+
+=item * varchar(30)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 start_time
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 end_time
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 machine
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 operator
+
+=over 4
+
+=item * varchar(30)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 log_file
+
+=over 4
+
+=item * varchar(511)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 jobregister_dbid
+
+=over 4
+
+=item * int(15)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 comment
+
+=over 4
+
+=item * text(65535)
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = array_design_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = experiment_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = array_design_id
+
+=item * Reference Table = L</array_designs>
+
+=item * Reference Fields = L</id>
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = experiment_id
+
+=item * Reference Table = L</experiments>
+
+=item * Reference Fields = L</id>
+
+=back
+
+=head2 experiments
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 accession
+
+=over 4
+
+=item * varchar(255)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 name
+
+=over 4
+
+=item * varchar(255)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 user_id
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 checker_score
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 software
+
+=over 4
+
+=item * varchar(100)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 status
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 data_warehouse_ready
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 date_last_edited
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 date_submitted
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 date_last_processed
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 in_curation
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 curator
+
+=over 4
+
+=item * char(30)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 comment
+
+=over 4
+
+=item * text(65535)
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 experiment_type
+
+=over 4
+
+=item * char(30)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 miamexpress_login
+
+=over 4
+
+=item * char(30)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 miamexpress_subid
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 is_affymetrix
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 is_mx_batchloader
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 miame_score
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 in_data_warehouse
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 num_submissions
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 submitter_description
+
+=over 4
+
+=item * text(65535)
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 curated_name
+
+=over 4
+
+=item * varchar(255)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 num_samples
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 num_hybridizations
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 has_raw_data
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 has_processed_data
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 release_date
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 is_released
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 ae_miame_score
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 ae_data_warehouse_score
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = user_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = user_id
+
+=item * Reference Table = L</users>
+
+=item * Reference Fields = L</id>
+
+=back
+
+=head2 experiments_factors
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 experiment_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 factor_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = experiment_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = factor_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = factor_id
+
+=item * Reference Table = L</factors>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = experiment_id
+
+=item * Reference Table = L</experiments>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 experiments_loaded_data
+
+=head3 FIELDS
+
+=head4 experiment_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 loaded_data_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = experiment_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = loaded_data_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = experiment_id
+
+=item * Reference Table = L</experiments>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = loaded_data_id
+
+=item * Reference Table = L</loaded_data>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 experiments_quantitation_types
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 quantitation_type_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 experiment_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = quantitation_type_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = experiment_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = quantitation_type_id
+
+=item * Reference Table = L</quantitation_types>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = experiment_id
+
+=item * Reference Table = L</experiments>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 factors
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 name
+
+=over 4
+
+=item * varchar(128)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head2 loaded_data
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 identifier
+
+=over 4
+
+=item * varchar(255)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 md5_hash
+
+=over 4
+
+=item * char(35)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 data_format_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 platform_id
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 needs_metrics_calculation
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 date_hashed
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = data_format_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = platform_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = platform_id
+
+=item * Reference Table = L</platforms>
+
+=item * Reference Fields = L</id>
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = data_format_id
+
+=item * Reference Table = L</data_formats>
+
+=item * Reference Fields = L</id>
+
+=back
+
+=head2 loaded_data_quality_metrics
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 value
+
+=over 4
+
+=item * decimal(12,5)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 quality_metric_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 loaded_data_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 date_calculated
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = quality_metric_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = loaded_data_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = loaded_data_id
+
+=item * Reference Table = L</loaded_data>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = quality_metric_id
+
+=item * Reference Table = L</quality_metrics>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 material_instances
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 material_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 experiment_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = material_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = experiment_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = material_id
+
+=item * Reference Table = L</materials>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = experiment_id
+
+=item * Reference Table = L</experiments>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 materials
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 display_label
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 ontology_category
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 ontology_value
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head2 meta
+
+=head3 FIELDS
+
+=head4 name
+
+=over 4
+
+=item * varchar(128)
+
+=item * PRIMARY KEY
+
+=item * Default '' 
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 value
+
+=over 4
+
+=item * varchar(128)
+
+=item * Default '' 
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = name
+
+=back
+
+=head2 organism_instances
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 organism_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 experiment_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = organism_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = experiment_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = organism_id
+
+=item * Reference Table = L</organisms>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = experiment_id
+
+=item * Reference Table = L</experiments>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 organisms
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 scientific_name
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 common_name
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 accession
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 taxon_id
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = taxon_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = taxon_id
+
+=item * Reference Table = L</taxons>
+
+=item * Reference Fields = L</id>
+
+=back
+
+=head2 permissions
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 name
+
+=over 4
+
+=item * varchar(40)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 info
+
+=over 4
+
+=item * varchar(80)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head2 permissions_roles
+
+=head3 FIELDS
+
+=head4 role_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 permission_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = role_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = permission_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = role_id
+
+=item * Reference Table = L</roles>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = permission_id
+
+=item * Reference Table = L</permissions>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 platforms
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 name
+
+=over 4
+
+=item * varchar(50)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 UNIQUE
+
+=over 4
+
+=item * Fields = name
+
+=back
+
+=head2 protocols
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 accession
+
+=over 4
+
+=item * char(15)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 user_accession
+
+=over 4
+
+=item * varchar(100)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 expt_accession
+
+=over 4
+
+=item * char(15)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 name
+
+=over 4
+
+=item * varchar(255)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 date_last_processed
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 comment
+
+=over 4
+
+=item * text(65535)
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 UNIQUE
+
+=over 4
+
+=item * Fields = accession
+
+=back
+
+=head2 quality_metrics
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 type
+
+=over 4
+
+=item * varchar(50)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 description
+
+=over 4
+
+=item * text(65535)
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 UNIQUE
+
+=over 4
+
+=item * Fields = type
+
+=back
+
+=head2 quantitation_types
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 name
+
+=over 4
+
+=item * varchar(128)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head2 roles
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 name
+
+=over 4
+
+=item * varchar(40)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 info
+
+=over 4
+
+=item * varchar(80)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head2 roles_users
+
+=head3 FIELDS
+
+=head4 user_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 role_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = user_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = role_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = user_id
+
+=item * Reference Table = L</users>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = role_id
+
+=item * Reference Table = L</roles>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 spreadsheets
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 experiment_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 name
+
+=over 4
+
+=item * varchar(255)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = experiment_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = experiment_id
+
+=item * Reference Table = L</experiments>
+
+=item * Reference Fields = L</id>
+
+=back
+
+=head2 taxons
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 scientific_name
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 common_name
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 accession
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head2 users
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 login
+
+=over 4
+
+=item * varchar(40)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 name
+
+=over 4
+
+=item * varchar(40)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 password
+
+=over 4
+
+=item * varchar(40)
+
+=item * Nullable 'No' 
+
+=back
+
+=head4 email
+
+=over 4
+
+=item * varchar(100)
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 modified_at
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 created_at
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 access
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL' 
+
+=item * Nullable 'Yes' 
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No' 
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 UNIQUE
+
+=over 4
+
+=item * Fields = login
+
+=back
+
+=head1 PRODUCED BY
+
+SQL::Translator::Producer::POD
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2004.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments. 
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Accessionable.pm b/lib/ArrayExpress/AutoSubmission/DB/Accessionable.pm
new file mode 100644
index 0000000..7682c7f
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Accessionable.pm
@@ -0,0 +1,56 @@
+#!/usr/bin/env perl
+#
+# $Id: Accessionable.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+####################################################################
+# Superclass for experiment, array, protocol which have accessions #
+####################################################################
+package ArrayExpress::AutoSubmission::DB::Accessionable;
+
+# Abstract package without corresponding DB table
+use base 'Class::Data::Inheritable';
+use List::Util qw(max);
+use Carp;
+
+__PACKAGE__->mk_classdata('accession_prefix');
+
+sub next_accession {    # Class method.
+    my $proto = shift;
+    my $class = ref $proto || $proto;
+
+    my $prefix = $class->accession_prefix()
+	or croak("Error: $class->accession_prefix() not set.");
+
+    # Only use the accessions matching the prefix pattern.
+    my @accessions;
+    my $iterator = $class->retrieve_all();
+    while (my $object = $iterator->next() ) {
+	my $acc = $object->accession();
+	push(@accessions, $acc) if ($acc && $acc =~ s/^$prefix//);
+    }
+
+    return ( $prefix . ( ( max(@accessions) || 0 ) + 1 ) );
+}
+
+sub get_accession {    # Instance method.
+    my ($self, $values) = @_;
+
+    # Set any additional accessors passed.
+    if ($values) {
+	ref $values eq 'HASH'
+	    or croak("Error: get_accession() needs a hash ref.");
+	$self->set( %{ $values } );
+    }
+
+    # Grab the accession, or generate a new one.
+    unless ( $self->accession() ) {
+        $self->set( accession => $self->next_accession() );
+    }
+    $self->update();
+    return ( $self->accession() );
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/ArrayDesign.pm b/lib/ArrayExpress/AutoSubmission/DB/ArrayDesign.pm
new file mode 100644
index 0000000..29c9f44
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/ArrayDesign.pm
@@ -0,0 +1,59 @@
+#!/usr/bin/env perl
+#
+# $Id: ArrayDesign.pm 1898 2008-01-18 20:13:25Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::ArrayDesign;
+use base 'ArrayExpress::AutoSubmission::DB';
+use base 'ArrayExpress::AutoSubmission::DB::Accessionable';
+__PACKAGE__->table('array_designs');
+__PACKAGE__->columns(
+    Primary => qw(
+        id
+	)
+);
+__PACKAGE__->columns(
+    Essential => qw(
+        miamexpress_subid
+        accession
+	name
+        status
+        date_last_processed
+        release_date
+        is_deleted
+        )
+);
+__PACKAGE__->columns(
+    Others => qw(
+        miamexpress_login
+	miame_score
+        data_warehouse_ready
+        in_data_warehouse
+        annotation_source
+        annotation_version
+        biomart_table_name
+        is_released
+        comment
+	)
+);
+__PACKAGE__->has_many(
+    organisms => [
+        'ArrayExpress::AutoSubmission::DB::ArrayDesignOrganism' => 'organism_id'
+    ]
+);
+__PACKAGE__->has_many(
+    organism_instances => 
+        'ArrayExpress::AutoSubmission::DB::ArrayDesignOrganism'
+);
+__PACKAGE__->has_many(
+    experiments => [
+        'ArrayExpress::AutoSubmission::DB::ArrayDesignExperiment' => 'experiment_id'
+    ]
+);
+__PACKAGE__->has_many(
+    events => 'ArrayExpress::AutoSubmission::DB::Event'
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/ArrayDesignExperiment.pm b/lib/ArrayExpress/AutoSubmission/DB/ArrayDesignExperiment.pm
new file mode 100644
index 0000000..eeb6d55
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/ArrayDesignExperiment.pm
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+#
+# $Id: ArrayDesignExperiment.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::ArrayDesignExperiment;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('array_designs_experiments');
+__PACKAGE__->columns(
+    All => qw(
+        id
+        experiment_id
+        array_design_id
+        )
+);
+__PACKAGE__->has_a(
+    experiment_id => 'ArrayExpress::AutoSubmission::DB::Experiment' );
+__PACKAGE__->has_a(
+    array_design_id => 'ArrayExpress::AutoSubmission::DB::ArrayDesign' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/ArrayDesignOrganism.pm b/lib/ArrayExpress/AutoSubmission/DB/ArrayDesignOrganism.pm
new file mode 100644
index 0000000..dbb1ab6
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/ArrayDesignOrganism.pm
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+#
+# $Id: ArrayDesignOrganism.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::ArrayDesignOrganism;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('array_designs_organisms');
+__PACKAGE__->columns(
+    All => qw(
+        id
+        organism_id
+        array_design_id
+        )
+);
+__PACKAGE__->has_a(
+    organism_id => 'ArrayExpress::AutoSubmission::DB::Organism' );
+__PACKAGE__->has_a(
+    array_design_id => 'ArrayExpress::AutoSubmission::DB::ArrayDesign' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Category.pm b/lib/ArrayExpress/AutoSubmission/DB/Category.pm
new file mode 100644
index 0000000..234067c
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Category.pm
@@ -0,0 +1,33 @@
+#!/usr/bin/env perl
+#
+# $Id: Category.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::Category;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('categories');
+__PACKAGE__->columns(
+    All => qw(
+        id
+        ontology_term
+        display_label
+	is_common
+	is_bmc
+	is_fv
+	is_deleted
+        )
+);
+__PACKAGE__->has_many( taxons =>
+        [ 'ArrayExpress::AutoSubmission::DB::CategoryTaxon' => 'taxon_id' ] );
+__PACKAGE__->has_many( designs =>
+        [ 'ArrayExpress::AutoSubmission::DB::CategoryDesign' => 'design_id' ]
+);
+__PACKAGE__->has_many(
+    materials => [
+        'ArrayExpress::AutoSubmission::DB::CategoryMaterial' => 'material_id'
+    ]
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/CategoryDesign.pm b/lib/ArrayExpress/AutoSubmission/DB/CategoryDesign.pm
new file mode 100644
index 0000000..d25bb40
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/CategoryDesign.pm
@@ -0,0 +1,21 @@
+#!/usr/bin/env perl
+#
+# $Id: CategoryDesign.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::CategoryDesign;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('categories_designs');
+__PACKAGE__->columns(
+    Primary => qw(
+        category_id
+        design_id
+        )
+);
+__PACKAGE__->has_a(
+    category_id => 'ArrayExpress::AutoSubmission::DB::Category' );
+__PACKAGE__->has_a( design_id => 'ArrayExpress::AutoSubmission::DB::Design' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/CategoryMaterial.pm b/lib/ArrayExpress/AutoSubmission/DB/CategoryMaterial.pm
new file mode 100644
index 0000000..66e3c78
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/CategoryMaterial.pm
@@ -0,0 +1,22 @@
+#!/usr/bin/env perl
+#
+# $Id: CategoryMaterial.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::CategoryMaterial;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('categories_materials');
+__PACKAGE__->columns(
+    Primary => qw(
+        category_id
+        material_id
+        )
+);
+__PACKAGE__->has_a(
+    category_id => 'ArrayExpress::AutoSubmission::DB::Category' );
+__PACKAGE__->has_a(
+    material_id => 'ArrayExpress::AutoSubmission::DB::Material' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/CategoryTaxon.pm b/lib/ArrayExpress/AutoSubmission/DB/CategoryTaxon.pm
new file mode 100644
index 0000000..8755bd0
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/CategoryTaxon.pm
@@ -0,0 +1,21 @@
+#!/usr/bin/env perl
+#
+# $Id: CategoryTaxon.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::CategoryTaxon;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('categories_taxons');
+__PACKAGE__->columns(
+    Primary => qw(
+        category_id
+        taxon_id
+        )
+);
+__PACKAGE__->has_a(
+    category_id => 'ArrayExpress::AutoSubmission::DB::Category' );
+__PACKAGE__->has_a( taxon_id => 'ArrayExpress::AutoSubmission::DB::Taxon' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/DataFile.pm b/lib/ArrayExpress/AutoSubmission/DB/DataFile.pm
new file mode 100644
index 0000000..4449d69
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/DataFile.pm
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+#
+# $Id: DataFile.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::DataFile;
+use base 'ArrayExpress::AutoSubmission::DB';
+use base 'ArrayExpress::AutoSubmission::DB::File';
+__PACKAGE__->table('data_files');
+__PACKAGE__->columns(
+    All => qw(
+      id
+      experiment_id
+      name
+      is_unpacked
+      is_deleted
+      )
+);
+__PACKAGE__->has_a( experiment_id => 'ArrayExpress::AutoSubmission::DB::Experiment' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/DataFormat.pm b/lib/ArrayExpress/AutoSubmission/DB/DataFormat.pm
new file mode 100644
index 0000000..cd1c3d3
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/DataFormat.pm
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+#
+# $Id: DataFormat.pm 1936 2008-02-08 19:13:37Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::DataFormat;
+use base 'ArrayExpress::AutoSubmission::DB';
+
+__PACKAGE__->table('data_formats');
+__PACKAGE__->columns(
+    All => qw(
+        id
+	name
+        )
+);
+
+__PACKAGE__->has_many(
+    loaded_data => 'ArrayExpress::AutoSubmission::DB::LoadedData'
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Design.pm b/lib/ArrayExpress/AutoSubmission/DB/Design.pm
new file mode 100644
index 0000000..e24e40c
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Design.pm
@@ -0,0 +1,32 @@
+#!/usr/bin/env perl
+#
+# $Id: Design.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::Design;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('designs');
+__PACKAGE__->columns(
+    All => qw(
+        id
+        display_label
+        ontology_category
+        ontology_value
+	design_type
+        is_deleted
+        )
+);
+__PACKAGE__->has_many(
+    categories => [
+        'ArrayExpress::AutoSubmission::DB::CategoryDesign' => 'category_id'
+    ]
+);
+__PACKAGE__->has_many(
+    experiments => [
+        'ArrayExpress::AutoSubmission::DB::DesignInstance' => 'experiment_id'
+    ]
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/DesignInstance.pm b/lib/ArrayExpress/AutoSubmission/DB/DesignInstance.pm
new file mode 100644
index 0000000..7a1bcb1
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/DesignInstance.pm
@@ -0,0 +1,22 @@
+#!/usr/bin/env perl
+#
+# $Id: DesignInstance.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::DesignInstance;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('design_instances');
+__PACKAGE__->columns(
+    All => qw(
+        id
+        design_id
+        experiment_id
+        )
+);
+__PACKAGE__->has_a( design_id => 'ArrayExpress::AutoSubmission::DB::Design' );
+__PACKAGE__->has_a(
+    experiment_id => 'ArrayExpress::AutoSubmission::DB::Experiment' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Event.pm b/lib/ArrayExpress/AutoSubmission/DB/Event.pm
new file mode 100644
index 0000000..5477b79
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Event.pm
@@ -0,0 +1,49 @@
+#!/usr/bin/env perl
+#
+# $Id: Event.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::Event;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('events');
+__PACKAGE__->columns(
+    Primary => qw(
+        id
+	)
+);
+__PACKAGE__->columns(
+    Essential => qw(
+        array_design_id
+        experiment_id
+        event_type
+        jobregister_dbid
+        is_deleted
+        )
+);
+__PACKAGE__->columns(
+    Others => qw(
+        was_successful
+        source_db
+        target_db
+        start_time
+        end_time
+        machine
+        operator
+        log_file
+        comment
+	)
+);
+__PACKAGE__->has_a(
+    array_design_id => 'ArrayExpress::AutoSubmission::DB::ArrayDesign',
+);
+__PACKAGE__->has_a(
+    experiment_id => 'ArrayExpress::AutoSubmission::DB::Experiment',
+);
+
+__PACKAGE__->set_sql(
+    last_jobid => "SELECT MAX(jobregister_dbid) FROM __TABLE__ WHERE %s = ?",
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Experiment.pm b/lib/ArrayExpress/AutoSubmission/DB/Experiment.pm
new file mode 100644
index 0000000..97b8f8e
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Experiment.pm
@@ -0,0 +1,324 @@
+#!/usr/bin/env perl
+#
+# $Id: Experiment.pm 2012 2008-03-30 17:27:46Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::Experiment;
+use base 'ArrayExpress::AutoSubmission::DB';
+use base 'ArrayExpress::AutoSubmission::DB::Accessionable';
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(untaint);
+use File::Spec;
+use File::Path;
+use File::Basename;
+use Carp;
+
+__PACKAGE__->table('experiments');
+__PACKAGE__->columns(
+    Primary => qw(
+        id
+        )
+);
+
+# These are columns used in the main submissions tracking system, so
+# are generally essential.
+__PACKAGE__->columns(
+    Essential => qw(
+        accession
+        name
+        user_id
+	date_last_edited
+	date_submitted
+        date_last_processed
+	in_curation
+        status
+        experiment_type
+	is_affymetrix
+        miamexpress_subid
+        release_date
+        is_released
+        curated_name
+        is_deleted
+	)
+);
+
+# These are less-used columns which may be retrieved for more extended
+# tracking purposes.
+__PACKAGE__->columns(
+    Others => qw(
+        checker_score
+	miame_score
+        software
+        data_warehouse_ready
+        in_data_warehouse
+	num_submissions
+        curator
+        miamexpress_login
+	is_mx_batchloader
+        submitter_description
+        num_samples
+        num_hybridizations
+	has_raw_data
+	has_processed_data
+        ae_miame_score
+        ae_data_warehouse_score
+        comment
+        )
+);
+__PACKAGE__->has_a( user_id => 'ArrayExpress::AutoSubmission::DB::User' );
+__PACKAGE__->has_many(
+    array_designs => [
+        'ArrayExpress::AutoSubmission::DB::ArrayDesignExperiment' => 'array_design_id'
+    ]
+);
+__PACKAGE__->has_many(
+    array_design_instances => 
+        'ArrayExpress::AutoSubmission::DB::ArrayDesignExperiment'
+);
+__PACKAGE__->has_many(
+    designs => [
+        'ArrayExpress::AutoSubmission::DB::DesignInstance' => 'design_id'
+    ]
+);
+__PACKAGE__->has_many(
+    materials => [
+        'ArrayExpress::AutoSubmission::DB::MaterialInstance' => 'material_id'
+    ]
+);
+__PACKAGE__->has_many(
+    organisms => [
+        'ArrayExpress::AutoSubmission::DB::OrganismInstance' => 'organism_id'
+    ]
+);
+__PACKAGE__->has_many(
+    design_instances => 
+        'ArrayExpress::AutoSubmission::DB::DesignInstance'
+);
+__PACKAGE__->has_many(
+    material_instances => 
+        'ArrayExpress::AutoSubmission::DB::MaterialInstance'
+);
+__PACKAGE__->has_many(
+    organism_instances => 
+        'ArrayExpress::AutoSubmission::DB::OrganismInstance'
+);
+__PACKAGE__->has_many(
+    data_files => 
+        'ArrayExpress::AutoSubmission::DB::DataFile',
+    # Order by ID so that more recent uploads are unpacked later.
+    { order_by => 'id' }
+);
+__PACKAGE__->has_many(
+    spreadsheets => 
+        'ArrayExpress::AutoSubmission::DB::Spreadsheet'
+);
+__PACKAGE__->has_many(
+    factors => [
+        'ArrayExpress::AutoSubmission::DB::ExperimentFactor' => 'factor_id'
+    ]
+);
+__PACKAGE__->has_many(
+    factor_instances => 
+        'ArrayExpress::AutoSubmission::DB::ExperimentFactor'
+);
+__PACKAGE__->has_many(
+    quantitation_types => [
+        'ArrayExpress::AutoSubmission::DB::ExperimentQT' => 'quantitation_type_id'
+    ]
+);
+__PACKAGE__->has_many(
+    quantitation_type_instances => 
+        'ArrayExpress::AutoSubmission::DB::ExperimentQT'
+);
+__PACKAGE__->has_many(
+    events => 'ArrayExpress::AutoSubmission::DB::Event'
+);
+
+__PACKAGE__->has_many(
+    loaded_data => [
+        'ArrayExpress::AutoSubmission::DB::ExperimentLoadedData' => 'loaded_data_id'
+    ]
+);
+__PACKAGE__->has_many(
+    loaded_data_instances => 'ArrayExpress::AutoSubmission::DB::ExperimentLoadedData'
+);
+
+__PACKAGE__->set_sql(
+    status_for_update => "SELECT status FROM __TABLE__ WHERE id = ? FOR UPDATE",
+);
+
+sub update_designs {
+
+    my ($self, $new_assns) = @_;
+
+    my @old_assn_instances = $self->design_instances();
+
+    my %new = map { $_->id()        => 1 } @$new_assns;
+    my %old = map { $_->design_id() => 1 } @old_assn_instances;
+
+    foreach my $assn (@old_assn_instances) {
+	unless ($new{$assn->design_id()}) {
+	    $assn->delete();
+	}
+    }
+    foreach my $assn (@$new_assns) {
+	unless ($old{$assn->id()}) {
+	    $self->add_to_design_instances({
+		design_id => $assn,
+	    });
+	}
+    }
+    $self->update;
+
+    return;
+}
+
+sub update_materials {
+
+    my ($self, $new_assns) = @_;
+
+    my @old_assn_instances = $self->material_instances();
+
+    my %new = map { $_->id()          => 1 } @$new_assns;
+    my %old = map { $_->material_id() => 1 } @old_assn_instances;
+
+    foreach my $assn (@old_assn_instances) {
+	unless ($new{$assn->material_id()}) {
+	    $assn->delete();
+	}
+    }
+    foreach my $assn (@$new_assns) {
+	unless ($old{$assn->id()}) {
+	    $self->add_to_material_instances({
+		material_id => $assn,
+	    });
+	}
+    }
+    $self->update;
+
+    return;
+}
+
+sub update_organisms {
+
+    my ($self, $new_assns) = @_;
+
+    my @old_assn_instances = $self->organism_instances();
+
+    my %new = map { $_->id()          => 1 } @$new_assns;
+    my %old = map { $_->organism_id() => 1 } @old_assn_instances;
+
+    foreach my $assn (@old_assn_instances) {
+	unless ($new{$assn->organism_id()}) {
+	    $assn->delete();
+	}
+    }
+    foreach my $assn (@$new_assns) {
+	unless ($old{$assn->id()}) {
+	    $self->add_to_organism_instances({
+		organism_id => $assn,
+	    });
+	}
+    }
+    $self->update;
+
+    return;
+}
+
+sub filesystem_directory {
+    
+    # Returns the top-level submissions directory containing
+    # spreadsheets and data file archives, having first created it.
+    my $self = shift;
+
+    # Check we have a valid experiment (MIAMExpress submissions are
+    # handled separately, in MIAMExpress.pm).
+    unless (    $self->user_id()
+	     && $self->user_id()->login()
+	     && $self->experiment_type()
+	     && $self->id() ) {
+	croak("Error: Invalid Experiment object for filesystem directory creation.");
+    }
+    my $dir = File::Spec->catdir(
+	$CONFIG->get_AUTOSUBMISSIONS_FILEBASE(),
+	untaint($self->user_id()->login()),
+	untaint($self->experiment_type()) . q{_} . untaint($self->id()),
+    );
+
+    # Check for pre-existence because otherwise we can be stuck trying
+    # to change permissions on a pre-existing directory which may not
+    # belong to us.
+    unless ( -e $dir ) {
+
+	# Attempt to create submissions directory.
+        eval {
+
+	    # Handle the umask.
+	    my $original_umask = umask;
+	    umask(0);
+
+	    # Needed because Webform.pm changes this to parse the
+	    # filename; we however need this to be based on the server
+	    # OS. File::Path::mkpath() uses File::Basename.
+	    my $old_fstype = fileparse_set_fstype($^O);
+
+	    # Do the deed.
+	    mkpath($dir, undef, oct(777));
+	    chmod(oct($CONFIG->get_DIR_PERMISSIONS()), $dir)
+		or croak("Error changing permissions on $dir: $!");
+
+	    # Reset the original settings.
+	    fileparse_set_fstype($old_fstype) if $old_fstype;
+	    umask($original_umask);
+	};
+
+	# If it failed, check whether we can at least rwx the dir.
+	if ($@) {
+	    unless ( -d $dir && -r _ && -w _ && -x _ ) {
+		die("Error creating submissions directory.\n");
+	    }
+	}
+    }
+
+    return $dir;
+}
+
+sub unpack_directory {
+
+    # Returns the directory into which the data archives will be
+    # unpacked, having first created it.
+    my $self = shift;
+
+    my $dir = File::Spec->catdir(
+	$self->filesystem_directory(), 'unpacked'
+    );
+
+    # Check for pre-existence because otherwise we can be stuck trying
+    # to change permissions on a pre-existing directory which may not
+    # belong to us.
+    unless ( -e $dir ) {
+
+	# Attempt to create unpacking directory.
+        eval {
+	    my $original_umask = umask;
+	    umask(0);
+	    mkpath($dir, undef, oct(777));
+	    chmod(oct($CONFIG->get_DIR_PERMISSIONS()), $dir)
+		or croak("Error changing permissions on $dir: $!");
+	    umask($original_umask);
+	};
+
+	# If it failed, check whether we can at least rwx the dir.
+	if ($@) {
+	    unless ( -d $dir && -r _ && -w _ && -x _ ) {
+		die("Error creating submissions directory.\n");
+	    }
+	}
+    }
+
+    return $dir;
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/ExperimentFactor.pm b/lib/ArrayExpress/AutoSubmission/DB/ExperimentFactor.pm
new file mode 100644
index 0000000..62f65aa
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/ExperimentFactor.pm
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+#
+# $Id: ExperimentFactor.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::ExperimentFactor;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('experiments_factors');
+__PACKAGE__->columns(
+    All => qw(
+        id
+        factor_id
+        experiment_id
+        )
+);
+__PACKAGE__->has_a(
+    experiment_id => 'ArrayExpress::AutoSubmission::DB::Experiment' );
+__PACKAGE__->has_a(
+    factor_id => 'ArrayExpress::AutoSubmission::DB::Factor' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/ExperimentLoadedData.pm b/lib/ArrayExpress/AutoSubmission/DB/ExperimentLoadedData.pm
new file mode 100644
index 0000000..06c6d3d
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/ExperimentLoadedData.pm
@@ -0,0 +1,20 @@
+#!/usr/bin/env perl
+#
+# $Id: ExperimentLoadedData.pm 1936 2008-02-08 19:13:37Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::ExperimentLoadedData;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('experiments_loaded_data');
+__PACKAGE__->columns(
+    Primary => qw(
+        loaded_data_id
+        experiment_id
+        )
+);
+__PACKAGE__->has_a( loaded_data_id => 'ArrayExpress::AutoSubmission::DB::LoadedData' );
+__PACKAGE__->has_a( experiment_id  => 'ArrayExpress::AutoSubmission::DB::Experiment' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/ExperimentQT.pm b/lib/ArrayExpress/AutoSubmission/DB/ExperimentQT.pm
new file mode 100644
index 0000000..a1237ae
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/ExperimentQT.pm
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+#
+# $Id: ExperimentQT.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::ExperimentQT;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('experiments_quantitation_types');
+__PACKAGE__->columns(
+    All => qw(
+        id
+        quantitation_type_id
+        experiment_id
+        )
+);
+__PACKAGE__->has_a(
+    experiment_id => 'ArrayExpress::AutoSubmission::DB::Experiment' );
+__PACKAGE__->has_a(
+    quantitation_type_id => 'ArrayExpress::AutoSubmission::DB::QuantitationType' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Factor.pm b/lib/ArrayExpress/AutoSubmission/DB/Factor.pm
new file mode 100644
index 0000000..417a232
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Factor.pm
@@ -0,0 +1,24 @@
+#!/usr/bin/env perl
+#
+# $Id: Factor.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::Factor;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('factors');
+__PACKAGE__->columns(
+    All => qw(
+        id
+	name
+        is_deleted
+        )
+);
+__PACKAGE__->has_many(
+    experiments => [
+        'ArrayExpress::AutoSubmission::DB::ExperimentFactor' => 'experiment_id'
+    ]
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/File.pm b/lib/ArrayExpress/AutoSubmission/DB/File.pm
new file mode 100644
index 0000000..0fcdf2c
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/File.pm
@@ -0,0 +1,35 @@
+#!/usr/bin/env perl
+#
+# $Id: File.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::File;
+
+# Abstract package without corresponding DB table
+use File::Spec;
+use Carp;
+
+sub filesystem_path {
+    my $self = shift;
+
+    my $experiment;
+    unless ( $experiment = $self->experiment_id() ) {
+	croak(
+	    sprintf(
+		"Error: Orphaned file has no experiment: %s",
+		$self->name()
+	    )
+	);
+    }
+
+    my $path = File::Spec->catfile(
+	$experiment->filesystem_directory(),
+	$self->name()
+    );
+
+    return $path;
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/LoadedData.pm b/lib/ArrayExpress/AutoSubmission/DB/LoadedData.pm
new file mode 100644
index 0000000..fdeea69
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/LoadedData.pm
@@ -0,0 +1,44 @@
+#!/usr/bin/env perl
+#
+# $Id: LoadedData.pm 1960 2008-02-21 12:03:19Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::LoadedData;
+use base 'ArrayExpress::AutoSubmission::DB';
+
+__PACKAGE__->table('loaded_data');
+__PACKAGE__->columns(
+    All => qw(
+        id
+	identifier
+	md5_hash
+	data_format_id
+	platform_id
+	needs_metrics_calculation
+	date_hashed
+	is_deleted
+        )
+);
+
+__PACKAGE__->has_a( data_format_id => 'ArrayExpress::AutoSubmission::DB::DataFormat' );
+__PACKAGE__->has_a( platform_id    => 'ArrayExpress::AutoSubmission::DB::Platform' );
+__PACKAGE__->has_many(
+    experiments => [
+        'ArrayExpress::AutoSubmission::DB::ExperimentLoadedData' => 'experiment_id'
+    ]
+);
+__PACKAGE__->has_many(
+    loaded_data_instances => 'ArrayExpress::AutoSubmission::DB::ExperimentLoadedData'
+);
+__PACKAGE__->has_many(
+    quality_metrics => [
+        'ArrayExpress::AutoSubmission::DB::QualityMetricInstance' => 'quality_metric_id'
+    ]
+);
+__PACKAGE__->has_many(
+    quality_metric_instances => 'ArrayExpress::AutoSubmission::DB::QualityMetricInstance'
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Material.pm b/lib/ArrayExpress/AutoSubmission/DB/Material.pm
new file mode 100644
index 0000000..2a6ea5e
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Material.pm
@@ -0,0 +1,32 @@
+#!/usr/bin/env perl
+#
+# $Id: Material.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::Material;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('materials');
+__PACKAGE__->columns(
+    All => qw(
+        id
+        display_label
+        ontology_category
+        ontology_value
+        is_deleted
+        )
+);
+__PACKAGE__->has_many(
+    categories => [
+        'ArrayExpress::AutoSubmission::DB::CategoryMaterial' => 'category_id'
+    ]
+);
+__PACKAGE__->has_many(
+    experiments => [
+        'ArrayExpress::AutoSubmission::DB::MaterialInstance' =>
+            'experiment_id'
+    ]
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/MaterialInstance.pm b/lib/ArrayExpress/AutoSubmission/DB/MaterialInstance.pm
new file mode 100644
index 0000000..1ec17ee
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/MaterialInstance.pm
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+#
+# $Id: MaterialInstance.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::MaterialInstance;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('material_instances');
+__PACKAGE__->columns(
+    All => qw(
+        id
+        material_id
+        experiment_id
+        )
+);
+__PACKAGE__->has_a(
+    material_id => 'ArrayExpress::AutoSubmission::DB::Material' );
+__PACKAGE__->has_a(
+    experiment_id => 'ArrayExpress::AutoSubmission::DB::Experiment' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Organism.pm b/lib/ArrayExpress/AutoSubmission/DB/Organism.pm
new file mode 100644
index 0000000..b9dc226
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Organism.pm
@@ -0,0 +1,34 @@
+#!/usr/bin/env perl
+#
+# $Id: Organism.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::Organism;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('organisms');
+__PACKAGE__->columns(
+    All => qw(
+        id
+        scientific_name
+        common_name
+        accession
+        taxon_id
+        is_deleted
+        )
+);
+__PACKAGE__->has_a( taxon_id => 'ArrayExpress::AutoSubmission::DB::Taxon' );
+__PACKAGE__->has_many(
+    experiments => [
+        'ArrayExpress::AutoSubmission::DB::OrganismInstance' =>
+            'experiment_id'
+    ]
+);
+__PACKAGE__->has_many(
+    array_designs => [
+        'ArrayExpress::AutoSubmission::DB::ArrayDesignOrganism' => 'array_design_id'
+    ]
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/OrganismInstance.pm b/lib/ArrayExpress/AutoSubmission/DB/OrganismInstance.pm
new file mode 100644
index 0000000..5805efe
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/OrganismInstance.pm
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+#
+# $Id: OrganismInstance.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::OrganismInstance;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('organism_instances');
+__PACKAGE__->columns(
+    All => qw(
+        id
+        organism_id
+        experiment_id
+        )
+);
+__PACKAGE__->has_a(
+    organism_id => 'ArrayExpress::AutoSubmission::DB::Organism' );
+__PACKAGE__->has_a(
+    experiment_id => 'ArrayExpress::AutoSubmission::DB::Experiment' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Permission.pm b/lib/ArrayExpress/AutoSubmission/DB/Permission.pm
new file mode 100644
index 0000000..8de9e55
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Permission.pm
@@ -0,0 +1,22 @@
+#!/usr/bin/env perl
+#
+# $Id: Permission.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::Permission;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('permissions');
+__PACKAGE__->columns(
+    All => qw(
+        id
+        name
+        info
+        is_deleted
+        )
+);
+__PACKAGE__->has_many( roles =>
+        [ 'ArrayExpress::AutoSubmission::DB::PermissionRole' => 'role_id' ] );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/PermissionRole.pm b/lib/ArrayExpress/AutoSubmission/DB/PermissionRole.pm
new file mode 100644
index 0000000..21e69c5
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/PermissionRole.pm
@@ -0,0 +1,21 @@
+#!/usr/bin/env perl
+#
+# $Id: PermissionRole.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::PermissionRole;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('permissions_roles');
+__PACKAGE__->columns(
+    Primary => qw(
+        role_id
+        permission_id
+        )
+);
+__PACKAGE__->has_a( role_id => 'ArrayExpress::AutoSubmission::DB::Role' );
+__PACKAGE__->has_a(
+    permission_id => 'ArrayExpress::AutoSubmission::DB::Permission' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Platform.pm b/lib/ArrayExpress/AutoSubmission/DB/Platform.pm
new file mode 100644
index 0000000..734f432
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Platform.pm
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+#
+# $Id: Platform.pm 1960 2008-02-21 12:03:19Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::Platform;
+use base 'ArrayExpress::AutoSubmission::DB';
+
+__PACKAGE__->table('platforms');
+__PACKAGE__->columns(
+    All => qw(
+        id
+	name
+        )
+);
+
+__PACKAGE__->has_many(
+    loaded_data => 'ArrayExpress::AutoSubmission::DB::LoadedData'
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Protocol.pm b/lib/ArrayExpress/AutoSubmission/DB/Protocol.pm
new file mode 100644
index 0000000..6313481
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Protocol.pm
@@ -0,0 +1,115 @@
+#!/usr/bin/env perl
+#
+# $Id: Protocol.pm 2024 2008-04-14 10:23:23Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::Protocol;
+use base 'ArrayExpress::AutoSubmission::DB';
+use base 'ArrayExpress::AutoSubmission::DB::Accessionable';
+
+use ArrayExpress::Curator::Common qw(date_now);
+
+__PACKAGE__->table('protocols');
+__PACKAGE__->columns(
+    All => qw(
+        id
+        accession
+        user_accession
+        expt_accession
+        name
+        date_last_processed
+        comment
+        is_deleted
+        )
+);
+
+sub reassign_protocol {    # Class method.
+
+    my ( $class, $user_accession, $expt_accession, $protocol_name ) = @_;
+
+    # This is non-essential, but ends up in the database.
+    $protocol_name ||= q{Unknown};
+
+    # Search for (user_accession eq accession), then connects to AE if
+    # $CONFIG allows it to check for (preloaded accession eq
+    # user_accession). Finally checks for (user_accession eq
+    # user_accession and expt_accession eq expt_accession). Creates a
+    # new protocol in db and assigns accession if not found.
+    my $prot_accession;
+
+    # First look for simple reuse of DB accessions
+    my $db_protocol
+	= ArrayExpress::AutoSubmission::DB::Protocol->retrieve(
+	    accession  => $user_accession,
+	    is_deleted => 0,
+	);
+
+    if ( $db_protocol ) {
+	$prot_accession = $db_protocol->accession();
+    }
+    else {
+
+	# Otherwise, check that ArrayExpress doesn't already have the
+	# protocol, if possible.
+	require ArrayExpress::Curator::Database;
+	ArrayExpress::Curator::Database->import('get_ae_dbh');
+
+	if ( my $dbh = get_ae_dbh() ) {
+
+	    print STDOUT (
+		"Querying ArrayExpress for protocol accession $user_accession\n"
+	    );
+	    my $sth = $dbh->prepare(<<'QUERY');
+select distinct i.identifier
+from tt_identifiable i, tt_protocol p
+where i.id=p.id and i.identifier=?
+QUERY
+		
+	    # Query on protocol identifier, return the user
+	    # accession if it's found.
+	    $sth->execute( $user_accession )
+		or croak($sth->errstr() . " $DBI::errstr");
+	    my $arrayref = $sth->fetchall_arrayref;
+	    if ( scalar @$arrayref ) {
+		print STDOUT (
+		    "Using preexisting ArrayExpress protocol accession $user_accession\n"
+		);
+		$prot_accession = $user_accession;
+	    }
+	}
+
+	# If no AE accession, check user and expt accessions,
+	# creating a new protocol if necessary.
+	unless ($prot_accession) {
+	    unless ($db_protocol) {
+		$db_protocol
+		    = ArrayExpress::AutoSubmission::DB::Protocol->find_or_create(
+			user_accession => $user_accession,
+			expt_accession => $expt_accession,
+			is_deleted     => 0,
+		    );
+		unless ($db_protocol->name()) {
+		    $db_protocol->set(
+			name => $protocol_name,
+		    );
+		}
+	    }
+	    $prot_accession = $db_protocol->get_accession();
+	}
+    }
+
+    # If we're using a protocol from the autosubs system, set the date
+    # to record its usage.
+    if ( $db_protocol ) {
+	$db_protocol->set(
+	    date_last_processed => date_now(),
+	);
+	$db_protocol->update();
+    }
+
+    return $prot_accession;
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/QualityMetric.pm b/lib/ArrayExpress/AutoSubmission/DB/QualityMetric.pm
new file mode 100644
index 0000000..83b2c70
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/QualityMetric.pm
@@ -0,0 +1,29 @@
+#!/usr/bin/env perl
+#
+# $Id: QualityMetric.pm 1960 2008-02-21 12:03:19Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::QualityMetric;
+use base 'ArrayExpress::AutoSubmission::DB';
+
+__PACKAGE__->table('quality_metrics');
+__PACKAGE__->columns(
+    All => qw(
+        id
+	type
+        description
+        )
+);
+
+__PACKAGE__->has_many(
+    loaded_data => [
+        'ArrayExpress::AutoSubmission::DB::QualityMetricInstance' => 'loaded_data_id'
+    ]
+);
+__PACKAGE__->has_many(
+    quality_metric_instances => 'ArrayExpress::AutoSubmission::DB::QualityMetricInstance'
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/QualityMetricInstance.pm b/lib/ArrayExpress/AutoSubmission/DB/QualityMetricInstance.pm
new file mode 100644
index 0000000..57037ba
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/QualityMetricInstance.pm
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+#
+# $Id: QualityMetricInstance.pm 1960 2008-02-21 12:03:19Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::QualityMetricInstance;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('loaded_data_quality_metrics');
+__PACKAGE__->columns(
+    All => qw(
+	id
+	value
+	date_calculated
+        loaded_data_id
+        quality_metric_id
+        )
+);
+__PACKAGE__->has_a( loaded_data_id    => 'ArrayExpress::AutoSubmission::DB::LoadedData' );
+__PACKAGE__->has_a( quality_metric_id => 'ArrayExpress::AutoSubmission::DB::QualityMetric' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/QuantitationType.pm b/lib/ArrayExpress/AutoSubmission/DB/QuantitationType.pm
new file mode 100644
index 0000000..596e5fa
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/QuantitationType.pm
@@ -0,0 +1,24 @@
+#!/usr/bin/env perl
+#
+# $Id: QuantitationType.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::QuantitationType;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('quantitation_types');
+__PACKAGE__->columns(
+    All => qw(
+        id
+	name
+        is_deleted
+        )
+);
+__PACKAGE__->has_many(
+    experiments => [
+        'ArrayExpress::AutoSubmission::DB::ExperimentQT' => 'experiment_id'
+    ]
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Role.pm b/lib/ArrayExpress/AutoSubmission/DB/Role.pm
new file mode 100644
index 0000000..baab88a
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Role.pm
@@ -0,0 +1,27 @@
+#!/usr/bin/env perl
+#
+# $Id: Role.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::Role;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('roles');
+__PACKAGE__->columns(
+    All => qw(
+        id
+        name
+        info
+        is_deleted
+        )
+);
+__PACKAGE__->has_many(
+    users => [ 'ArrayExpress::AutoSubmission::DB::RoleUser' => 'user_id' ] );
+__PACKAGE__->has_many(
+    permissions => [
+        'ArrayExpress::AutoSubmission::DB::PermissionRole' => 'permission_id'
+    ]
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/RoleUser.pm b/lib/ArrayExpress/AutoSubmission/DB/RoleUser.pm
new file mode 100644
index 0000000..82a6299
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/RoleUser.pm
@@ -0,0 +1,20 @@
+#!/usr/bin/env perl
+#
+# $Id: RoleUser.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::RoleUser;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('roles_users');
+__PACKAGE__->columns(
+    Primary => qw(
+        user_id
+        role_id
+        )
+);
+__PACKAGE__->has_a( role_id => 'ArrayExpress::AutoSubmission::DB::Role' );
+__PACKAGE__->has_a( user_id => 'ArrayExpress::AutoSubmission::DB::User' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Spreadsheet.pm b/lib/ArrayExpress/AutoSubmission/DB/Spreadsheet.pm
new file mode 100644
index 0000000..d793711
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Spreadsheet.pm
@@ -0,0 +1,22 @@
+#!/usr/bin/env perl
+#
+# $Id: Spreadsheet.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::Spreadsheet;
+use base 'ArrayExpress::AutoSubmission::DB';
+use base 'ArrayExpress::AutoSubmission::DB::File';
+__PACKAGE__->table('spreadsheets');
+__PACKAGE__->columns(
+    All => qw(
+      id
+      experiment_id
+      name
+      is_deleted
+      )
+);
+__PACKAGE__->has_a( experiment_id => 'ArrayExpress::AutoSubmission::DB::Experiment' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Taxon.pm b/lib/ArrayExpress/AutoSubmission/DB/Taxon.pm
new file mode 100644
index 0000000..7cb884d
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Taxon.pm
@@ -0,0 +1,28 @@
+#!/usr/bin/env perl
+#
+# $Id: Taxon.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::Taxon;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('taxons');
+__PACKAGE__->columns(
+    All => qw(
+        id
+        scientific_name
+        common_name
+        accession
+        is_deleted
+        )
+);
+__PACKAGE__->has_many(
+    organisms => 'ArrayExpress::AutoSubmission::DB::Organism' );
+__PACKAGE__->has_many(
+    categories => [
+        'ArrayExpress::AutoSubmission::DB::CategoryTaxon' => 'category_id'
+    ]
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/User.pm b/lib/ArrayExpress/AutoSubmission/DB/User.pm
new file mode 100644
index 0000000..980b054
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/User.pm
@@ -0,0 +1,29 @@
+#!/usr/bin/env perl
+#
+# $Id: User.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::User;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('users');
+__PACKAGE__->columns(
+    All => qw(
+        id
+        login
+        name
+        password
+	email
+        modified_at
+        created_at
+        access
+        is_deleted
+        )
+);
+__PACKAGE__->has_many(
+    experiments => 'ArrayExpress::AutoSubmission::DB::Experiment' );
+__PACKAGE__->has_many(
+    roles => [ 'ArrayExpress::AutoSubmission::DB::RoleUser' => 'role_id' ] );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/Daemon.pm b/lib/ArrayExpress/AutoSubmission/Daemon.pm
new file mode 100644
index 0000000..48963a7
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/Daemon.pm
@@ -0,0 +1,435 @@
+#!/usr/bin/env perl
+#
+# Module used to construct checker and exporter daemon objects
+#
+# Tim Rayner 2006, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Daemon.pm 2017 2008-04-01 21:24:23Z tfrayner $
+#
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::Daemon;
+
+use Carp;
+use Class::Std;
+use MIME::Lite;
+use Proc::Daemon;
+use POSIX qw(:sys_wait_h);
+use Socket;
+use IO::Socket;
+use IO::Select;
+use IO::Handle;
+use Scalar::Util qw(looks_like_number);
+use English qw( -no_match_vars );
+use Sys::Hostname;
+
+use ArrayExpress::Curator::Common qw(date_now);
+use ArrayExpress::Curator::Config qw($CONFIG);
+require ArrayExpress::AutoSubmission::DB::Experiment;
+
+# These are only set upon initialization
+my %polling_interval  : ATTR( :get<polling_interval>,  :init_arg<polling_interval>  );
+my %experiment_type   : ATTR( :get<experiment_type>,   :init_arg<experiment_type>   );
+my %accession_prefix  : ATTR( :get<accession_prefix>,  :init_arg<accession_prefix>,  :default<undef> );
+my %autosubs_admin    : ATTR( :get<autosubs_admin>,    :init_arg<autosubs_admin>    );
+
+# Optional for exporter daemons; the checker daemons will check that
+# this is defined.
+my %pidfile           : ATTR( :get<pidfile>,           :init_arg<pidfile>,           :default<undef> );
+my %checker_threshold : ATTR( :get<checker_threshold>, :init_arg<checker_threshold>, :default<undef> );
+
+# The log file names are manipulated post-init.
+my %logfile           : ATTR( :name<logfile>, :init_arg<logfile>, :default<q{}> );
+
+# Optional means to control the set of QTs used by the daemons.
+my %qt_filename       : ATTR( :get<qt_filename>, :init_arg<qt_filename>, :default<undef> );
+
+# Attribute holding a flag notifying the process that it should quit.
+my %quit_when_done    : ATTR( :name<quit_when_done>, :default<undef> );
+
+# Initialization subroutine.
+sub START {
+    my ( $self, $id, $args ) = @_;
+
+    croak("Error: no polling_interval set")
+	unless ( $self->get_polling_interval() );
+
+    croak("Error: no experiment_type set")
+	unless ( $self->get_experiment_type() );
+
+    croak("Error: no autosubs_admin email set.")
+	unless ( $self->get_autosubs_admin() );
+
+    return;
+}
+
+sub run {
+
+    my ( $self ) = @_;
+
+    if (my $prefix = $self->get_accession_prefix() ) {
+	ArrayExpress::AutoSubmission::DB::Experiment->accession_prefix(
+	    $prefix,
+	);
+    }
+
+    # Check that there are no other processes running, if a pidfile
+    # has been provided. Typically the checker daemons have a pidfile,
+    # but the exporter daemons do not.
+    if ( $self->get_pidfile() && -f $self->get_pidfile() ) {
+        die(
+            sprintf(
+"Error: Old pidfile %s exists; another checker daemon may be running.\n",
+                $self->get_pidfile() )
+        );
+    }
+
+    # Check that the right user has launched the process; otherwise we
+    # end up with multiple copies running under multiple accounts and
+    # it's a nightmare to get control again.
+    if ( my $admin_user = $CONFIG->get_AUTOSUBS_ADMIN_USERNAME() ) {
+	unless ( getlogin() eq $admin_user ) {
+	    die(  "Error: Non-admin user attempting to launch daemon processes"
+		. " (you should be $admin_user). Please ensure AUTOSUBS_ADMIN_USERNAME"
+	        . " is set to the correct login in the site configuration file.\n");
+	}
+    }
+    else {
+	die(  "Error: AUTOSUBS_ADMIN_USERNAME must be set to an admin"
+	    . " username in the site configuration file.\n");
+    }
+
+    # Run as a daemon
+    printf STDOUT ( "Starting %s %s daemon...\n",
+        $self->get_experiment_type(), ref($self) );
+    Proc::Daemon::Init;
+
+    ##########################
+    ### Exception handlers ###
+    ##########################
+    $SIG{TERM} = sub { $self->alert_on_signal(@_) };    # 15
+    $SIG{QUIT} = sub { $self->alert_on_signal(@_) };    # 3
+    $SIG{SEGV} = sub { $self->alert_on_signal(@_) };    # 11
+    $SIG{INT}  = sub { $self->alert_on_signal(@_) };    # 2
+    $SIG{USR1} = sub { $self->set_quit_when_done(1) };  # 16
+
+    # From here, all die()s should mail us a notification.
+    $SIG{__DIE__} = sub { $self->alert_admin(@_) };
+
+    if ( $self->get_pidfile() ) {
+	
+	# Create a file containing the process id ($$). This has to
+	# happen after daemon forking, above.
+	open( my $pid, ">", $self->get_pidfile() )
+	    or die(sprintf("Unable to open pidfile %s: %s\n",
+			   $self->get_pidfile(), $!));
+	print $pid "$PROCESS_ID\n";
+	close $pid or die("$!\n");
+    }
+
+    # The daemon starts via this entry point method.
+    $self->monitor_submissions();
+
+    return;
+}
+
+{
+    my ( $oldout, $olderr );
+
+    sub redirect_stdout_to_file : RESTRICTED {
+
+	my ( $self, $logfile ) = @_;
+
+	unless ( defined($logfile) ) {
+	    die("Error: Undefined log file name.");
+	}
+
+	open( $oldout, '>&', \*STDOUT ) and seek( $oldout, 0, 0 );
+	open( $olderr, '>&', \*STDERR ) and seek( $olderr, 0, 0 );
+
+	open( STDOUT, '>', $logfile )
+	    or die("Can't redirect STDOUT to logfile $logfile\n");
+	open( STDERR, '>&', \*STDOUT )
+	    or die("Can't redirect STDERR to STDOUT");
+	*STDERR->autoflush();    # make unbuffered
+	*STDOUT->autoflush();    # make unbuffered
+
+	return;
+    }
+
+    sub restore_stdout_from_file : RESTRICTED {
+
+	my ( $self ) = @_;
+
+	unless ( $oldout && $olderr ) {
+	    die("Error: STDOUT and STDERR not cached;"
+	      . " have you called redirect_stdout_to_file() yet?");
+	}
+
+	( close(STDOUT) and open( STDOUT, '>&', $oldout ) )
+	    or die("Can't restore STDOUT: $!\n");
+	( close(STDERR) and open( STDERR, '>&', $olderr ) )
+	    or die("Can't restore STDERR: $!\n");
+
+	return;
+    }
+}
+
+sub expt_check_submission : RESTRICTED {
+
+    my ( $self, $submission, $checker ) = @_;
+
+    # Set up our sockets for parent-child communication
+    socketpair( CHILD, PARENT, AF_UNIX, SOCK_STREAM, PF_UNSPEC )
+      or die "Cannot create socketpair: $!";
+
+    CHILD->autoflush(1);
+    PARENT->autoflush(1);
+
+    my $pid;
+    my $selector = IO::Select->new();
+    $selector->add( \*CHILD );
+
+    # Launch a subprocess to check the submission and return a score.
+    my ( $checker_score,
+	 $consensus_software,
+	 $dw_score,
+	 $miame_score,
+	 $comment );
+
+    my $start_time = date_now();
+
+    if ( $pid = fork ) {
+
+        # Parent process code
+
+        close PARENT;
+
+        my $checker_signal;
+        while ( my @handles = $selector->can_read ) {
+            foreach my $handle (@handles) {
+                recv( $handle, $checker_signal, 1024, 0 );
+            }
+            last;
+        }
+
+        # Post-process the child signal.
+        chomp $checker_signal;
+        ( $checker_score,
+	  $consensus_software,
+	  $dw_score,
+	  $miame_score,
+	  $comment ) =
+          split /\t/, $checker_signal;
+
+        close CHILD;
+        waitpid( $pid, 0 );
+
+    }
+
+    else {
+
+        # Child process code; must end with an exit
+
+        die "Error: Cannot fork: $!" unless defined $pid;
+        close CHILD;
+        $PROGRAM_NAME .= '_child';
+
+	$checker->set_clobber(1);
+
+        # Redirect STDOUT and STDERR to our log file.
+        my $logfile = $self->get_logfile()
+          or croak("Error: log file name not set.");
+
+	$self->redirect_stdout_to_file( $logfile );
+
+        # Run all the checks
+        eval { $checker->check(); };
+
+        # Communicate results with parent process
+        if ($EVAL_ERROR) {
+
+            # Checker score of 512 is reserved for checker crashes.
+            print PARENT (
+		join( "\t",
+		      $CONFIG->get_ERROR_CHECKERCRASH(),
+		      q{Unknown},
+		      q{},
+		      q{},
+		      $EVAL_ERROR,
+		  ),
+                "\n" );
+        }
+        else {
+
+            # Print out to the log.
+            print STDOUT ( "\n\nExperiment checker exit code: "
+                  . $checker->get_error()
+                  . "\n\n" );
+
+            # Pass the results to parent.
+            print PARENT (
+                join( "\t",
+		      $checker->get_error(),
+		      $checker->get_miamexpress_software_type() || q{},
+		      $checker->get_aedw_score(),
+		      $checker->get_miame(),
+		      q{}
+		  ),
+                "\n"
+            );
+        }
+        close PARENT;
+
+        # Restore STDOUT and STDERR.
+        $self->restore_stdout_from_file();
+
+        exit;    # quit the child
+
+    }
+
+    my $end_time = date_now();
+
+    #################################################################
+    # Post-checking update of the database record for a submission. #
+    #################################################################
+	    
+    # Checker score 512 is reserved for checker crashes
+    if ( $checker_score & $CONFIG->get_ERROR_CHECKERCRASH() ) {
+	$submission->set(
+	    status              => $CONFIG->get_STATUS_CRASHED(),
+	    comment             => $submission->comment()
+		                   . ( $comment ? "\n\n$comment\n" : q{} ),
+	    date_last_processed => $end_time,
+	);
+	$submission->update();
+
+	# Give up at this point.
+	return;
+    }
+
+    # Update the cache with the checker score etc.
+    my $accession = $submission->get_accession({
+	checker_score        => $checker_score,
+	software             => ( $consensus_software || 'Unknown' ),
+	data_warehouse_ready => $dw_score,
+	miame_score          => $miame_score,
+	comment              => $submission->comment()
+		                   . ( $comment ? "\n\n$comment\n" : q{} ),
+	date_last_processed  => $end_time,
+    });
+    $submission->update();
+
+    # Add an event to record this run.
+    $submission->add_to_events({
+	event_type       => 'Experiment Checker',
+	was_successful   => ( $checker_score & $CONFIG->get_ERROR_CHECKERCRASH() ? 0 : 1 ),
+	source_db        => $submission->experiment_type(),
+	start_time       => $start_time,
+	end_time         => $end_time,
+	machine          => hostname(),
+	operator         => $submission->curator(),
+	log_file         => $self->get_logfile(),
+	is_deleted       => 0,
+    });
+
+    # Check the returned info, act as necessary.
+    if (   looks_like_number($checker_score)
+	   && ( $checker_score <= $self->get_checker_threshold() ) ) {
+
+	# Submission passes checks - mark for export.
+	$submission->set(
+	    status              => $CONFIG->get_STATUS_PASSED(),
+	    date_last_processed => $end_time,
+	);
+	$submission->update();
+    }
+    else {
+
+	# Submission failed checks.
+	$submission->set(
+	    status              => $CONFIG->get_STATUS_FAILED(),
+	    date_last_processed => $end_time,
+	);
+	$submission->update();
+    }
+
+    return;
+}
+
+sub is_magetab_doc : RESTRICTED {
+
+    # Tests for combined MAGETAB IDF+SDRF document versus IDF only,
+    # returns true in the former case, false for the latter. Used in
+    # both Daemon::Checker and Daemon::Exporter subclasses.
+
+    my ( $self, $doc ) = @_;
+
+    open( my $fh, '<', $doc )
+	or croak(qq{Error: Unable to open document "$doc": $!\n});
+
+    my ( $idf_found, $sdrf_found );
+    while ( my $line = <$fh> ) {
+	$idf_found++        if ( $line =~ m/\A \s* \"? \s* \[IDF\]/ixms );
+	$sdrf_found++       if ( $line =~ m/\A \s* \"? \s* \[SDRF\]/ixms );
+    }
+
+    close( $fh ) or croak(qq{Error: Unable to close document "$doc": $!\n});
+
+    if ( $idf_found && $sdrf_found ) {
+	return 1;
+    }
+
+    return;
+}
+
+sub get_curation_experiments : RESTRICTED {
+    confess("Error: stub method called in abstract parent class.");
+}
+
+sub monitor_submissions : RESTRICTED {
+    confess("Error: stub method called in abstract parent class.");
+}
+
+# Signal handling subroutines (not OO as such)
+sub alert_admin : PRIVATE {
+
+    my ($self, $error) = @_;
+
+    my $mailbody =
+        "The automatic experiment checking system ($PROCESS_ID) "
+      . "has crashed with the following error message: \n\n  $error\n\n";
+
+    my $mail = MIME::Lite->new(
+        From     => $self->get_autosubs_admin(),
+        To       => $self->get_autosubs_admin(),
+        Subject  => 'Crash notification',
+        Type     => 'text/plain',
+        Encoding => 'quoted-printable',
+        Data     => $mailbody,
+    );
+    $mail->send();
+
+    return;
+}
+
+sub alert_on_signal : PRIVATE {
+
+    my ($self, $signal) = @_;
+
+    # Delete our PID file, if we're using one.
+    my $pidfile = $self->get_pidfile();
+    unlink $pidfile if (defined $pidfile);
+
+    # Call for help. NB if we die here we get two emails instead of
+    # one. I assume that when this signal handler returns a die() is
+    # invoked, not sure about that though.
+    $self->alert_admin("Error: Signal SIG$signal received.\n");
+
+    # FIXME this is currently undetected by any parent processes.
+    exit(255);
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/Daemon/Checker.pm b/lib/ArrayExpress/AutoSubmission/Daemon/Checker.pm
new file mode 100644
index 0000000..47a8c4a
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/Daemon/Checker.pm
@@ -0,0 +1,295 @@
+#!/usr/bin/env perl
+#
+# Module used to construct spreadsheet checker daemon objects
+#
+# Tim Rayner 2007, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Checker.pm 2012 2008-03-30 17:27:46Z tfrayner $
+#
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::Daemon::Checker;
+use base 'ArrayExpress::AutoSubmission::Daemon';
+
+use Class::Std;
+use Archive::Tar;
+use Archive::Zip;
+use IO::Zlib;
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(date_now);
+require ArrayExpress::AutoSubmission::DB::Experiment;
+use File::Spec;
+use File::Find;
+use File::Copy;
+use Carp;
+use Cwd;
+use English qw( -no_match_vars );
+
+sub START {
+    my ( $self, $id, $args ) = @_;
+
+    croak("Error: no checker_threshold set.")
+	unless ( defined( $self->get_checker_threshold() ) );
+
+    return;
+}
+
+sub unpack_archive : PRIVATE {
+
+    my ($self, $file, $submission) = @_;
+
+    my $filepath   = $file->filesystem_path();
+    my $unpack_dir = $submission->unpack_directory();
+
+    # Unpack the files into the appropriate directory. This should not
+    # create a significant memory problem - files are written directly
+    # to disk.
+    my $starting_dir = getcwd();
+    chdir $unpack_dir
+	or croak ("Error changing to unpack directory $unpack_dir: $!");
+
+    # Previously we used Archive::Any; however, memory requirements
+    # necessitated that we make the selection ourselves. Maybe add
+    # File::MMagic processing here as well as file extensions FIXME?
+    if ( $filepath =~ m/\. (?:tar.gz|tgz|tar) \z/ixms) {
+
+	# Tar archive
+
+	# In general the tar command suffers from fewer memory issues
+	# than Archive::Tar, which loads each archived data file
+	# completely into memory (not good for large files). We only
+	# fall back to Archive::Tar when tar isn't available.
+	my $rc = system("tar xvzf $filepath");
+
+	if ( $rc ) {
+
+	    # Try again, using Archive::Tar. Trap any errors;
+	    # otherwise this daemon will crash silently.
+	    local $EVAL_ERROR;
+
+	    # Apparently this call dies quite a lot; presumably
+	    # trapped with an eval. However, to cut out the spurious
+	    # alert emails we temporarily deactivate the die signal
+	    # handler.
+	    my $sighandler = $SIG{__DIE__};
+	    delete $SIG{__DIE__};
+
+	    eval {
+		Archive::Tar->extract_archive($filepath);
+	    };
+	    $SIG{__DIE__} = $sighandler if $sighandler;
+	    if ($EVAL_ERROR) {
+		croak ("Error extracting archive $filepath: $EVAL_ERROR");
+	    }
+	}
+    }
+    elsif ( $filepath =~ m/\. (?:zip) \z/ixms) {
+
+	# Zip archive
+	my $zip = Archive::Zip->new();
+	my $rc  = $zip->read($filepath);
+	if ($rc) {
+	    croak("Error reading zip file $filepath: $rc");
+	}
+	$rc = $zip->extractTree();
+	if ($rc) {
+	    croak("Error extracting zip file $filepath: $rc");
+	}
+    }
+    else {
+	croak("Error: unrecognized file type for $filepath");
+    }
+
+    # We now want to flatten the directory structure.
+    find(
+	sub {
+	    if ( -f $_ && -o $_ ) {
+		chmod(oct($CONFIG->get_FILE_PERMISSIONS()), $_)
+		    or croak("Error changing permissions on $_: $!");
+		move($_, $unpack_dir)
+		    or croak("Error moving file $_: $!");
+	    }
+	    elsif ( -d $_ && -o $_ ) {
+		chmod(oct($CONFIG->get_DIR_PERMISSIONS()), $_)
+			or croak("Error changing permissions on $_: $!");
+	    }
+	},
+	$unpack_dir,
+    );
+
+    # Back out of the unpacking directory.
+    chdir $starting_dir
+	or croak ("Error changing back to original directory: $!");
+
+    return;
+}
+
+sub monitor_submissions : RESTRICTED {
+    my $self = shift;
+
+    # Loop forever
+    EVENT_LOOP:
+    while (1) {
+
+        # Get the list of all experiments in curation.
+        my $new_results = ArrayExpress::AutoSubmission::DB::Experiment->search(
+	    in_curation     => 1,
+	    status          => $CONFIG->get_STATUS_PENDING(),
+	    experiment_type => $self->get_experiment_type(),
+	    is_deleted      => 0,
+	);
+
+        # Process the new submissions in order of id.
+	SUBMISSION:
+        while ( my $submission = $new_results->next() ) {
+
+	    # Skip submissions by test user, or submissions without any user.
+	    next SUBMISSION if ( ! $submission->user_id()
+				|| $submission->user_id()->login() eq 'test' );
+
+	    # Start a transaction.
+	    my $dbh = ArrayExpress::AutoSubmission::DB::Experiment->db_Main();
+	    $dbh->begin_work();
+
+	    # Get current status and a row-level read lock.
+	    my $current_status =
+		ArrayExpress::AutoSubmission::DB::Experiment->sql_status_for_update()
+		    ->select_val( $submission->id() ) || q{};
+
+	    # Double-check, in case something changes in the meantime.
+	    unless ( $current_status eq $CONFIG->get_STATUS_PENDING() ) {
+		$dbh->commit();
+		next SUBMISSION;
+	    }
+
+	    # Mark the submission as in checking.
+	    $submission->set(
+		status              => $CONFIG->get_STATUS_CHECKING(),
+		date_last_processed => date_now(),
+	    );
+	    $submission->update();
+
+	    # End the transaction.
+	    $dbh->commit();
+
+	    # Iterate over all the data file archives, unpacking them.
+	    my $datafiles = $submission->data_files(is_deleted => 0);
+
+	    # Note that data files are returned in order of their database id.
+	    FILE:
+	    while ( my $file = $datafiles->next() ) {
+
+		# Don't unpack a file more than once.
+		next FILE if $file->is_unpacked();
+
+		my $filepath = $file->filesystem_path();
+
+		# If the file exists, do something with it.
+		if ( -f $filepath ) {
+
+		    # This area is prone to crashes, we want to trap them locally.
+		    local $EVAL_ERROR;
+
+		    # Process those files that Archive::Any can handle.
+		    if ( $filepath =~ m/\. (?:tar.gz|tgz|tar|zip) \z/ixms ) {
+			eval {
+			    $self->unpack_archive( $file, $submission )
+			};
+		    }
+
+		    # Gzipped files can be mistaken for tarred
+		    # files. We handle them separately here.
+		    elsif (my ($outfile) = ($filepath =~ m/(.*)\. (?:gz) \z/ixms) ) {
+			eval {
+			    my $zlib_fh = new IO::Zlib;
+			    if ( $zlib_fh->open($filepath, "rb") ) {
+				open (my $output_fh, ">", $outfile)
+				    or croak("Error opening gzip output file $outfile: $!");
+				while ( my $line = <$zlib_fh> ) {
+				    print $output_fh $line;
+				}
+
+				$zlib_fh->close();
+				close( $output_fh )
+				    or croak("Error closing output file: $!");
+			    }
+			    move($outfile, $submission->unpack_directory())
+				or croak("Error moving file $outfile: $!");
+			};
+		    }
+
+		    # All uncompressed or unrecognized files are just
+		    # copied to the unpack directory.
+		    else {
+			eval{copy($filepath, $submission->unpack_directory())
+			    or croak("Error copying file $filepath: $!")};
+		    }
+
+		    # Check that there were no problems.
+		    if ( $EVAL_ERROR ) {
+			$submission->set(
+			    status              => $CONFIG->get_STATUS_CRASHED(),
+			    comment             => $submission->comment()
+				                   . "\n\nError: Unable to unpack file or archive: $filepath\n",
+			    date_last_processed => date_now(),
+			);
+			$submission->update();
+			next SUBMISSION;
+		    }
+
+		    # Mark the file as unpacked in the database.
+		    $file->set(is_unpacked => 1);
+		    $file->update();
+                }
+		else {
+
+		    # File not found. Mark the submission as bad, skip to next one.
+		    $submission->set(
+			status              => $CONFIG->get_STATUS_CRASHED(),
+			comment             => $submission->comment()
+			                       . "\n\nError: File in database not found on filesystem: $filepath\n",
+			date_last_processed => date_now(),
+		    );
+		    $submission->update();
+		    next SUBMISSION;
+		}
+	    }
+
+	    # Only one spreadsheet per submission at this point.
+	    my $spreadsheet
+		= $submission->spreadsheets(is_deleted => 0)->next();
+
+	    # Sort out the STDOUT logfile.
+            my $logfile_string = $spreadsheet->name();
+            $logfile_string =~ s/\.\w{3,4}$//;     # strip off the extension
+	    $self->set_logfile(
+		File::Spec->catfile(
+		    $submission->filesystem_directory(),
+		    "expt_${logfile_string}_stdout.log"
+		)
+	    );
+
+            # Actually check the submission for errors. This also
+            # updates the autosubs db.
+	    $self->check( $submission, $spreadsheet );
+
+	    last EVENT_LOOP if $self->get_quit_when_done();
+
+	}
+
+        sleep( $self->get_polling_interval() * 60 );
+
+	last EVENT_LOOP if $self->get_quit_when_done();
+
+    }
+
+    return;
+}
+
+sub check : RESTRICTED {
+    confess("Error: stub method called in abstract superclass.");
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/Daemon/Exporter.pm b/lib/ArrayExpress/AutoSubmission/Daemon/Exporter.pm
new file mode 100644
index 0000000..3d38470
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/Daemon/Exporter.pm
@@ -0,0 +1,219 @@
+#!/usr/bin/env perl
+#
+# Module used to construct spreadsheet checker exporter objects
+#
+# Tim Rayner 2007, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Exporter.pm 2012 2008-03-30 17:27:46Z tfrayner $
+#
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::Daemon::Exporter;
+use base 'ArrayExpress::AutoSubmission::Daemon';
+
+use Class::Std;
+use English qw( -no_match_vars );
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(date_now);
+require ArrayExpress::AutoSubmission::DB::Experiment;
+use Carp;
+use POSIX qw(:sys_wait_h);
+use Socket;
+use IO::Socket;
+use IO::Select;
+use Sys::Hostname;
+
+# This is simply passed through to the Tab2MAGE object.
+my %keep_protocol_accns : ATTR( :get<keep_protocol_accns>, :init_arg<keep_protocol_accns>, :default<undef> );
+
+# Track which subdirectory to write the output to.
+my %pipeline_subdir : ATTR( :get<pipeline_subdir>, :init_arg<pipeline_subdir>, :default<undef> );
+
+sub export_tab2mage : PRIVATE {
+
+    my ( $self, $submission ) = @_;
+
+    # Set up our sockets for parent-child communication
+    socketpair( CHILD, PARENT, AF_UNIX, SOCK_STREAM, PF_UNSPEC )
+      or die "Cannot create socketpair: $!";
+
+    CHILD->autoflush(1);
+    PARENT->autoflush(1);
+
+    my $pid;
+    my $selector = IO::Select->new();
+    $selector->add( \*CHILD );
+
+    my $exporter_signal;
+
+    if ( $pid = fork ) {
+
+        # Parent process code
+
+        close PARENT;
+
+        while ( my @handles = $selector->can_read ) {
+            foreach my $handle (@handles) {
+                recv( $handle, $exporter_signal, 1024, 0 );
+            }
+            last;
+        }
+	chomp $exporter_signal;
+
+        close CHILD;
+        waitpid( $pid, 0 );
+    }
+
+    else {
+
+        # Child process code; must end with an exit
+
+        die "Error: Cannot fork: $!" unless defined $pid;
+        close CHILD;
+        $PROGRAM_NAME .= '_child';
+
+	my $spreadsheet
+	    = $submission->spreadsheets(is_deleted => 0)->next();
+
+	# Create the Tab2MAGE or MAGE-TAB exporter object.
+	my $exporter = $self->create_exporter( $submission, $spreadsheet );
+
+	# Here we actually do the MAGE-ML export.
+	eval {
+
+	    # Handle the combined MAGETAB doc case here.
+	    if ( $exporter->can('parse_magetab_doc')
+		 && $exporter->get_magetab_doc() ) {
+		$exporter->parse_magetab_doc();
+	    }
+
+	    # Actually export the MAGE-ML.
+	    $exporter->write_mageml();
+	};
+
+	print PARENT ("$EVAL_ERROR\n");
+
+	close PARENT;
+
+	exit;
+    }
+
+    return $exporter_signal; # non-zero is failure
+}
+
+sub monitor_submissions : RESTRICTED {
+    my $self = shift;
+
+    # Loop forever
+    EVENT_LOOP:
+    while (1) {
+
+	# Create the accession cache object.
+	my $iterator
+	    = ArrayExpress::AutoSubmission::DB::Experiment->search(
+		experiment_type => $self->get_experiment_type(),
+		status          => $CONFIG->get_STATUS_PASSED(),
+		is_deleted      => 0,
+	);
+
+	SUBMISSION:
+	while ( my $submission = $iterator->next() ) {
+
+	    # Skip submissions by test user, or submissions without any user.
+	    next SUBMISSION if ( ! $submission->user_id()
+				|| $submission->user_id()->login() eq 'test' );
+
+	    # Start a transaction.
+	    my $dbh = ArrayExpress::AutoSubmission::DB::Experiment->db_Main();
+	    $dbh->begin_work();
+
+	    # Get current status and a row-level read lock.
+	    my $current_status =
+		ArrayExpress::AutoSubmission::DB::Experiment->sql_status_for_update()
+		    ->select_val( $submission->id() ) || q{};
+
+	    # Double-check, in case something changes in the meantime.
+	    unless ( $current_status eq $CONFIG->get_STATUS_PASSED() ) {
+		$dbh->commit();
+		next SUBMISSION;
+	    }
+
+	    # Set the status so a parallel daemon doesn't pick up.
+	    $submission->set(
+		status              => $CONFIG->get_STATUS_EXPORT(),
+		date_last_processed => date_now(),
+	    );
+	    $submission->update();
+
+	    # End the transaction.
+	    $dbh->commit();
+
+	    # Actually export the MAGE-ML.
+	    my $start_time = date_now();
+	    my $rc = $self->export_tab2mage( $submission );
+	    my $end_time = date_now();
+
+	    if ( ! $rc ) {    # Test for export success.
+		$submission->set(
+		    status              => $CONFIG->get_STATUS_COMPLETE(),
+		    date_last_processed => $end_time,
+		);
+	    }
+	    else {                    # Export failed
+		$submission->set(
+		    status              => $CONFIG->get_STATUS_EXPORT_ERROR(),
+		    date_last_processed => $end_time,
+		    comment             => $submission->comment()
+			                   . "\n\n$rc\n",
+		);
+	    }
+	    $submission->update();
+
+	    # Add an event to record this run.
+	    $submission->add_to_events({
+		event_type       => 'MAGE-ML Export',
+		was_successful   => ( $rc ? 0 : 1 ),
+		source_db        => $submission->experiment_type(),
+		start_time       => $start_time,
+		end_time         => $end_time,
+		machine          => hostname(),
+		operator         => $submission->curator(),
+		log_file         => $self->get_logfile(),
+		is_deleted       => 0,
+	    });
+
+	    last EVENT_LOOP if $self->get_quit_when_done();
+
+	}
+	
+	sleep( $self->get_polling_interval() * 60 );
+
+	last EVENT_LOOP if $self->get_quit_when_done();
+
+    }
+
+    return;
+}
+
+sub get_targetdir : RESTRICTED {
+
+    my ( $self ) = @_;
+
+    my $targetdir = $CONFIG->get_AUTOSUBMISSIONS_TARGET();
+    if ( my $subdir = $self->get_pipeline_subdir() ) {
+	$targetdir = File::Spec->catdir(
+	    $targetdir,
+	    $subdir,
+	);
+    }
+
+    return $targetdir;
+}
+
+sub create_exporter : RESTRICTED {
+    confess("Error: stub method called in abstract superclass");
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/Daemon/MAGETABChecker.pm b/lib/ArrayExpress/AutoSubmission/Daemon/MAGETABChecker.pm
new file mode 100644
index 0000000..e09f2a7
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/Daemon/MAGETABChecker.pm
@@ -0,0 +1,57 @@
+#!/usr/bin/env perl
+#
+# $Id: MAGETABChecker.pm 2020 2008-04-08 13:15:33Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::Daemon::MAGETABChecker;
+use base 'ArrayExpress::AutoSubmission::Daemon::Checker';
+
+require ArrayExpress::MAGETAB::Checker;
+
+use Class::Std;
+
+sub check : RESTRICTED {
+
+    my ( $self, $submission, $spreadsheet ) = @_;
+
+    my $file = $spreadsheet->filesystem_path();
+
+    # For MIAMExpress submissions converted to MAGE-TAB, we don't want
+    # to repeat the data file checks. Here we assume that the
+    # miamexpress_subid column is a good indicator (which, for the
+    # most part, it should be).
+    my $skip_data = $submission->miamexpress_subid() ? 1 : 0;
+
+    # Parameters passed to ExperimentChecker object.
+    my %checker_params;
+    if ( $self->is_magetab_doc( $file ) ) {
+	%checker_params = (
+	    magetab_doc        => $file,
+	    source_directory   => $submission->unpack_directory(),
+	    log_to_current_dir => $submission->filesystem_directory(),
+	    qt_filename        => $self->get_qt_filename(),
+            skip_data_checks   => $skip_data,
+            safe_filechecks    => 1,
+	);
+    }
+    else {
+	%checker_params = (
+	    idf                => $file,
+	    source_directory   => $submission->unpack_directory(),
+	    log_to_current_dir => $submission->filesystem_directory(),
+	    qt_filename        => $self->get_qt_filename(),
+            skip_data_checks   => $skip_data,
+            safe_filechecks    => 1,
+	);
+    }
+    $self->expt_check_submission(
+	$submission,
+	ArrayExpress::MAGETAB::Checker->new(\%checker_params),
+    );
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/Daemon/MAGETABExporter.pm b/lib/ArrayExpress/AutoSubmission/Daemon/MAGETABExporter.pm
new file mode 100644
index 0000000..e3462f7
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/Daemon/MAGETABExporter.pm
@@ -0,0 +1,64 @@
+#!/usr/bin/env perl
+#
+# $Id: MAGETABExporter.pm 1868 2008-01-03 14:26:51Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::Daemon::MAGETABExporter;
+use base 'ArrayExpress::AutoSubmission::Daemon::Exporter';
+
+use Class::Std;
+use File::Spec;
+require ArrayExpress::MAGETAB;
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+sub create_exporter : RESTRICTED {
+
+    my ( $self, $submission, $spreadsheet ) = @_;
+
+    my %common_opts = (
+	authority                 => $CONFIG->get_AUTOSUBS_DOMAIN(),
+	namespace                 => 'MAGETabulator',
+	expt_accession            => $submission->get_accession(),
+	output_file               => $submission->get_accession() . '.xml',
+	target_directory          => File::Spec->catdir(
+	    $self->get_targetdir(),
+	    $submission->accession(),
+	),
+	source_directory          => $submission->unpack_directory(),
+	use_plain_text            => 0,
+	protocol_accession_prefix => 'P-MTAB-',
+	keep_protocol_accns       => $self->get_keep_protocol_accns(),
+	clobber                   => 1,
+	qt_filename               => $self->get_qt_filename(),
+
+	# Note that if we change this we must also revert the
+	# ExperimentChecker code to flag unknown QTs as
+	# PARSEBAD.
+	keep_all_qts              => 1,   # review this FIXME
+    );
+
+    # Decide whether this is a combined MAGETAB (IDF+SDRF) or IDF-only
+    # document.
+    my $magetab;
+    my $file = $spreadsheet->filesystem_path();
+    if ( $self->is_magetab_doc( $file ) ) {
+	$magetab = ArrayExpress::MAGETAB->new({
+	    magetab_doc => $file,
+	    %common_opts,
+	});
+    }
+    else {
+	$magetab = ArrayExpress::MAGETAB->new({
+	    idf => $file,
+	    %common_opts,
+	});
+    }
+
+    $self->set_logfile( $magetab->logfiles('magetab') );
+
+    return $magetab;
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/Daemon/MXChecker.pm b/lib/ArrayExpress/AutoSubmission/Daemon/MXChecker.pm
new file mode 100644
index 0000000..fac69a7
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/Daemon/MXChecker.pm
@@ -0,0 +1,271 @@
+#!/usr/bin/env perl
+#
+# Module used to construct MIAMExpress checker daemon objects
+#
+# Tim Rayner 2007, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: MXChecker.pm 2013 2008-03-31 17:23:14Z tfrayner $
+#
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::Daemon::MXChecker;
+
+use base 'ArrayExpress::AutoSubmission::Daemon';
+
+use Class::Std;
+use DBI;
+use Carp;
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(date_now mx2tab);
+
+require ArrayExpress::AutoSubmission::DB::Experiment;
+require ArrayExpress::Curator::MIAMExpress;
+
+my %mx_dsn       : ATTR( :get<mx_dsn>,       :init_arg<mx_dsn>       );
+my %mx_username  : ATTR( :get<mx_username>,  :init_arg<mx_username>  );
+my %mx_password  : ATTR( :get<mx_password>,  :init_arg<mx_password>  );
+my %mx_dbparams  : ATTR( :get<mx_dbparams>,  :init_arg<mx_dbparams>  );
+
+sub START {
+    my ( $self, $id, $args ) = @_;
+
+    croak("Error: no mx_dsn set.")
+	unless ( $self->get_mx_dsn() );
+
+    croak("Error: no mx_username set.")
+	unless ( $self->get_mx_username() );
+
+    croak("Error: no mx_password set.")
+	unless ( $self->get_mx_password() );
+
+    croak("Error: no mx_dbparams set.")
+	unless ( $self->get_mx_dbparams() );
+
+    croak("Error: no checker_threshold set.")
+	unless ( defined( $self->get_checker_threshold() ) );
+
+    return;
+}
+
+sub get_curation_expts : PRIVATE {
+    my $self = shift;
+
+    my $dbh = DBI->connect(
+        $self->get_mx_dsn(),      $self->get_mx_username(),
+        $self->get_mx_password(), $self->get_mx_dbparams(),
+    ) or die("Database connection error: $DBI::errstr\n");
+
+    my $sth = $dbh->prepare(<<'QUERY');
+select distinct TSUBMIS_SYSUID from TSUBMIS, TEXPRMNT
+where TSUBMIS_PROC_STATUS in ('C','S')
+and TSUBMIS_COMP_STATUS='U'
+and TSUBMIS_SYSUID=TEXPRMNT_SUBID
+and TSUBMIS_DEL_STATUS='U'
+and TEXPRMNT_DEL_STATUS='U'
+and TSUBMIS_LOGIN!='test'
+QUERY
+
+    $sth->execute() or die($sth->errstr);
+
+    my $results = $sth->fetchall_hashref('TSUBMIS_SYSUID');
+
+    $dbh->disconnect()
+      or die( "Database disconnection error: " . $dbh->errstr() . "\n" );
+
+    return [ sort keys %{$results} ];
+
+}
+
+sub info_from_subid : PRIVATE {
+
+    my ( $self, $subid ) = @_;
+
+    my $dbh = DBI->connect(
+        $self->get_mx_dsn(),      $self->get_mx_username(),
+        $self->get_mx_password(), $self->get_mx_dbparams(),
+    ) or die("Database connection error: $DBI::errstr\n");
+
+    my $sth = $dbh->prepare(<<'QUERY');
+select distinct TSUBMIS_LOGIN, TSUBMIS_SUB_DESCR, TSUBMIS_LAST_CHANGE, TEXPRMNT_VARS
+from TSUBMIS, TEXPRMNT
+where TSUBMIS_SYSUID=?
+and TSUBMIS_SYSUID=TEXPRMNT_SUBID
+and TEXPRMNT_DEL_STATUS='U'
+and TSUBMIS_DEL_STATUS='U'
+QUERY
+
+    $sth->execute($subid) or die($sth->errstr);
+
+    my $results = $sth->fetchall_arrayref;
+
+    $sth->finish();
+
+    if ( scalar(@$results) != 1 ) {
+        die(
+	    "Error: submission id $subid maps to "
+	    . scalar(@$results)
+	    . " submissions in the database!\n"
+        );
+    }
+
+    my ( $login, $title, $date, $vars ) = @{ $results->[0] };
+
+    my $is_mxbl = 0;
+    if ( $vars && $vars =~ m/BatchUploadTool/ixms ) {
+	$is_mxbl = 1;
+    }
+
+    # Get the experiment filebase, while we have a connection.
+    $sth = $dbh->prepare(<<'QUERY');
+select distinct TSYSOPT_VALUE from TSYSOPT
+where TSYSOPT_CODE='experiment_datafiles_path'
+QUERY
+
+    $sth->execute() or die($sth->errstr);
+
+    # FIXME we're assuming the query worked here.
+    my $subsdir = File::Spec->catdir(
+	$sth->fetchrow_arrayref()->[0], $login, "submission$subid",
+    );
+
+    $dbh->disconnect()
+      or die( "Database disconnection error: " . $dbh->errstr() . "\n" );
+
+    return ( $login, $title, $subsdir, $date, $is_mxbl );
+
+}
+
+sub monitor_submissions : RESTRICTED {
+    my $self = shift;
+
+    # Loop forever
+    EVENT_LOOP:
+    while (1) {
+
+        # Get the list of all experiments in curation.
+        my $new_results = $self->get_curation_expts();
+
+        # Process the new submissions in order of id.
+	SUBMISSION:
+        foreach my $subid ( @{$new_results} ) {
+
+            # Retrieve each experiment record in turn, creating
+            # records where none exist.
+            my $submission =
+		ArrayExpress::AutoSubmission::DB::Experiment->retrieve(
+		    miamexpress_subid => $subid,
+		    is_deleted        => 0,
+		)
+	         ||
+	        ArrayExpress::AutoSubmission::DB::Experiment->insert({
+		    miamexpress_subid => $subid,
+		    status            => $CONFIG->get_STATUS_PENDING(),
+		    experiment_type   => $self->get_experiment_type(),
+		    is_deleted        => 0,
+		});
+
+            # We avoid checking non-MX submissions with MX submission
+            # IDs here (these will typically have been switched to
+            # e.g. MAGE-TAB).
+            next SUBMISSION
+              if ( $submission->experiment_type() ne $self->get_experiment_type() );
+
+	    # Start a transaction.
+	    my $dbh = ArrayExpress::AutoSubmission::DB::Experiment->db_Main();
+	    $dbh->begin_work();
+
+	    # Get current status and a row-level read lock.
+	    my $current_status =
+		ArrayExpress::AutoSubmission::DB::Experiment->sql_status_for_update()
+		    ->select_val( $submission->id() ) || q{};
+
+            # Skip the submission if it has a non-pending status
+            # already in the database. Occasionally checking crashes
+            # due to e.g. AE being down, so all submissions pending
+            # checking (or with NULL status) are fair game. Crashed
+            # subs are no longer checked.
+	    # Double-check, in case something changes in the meantime.
+	    unless ( $current_status eq $CONFIG->get_STATUS_PENDING() ) {
+		$dbh->commit();
+		next SUBMISSION;
+	    }
+
+            $submission->set(
+                status              => $CONFIG->get_STATUS_DB_RETRIEVAL(),
+                date_last_processed => date_now(),
+            );
+            $submission->update();
+
+	    # End the transaction.
+	    $dbh->commit();
+
+            my ( $login, $title, $subsdir, $subsdate, $is_mxbl )
+		= $self->info_from_subid($subid);
+
+            # This is specific to the current MIAMExpress version.
+            $self->set_logfile(
+                File::Spec->catfile(
+		    $subsdir,
+                    "expt_${login}_sub${subid}_stdout.log"
+                )
+            );
+
+            # Enter the login and expt name information.
+            $submission->set(
+                miamexpress_login   => $login,
+                name                => $title,
+                status              => $CONFIG->get_STATUS_CHECKING(),
+		date_submitted      => $subsdate,
+                date_last_processed => date_now(),
+                is_mx_batchloader   => $is_mxbl,
+            );
+            $submission->update();
+
+            # Actually check the submission for errors. This also
+            # updates the autosubs db.
+	    $self->expt_check_submission(
+		$submission,
+		ArrayExpress::Curator::MIAMExpress->new({
+		    mx_login => $login,
+		    mx_title => $title,
+		}),
+	    );
+
+	    # FIXME maybe bring the status changes from mx2tab into
+	    # this module so that they can be more easily managed
+	    # within a transaction. NOTE however that when I tried
+	    # wrapping this call in a transaction quite odd things
+	    # happened; possibly a DBD::mysql bug? The script failed
+	    # while complaining that the DBD driver hadn't implemented
+	    # the AutoCommit attribute (which, of course, it
+	    # has). Note also that the transaction earlier works just
+	    # fine, maybe there's a cryptic interaction between the
+	    # two.
+	    my $new_status =
+		ArrayExpress::AutoSubmission::DB::Experiment->sql_status_for_update()
+		    ->select_val( $submission->id() ) || q{};
+
+	    # If checks passed, we now switch the submission to
+	    # MAGE-TAB.
+	    if ( $new_status eq $CONFIG->get_STATUS_PASSED() ) {
+		eval {
+		    mx2tab($submission);
+		};
+	    }
+
+	    last EVENT_LOOP if $self->get_quit_when_done();
+
+	}
+	
+        sleep( $self->get_polling_interval() * 60 );
+
+	last EVENT_LOOP if $self->get_quit_when_done();
+
+    }
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/Daemon/MXExporter.pm b/lib/ArrayExpress/AutoSubmission/Daemon/MXExporter.pm
new file mode 100644
index 0000000..8fc41c8
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/Daemon/MXExporter.pm
@@ -0,0 +1,247 @@
+#!/usr/bin/env perl
+#
+# Module used to construct MIAMExpress exporter daemon objects
+#
+# Tim Rayner 2007, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: MXExporter.pm 2012 2008-03-30 17:27:46Z tfrayner $
+#
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::Daemon::MXExporter;
+use base 'ArrayExpress::AutoSubmission::Daemon';
+
+use English qw( -no_match_vars );
+use Class::Std;
+use DBI;
+use File::Spec;
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(date_now);
+require ArrayExpress::AutoSubmission::DB::Experiment;
+use Carp;
+use POSIX qw(:sys_wait_h);
+use Socket;
+use IO::Socket;
+use IO::Select;
+use Sys::Hostname;
+
+my %mx_export_command : ATTR( :get<mx_export_command>, :init_arg<mx_export_command> );
+
+sub START {
+    my ( $self, $id, $args ) = @_;
+
+    croak("Error: no mx_export_command set.")
+	unless ( $self->get_mx_export_command() );
+
+    return;
+}
+
+sub export_mx_mageml : PRIVATE {
+
+    my ( $self, $title, $software, $accession, $login, $subid ) = @_;
+
+    unless ( $self->get_mx_export_command() ) {
+        die("Error: MIAMExpress export command not defined.\n");
+    }
+
+    # Set up our sockets for parent-child communication
+    socketpair( CHILD, PARENT, AF_UNIX, SOCK_STREAM, PF_UNSPEC )
+      or die "Cannot create socketpair: $!";
+
+    CHILD->autoflush(1);
+    PARENT->autoflush(1);
+
+    my $pid;
+    my $selector = IO::Select->new();
+    $selector->add( \*CHILD );
+
+    my ( $exporter_signal, $logfile );
+
+    if ( $pid = fork ) {
+
+        # Parent process code
+
+        close PARENT;
+
+	my $child_signal;
+        while ( my @handles = $selector->can_read ) {
+            foreach my $handle (@handles) {
+                recv( $handle, $child_signal, 1024, 0 );
+            }
+            last;
+        }
+	chomp $child_signal;
+
+	# It's important to know where our logfile is.
+	( $logfile, $exporter_signal ) = split /\t/, $child_signal;
+
+        close CHILD;
+        waitpid( $pid, 0 );
+    }
+
+    else {
+
+        # Child process code; must end with an exit
+
+        die "Error: Cannot fork: $!" unless defined $pid;
+        close CHILD;
+        $PROGRAM_NAME .= '_child';
+
+	# First, sort out a log file for STDOUT/STDERR redirection.
+	my $dbh = DBI->connect(
+	    $CONFIG->get_MX_DSN(),      $CONFIG->get_MX_USERNAME(),
+	    $CONFIG->get_MX_PASSWORD(), $CONFIG->get_MX_DBPARAMS(),
+	) or die("Database connection error: $DBI::errstr\n");
+
+	my $sth = $dbh->prepare(<<'QUERY');
+select distinct TSYSOPT_VALUE from TSYSOPT
+where TSYSOPT_CODE='experiment_datafiles_path'
+QUERY
+
+	$sth->execute() or die($sth->errstr);
+
+	# FIXME we're assuming the query worked here.
+	my $logfile = File::Spec->catfile(
+	    $sth->fetchrow_arrayref()->[0],
+	    $login,
+	    "submission$subid",
+	    "expt_${login}_sub${subid}_mxexporter.log",
+	);
+
+	$dbh->disconnect();
+
+	my $sys_command = $self->get_mx_export_command()
+	    . qq{ "$title" "$software" "$accession"};
+
+	$self->redirect_stdout_to_file( $logfile );
+	print STDOUT "Running command:\n\n$sys_command\n\n";
+
+	my $rc = system($sys_command);
+
+	# Pass the logfile back to our parent.
+	print PARENT ("$logfile\t$rc\n");
+
+	close PARENT;
+
+	$self->restore_stdout_from_file();
+
+	exit;
+    }
+
+    return ( $logfile, $exporter_signal ); # non-zero is failure
+}
+
+sub monitor_submissions : RESTRICTED {
+    my $self = shift;
+
+    # Loop forever
+    EVENT_LOOP:
+    while (1) {
+
+	# Create the accession cache iterators.
+	my $passed
+	    = ArrayExpress::AutoSubmission::DB::Experiment->search(
+		experiment_type => $self->get_experiment_type(),
+		status          => $CONFIG->get_STATUS_PASSED(),
+		is_deleted      => 0,
+	);
+	my $postponed
+	    = ArrayExpress::AutoSubmission::DB::Experiment->search(
+		experiment_type => $self->get_experiment_type(),
+		status          => $CONFIG->get_STATUS_EXPORT_POSTPONED(),
+		is_deleted      => 0,
+	);
+
+	SUBMISSION:
+	while ( my $submission = ( $passed->next() || $postponed->next() ) ) {
+
+	    # Start a transaction.
+	    my $dbh = ArrayExpress::AutoSubmission::DB::Experiment->db_Main();
+	    $dbh->begin_work();
+
+	    # Get current status and a row-level read lock.
+	    my $current_status =
+		ArrayExpress::AutoSubmission::DB::Experiment->sql_status_for_update()
+		    ->select_val( $submission->id() ) || q{};
+
+	    # Double-check, in case something changes in the meantime.
+	    unless (   $current_status eq $CONFIG->get_STATUS_PASSED()
+		    || $current_status eq $CONFIG->get_STATUS_EXPORT_POSTPONED() ) {
+		$dbh->commit();
+		next SUBMISSION;
+	    }
+
+	    $submission->set(
+		status              => $CONFIG->get_STATUS_EXPORT(),
+		date_last_processed => date_now(),
+	    );
+	    $submission->update();
+
+	    # End the transaction.
+	    $dbh->commit();
+
+	    my $start_time = date_now();
+	    my ( $logfile, $rc ) = $self->export_mx_mageml(
+		$submission->name(),
+		$submission->software(),
+		$submission->accession(),
+		$submission->miamexpress_login(),
+		$submission->miamexpress_subid(),
+	    );
+	    my $end_time = date_now();
+
+	    if ( ! $rc ) {
+
+		# False == success.
+		$submission->set(
+		    status              => $CONFIG->get_STATUS_COMPLETE(),
+		    date_last_processed => $end_time,
+		);
+	    }
+	    elsif ( (unpack "c", pack "C", $rc >> 8) == -7 ) {
+
+		# Export failed due to connectivity problem ("exit -7").
+		$submission->set(
+		    status              => $CONFIG->get_STATUS_EXPORT_POSTPONED(),
+		    date_last_processed => $end_time,
+		);
+	    }
+	    else {
+
+		# Export failed.
+		$submission->set(
+		    status              => $CONFIG->get_STATUS_EXPORT_ERROR(),
+		    date_last_processed => $end_time,
+		);
+	    }
+	    $submission->update();
+
+	    # Add an event to record this run.
+	    $submission->add_to_events({
+		event_type       => 'MAGE-ML Export',
+		was_successful   => ( $rc ? 0 : 1 ),
+		source_db        => $submission->experiment_type(),
+		start_time       => $start_time,
+		end_time         => $end_time,
+		machine          => hostname(),
+		operator         => $submission->curator(),
+		log_file         => $logfile,
+		is_deleted       => 0,
+	    });
+
+	    last EVENT_LOOP if $self->get_quit_when_done();
+
+	}
+	
+	sleep( $self->get_polling_interval() * 60 );
+
+	last EVENT_LOOP if $self->get_quit_when_done();
+
+    }
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/Daemon/T2MChecker.pm b/lib/ArrayExpress/AutoSubmission/Daemon/T2MChecker.pm
new file mode 100644
index 0000000..9970b4f
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/Daemon/T2MChecker.pm
@@ -0,0 +1,32 @@
+#!/usr/bin/env perl
+#
+# $Id: T2MChecker.pm 2020 2008-04-08 13:15:33Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::Daemon::T2MChecker;
+use base 'ArrayExpress::AutoSubmission::Daemon::Checker';
+
+require ArrayExpress::Curator::Validate;
+
+use Class::Std;
+
+sub check : RESTRICTED {
+
+    my ( $self, $submission, $spreadsheet ) = @_;
+
+    $self->expt_check_submission(
+	$submission,
+	ArrayExpress::Curator::Validate->new({
+	    spreadsheet_filename => $spreadsheet->filesystem_path(),
+	    source_directory     => $submission->unpack_directory(),
+	    qt_filename          => $self->get_qt_filename(),
+            safe_filechecks      => 1,
+	}),
+    );
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/Daemon/T2MExporter.pm b/lib/ArrayExpress/AutoSubmission/Daemon/T2MExporter.pm
new file mode 100644
index 0000000..968f3d5
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/Daemon/T2MExporter.pm
@@ -0,0 +1,47 @@
+#!/usr/bin/env perl
+#
+# $Id: T2MExporter.pm 1868 2008-01-03 14:26:51Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::Daemon::T2MExporter;
+use base 'ArrayExpress::AutoSubmission::Daemon::Exporter';
+
+use Class::Std;
+use File::Spec;
+require ArrayExpress::Curator::Tab2MAGE;
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+sub create_exporter : RESTRICTED {
+
+    my ( $self, $submission, $spreadsheet ) = @_;
+
+    my $tab2mage = ArrayExpress::Curator::Tab2MAGE->new({
+
+	# Only one spreadsheet per submission at the moment.
+	spreadsheet_filename => $spreadsheet->filesystem_path(),
+	target_directory     => File::Spec->catdir(
+	    $self->get_targetdir(),
+	    $submission->accession(),
+	),
+	source_directory     => $submission->unpack_directory(),
+	external_accession   => $submission->get_accession(),
+	external_domain      => $CONFIG->get_AUTOSUBS_DOMAIN,
+	clobber              => 1,
+	binary_datafiles     => 1,
+	keep_protocol_accns  => $self->get_keep_protocol_accns(),
+	qt_filename          => $self->get_qt_filename(),
+
+	# Note that if we change this we must also revert the
+	# ExperimentChecker code to flag unknown QTs as
+	# PARSEBAD.
+	keep_all_qts         => 1,   # review this FIXME
+    });
+
+    $self->set_logfile( $tab2mage->logfiles('tab2mage') );
+
+    return $tab2mage;
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/Spreadsheet.pm b/lib/ArrayExpress/AutoSubmission/Spreadsheet.pm
new file mode 100644
index 0000000..422cfb5
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/Spreadsheet.pm
@@ -0,0 +1,1079 @@
+#!/usr/bin/env perl
+#
+# Module used to generate spreadsheet templates for Tab2MAGE submissions
+#
+# Tim Rayner 2006, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Spreadsheet.pm 2069 2008-06-04 14:33:52Z tfrayner $
+#
+
+package ArrayExpress::AutoSubmission::Spreadsheet;
+
+use strict;
+use warnings;
+
+use Class::Std;
+use Carp;
+use Readonly;
+use ArrayExpress::Curator::Common qw(decamelize);
+require ArrayExpress::AutoSubmission::DB::Category;
+use ArrayExpress::Curator::MAGE::Definitions qw(
+  $EDF_EXPTACCESSION
+  $EDF_EXPTDOMAIN
+  $EDF_EXPTNAME
+  $EDF_EXPTDESCRIPTION
+  $EDF_EXPTRELEASEDATE
+  $EDF_EXPTSUBMITTER
+  $EDF_EXPTDATACODER
+  $EDF_EXPTCURATOR
+  $EDF_EXPTINVESTIGATOR
+  $EDF_EXPTORGANIZATION
+  $EDF_EXPTADDRESS
+  $EDF_EXPTSUBMITTER_EMAIL
+  $EDF_EXPTCURATOR_EMAIL
+  $EDF_EXPTINVESTIGATOR_EMAIL
+  $EDF_EXPTDATACODER_EMAIL
+  $EDF_EXPTSUBMISSIONDATE
+  $EDF_EXPTDESIGNTYPE
+  $EDF_EXPTQUALITYCONTROL
+  $EDF_EXPTCURATEDNAME
+
+  $EDF_PUBTITLE
+  $EDF_PUBPAGES
+  $EDF_PUBJOURNAL
+  $EDF_PUBVOLUME
+  $EDF_PUBISSUE
+  $EDF_PUBYEAR
+  $EDF_PUBAUTHORS
+  $EDF_PUBURI
+  $EDF_PUBMEDID
+
+  $EDF_PROTOCOLACCESSION
+  $EDF_PROTOCOLNAME
+  $EDF_PROTOCOLTYPE
+  $EDF_PROTOCOLTEXT
+  $EDF_PROTOCOLPARAMS
+
+  $OE_VAL_CHIP_CHIP
+);
+
+my %experiment : ATTR( :init_arg<experiment>, :get<experiment>, :default<undef> );
+my %url        : ATTR( :init_arg<url>,        :get<url>,        :default<undef> );
+my %date       : ATTR( :init_arg<date>,       :get<date>,       :default<undef> );
+
+# 1 = all spreadsheets; 2 = dye-swap only; 4 = Affy only, 8 = ChIP
+Readonly my $ALL      => 1;
+Readonly my $DYESWAP  => 2;
+Readonly my $AFFY     => 4;
+Readonly my $CHIPCHIP => 8;
+
+Readonly my $MGED_ONTOLOGY => 'MGED Ontology';
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    my $experiment = $self->get_experiment();
+    unless (   $experiment
+	    && $experiment->isa('ArrayExpress::AutoSubmission::DB::Experiment') ) {
+	croak('Error: experiment attribute not set to an'
+	    . ' ArrayExpress::AutoSubmission::DB::Experiment object.');
+    }
+
+    unless ( $self->get_url() ) {
+	carp('Warning: URL attribute not set.');
+    }
+    unless ( $self->get_date() ) {
+	carp('Warning: Date attribute not set.');
+    }
+
+    return;
+}
+
+sub _tab2mage_expt_section : PRIVATE {
+
+    my ( $self ) = @_;
+
+    my $experiment = $self->get_experiment();
+
+    my $experiment_name = $experiment->name();
+    my $experiment_type = $experiment->experiment_type();
+    my $submitter       = $experiment->user_id()->name();
+    my $submitter_email = $experiment->user_id()->email();
+
+    my $url  = $self->get_url()  || q{};
+    my $date = $self->get_date() || q{};
+    
+    my $design_string =
+      join( ', ', map { $_->ontology_value() } $experiment->designs() );
+
+    my $template = <<"EXPTSECTION";
+#
+# $experiment_type Submission '$experiment_name', generated at $date
+#
+# Please save this page to disk, and edit it using any spreadsheet program (e.g.,
+# Microsoft Excel, OpenOffice.org Calc). This file can be imported into either of
+# these applications as a tab-delimited text file. It is highly recommended that
+# you set the cell format to plain text throughout the spreadsheet, to avoid
+# 'helpful' changes made by the spreadsheet application. Once you have completed
+# this spreadsheet please upload it using the submissions webpage alongside your
+# data files:
+#    $url
+#
+# Please see http://tab2mage.sourceforge.net/docs/spreadsheet.html for
+# information on filling in the spreadsheet.
+#
+# All lines beginning with the '#' character are treated as comments and ignored.
+# Blank lines may only be used between Experiment, Protocol and Hybridization sections.
+#
+#
+#
+# This section contains the top-level information for your experiment.
+Experiment section
+$EDF_EXPTNAME\t$experiment_name
+$EDF_EXPTDESCRIPTION\t
+$EDF_EXPTDESIGNTYPE\t$design_string
+# $EDF_EXPTQUALITYCONTROL examples: dye_swap_quality_control, biological_replicate, technical_replicate
+$EDF_EXPTQUALITYCONTROL\t
+# Dates should be entered in the form YYYY-MM-DD. If you are using MS Excel,
+# it is recommended that you set your spreadsheet to 'text' format throughout
+# to help avoid any unwanted changes made by Excel.
+$EDF_EXPTRELEASEDATE\t
+$EDF_EXPTSUBMISSIONDATE\t
+$EDF_EXPTSUBMITTER\t$submitter
+$EDF_EXPTORGANIZATION\t
+$EDF_EXPTADDRESS\t
+$EDF_EXPTSUBMITTER_EMAIL\t$submitter_email
+$EDF_PUBTITLE\t
+$EDF_PUBPAGES\t
+$EDF_PUBJOURNAL\t
+$EDF_PUBVOLUME\t
+$EDF_PUBISSUE\t
+$EDF_PUBYEAR\t
+$EDF_PUBAUTHORS\t
+$EDF_PUBMEDID\t
+EXPTSECTION
+
+    return $template;
+}
+
+sub _tab2mage_prot_section : PRIVATE {
+
+    my ( $self ) = @_;
+    
+    my $experiment    = $self->get_experiment();
+    my $is_affymetrix = $experiment->is_affymetrix();
+
+    my $template = <<"PROTHEADER";
+
+# The next section describes the protocols used in your experiment. Please select
+# your own accession codes (e.g. 'GROWTH', 'TREATMENT') for use within this spreadsheet,
+# or re-use ArrayExpress protocols by using the assigned ArrayExpress protocol
+# accession numbers. If you have already submitted protocols to ArrayExpress,
+# you can retrieve these using this web page:
+#    http://www.ebi.ac.uk/aerep/
+#
+Protocol section
+$EDF_PROTOCOLACCESSION\t$EDF_PROTOCOLNAME\t$EDF_PROTOCOLTEXT
+GROWTH\tGrowth\t<your growth protocol text here>
+TREATMENT\tTreatment\t<your treatment protocol text here>
+EXTRACTION\tExtraction\t<your nucleic acid extraction protocol text here>
+LABELING\tLabeling\t<your labeling protocol text here>
+PROTHEADER
+
+    if ( $is_affymetrix ) {
+        $template .= <<"PROTAFFY";
+# For Affymetrix experiments you do not need to provide hybridization or scanning
+# protocols. If you are supplying CHP files, you do not need to enter a normalization
+# protocol.
+NORMALIZATION\tNormalization\t<your normalization protocol text here>
+PROTAFFY
+    }
+    else {
+        $template .= <<"PROTDYESWAP";
+HYBRIDIZATION\tHybridization\t<your hybridization protocol text here>
+SCANNING\tScanning\t<your scanning protocol text here>
+NORMALIZATION\tNormalization\t<your normalization protocol text here>
+PROTDYESWAP
+    }
+    $template .= <<"PROTEND";
+TRANSFORMATION\tTransformation\t<your final data transformation protocol text here>
+# Add any other protocols here...
+PROTEND
+
+    return $template;
+}
+
+sub _retrieve_factorvalues : PRIVATE {
+
+    my ( $self ) = @_;
+
+    # Retrieve the factor values for the experiment. FIXME consider
+    # caching these?
+
+    my $experiment = $self->get_experiment();
+
+    # FactorValues. The factors are derived only from the design.  We
+    # go round the houses here because Class::DBI many-to-many sucks.
+    my @fv_categories;
+    my @design_categories = map { $_->categories() } $experiment->designs();
+    foreach my $cat ( @design_categories ) {
+	push(@fv_categories, $cat) if $cat->is_fv();
+    }
+    my %factorvalue = map { $_ => 1 }
+          map { $_->ontology_term() } @fv_categories;
+
+    return [ keys %factorvalue ];
+}
+
+sub _retrieve_bmcs : PRIVATE {
+
+    my ( $self ) = @_;
+
+    # Retrieve the BioMaterialCharacteristics for the
+    # experiment. FIXME consider caching these?
+
+    my $experiment = $self->get_experiment();
+
+    # BioMaterialCharacteristics: Designs.
+    my @all_categories;
+    push( @all_categories,
+          map { $_->categories() } $experiment->designs() );
+
+    # BioMaterialCharacteristics: Materials.
+    push( @all_categories,
+          map { $_->categories() } $experiment->materials() );
+
+    # BioMaterialCharacteristics: Organisms.
+    push( @all_categories,
+          map { $_->categories() }
+          map { $_->taxon_id() } $experiment->organisms() );
+
+    # Filter out the non-BMC categories.
+    my @bmc_categories;
+    foreach my $cat ( @all_categories ) {
+	push(@bmc_categories, $cat) if $cat->is_bmc();
+    }
+
+    # BioMaterialCharacteristics: Common categories
+    push(
+	@bmc_categories,
+	    ArrayExpress::AutoSubmission::DB::Category->search(
+		is_common => 1,
+	    )
+	);
+
+    # Unique the BMC categories.
+    my %bmc =
+	map { $_->ontology_term() => 1 } @bmc_categories;
+
+    return [ keys %bmc ];
+}
+
+sub _get_tab2mage_hyb_terms : PRIVATE {
+
+    my ( $self ) = @_;
+
+    my $experiment    = $self->get_experiment();
+    my $is_affymetrix = $experiment->is_affymetrix();
+
+    my $factorvalues    = $self->_retrieve_factorvalues();
+    my $characteristics = $self->_retrieve_bmcs();
+
+    # Custom help and example texts for BioMaterialCharacteristics.
+    my %bmc_help = (
+	'Organism' => 'Sample latin species name',
+	'Age'      => 'Sample Age value, e.g. 20 years',
+    );
+    my $example_organism = ($experiment->organisms())[0];
+    my $example_material = ($experiment->materials())[0];
+    my %bmc_example = (
+	'Organism' => $example_organism ? $example_organism->scientific_name() : q{},
+    );
+
+    # Here we define the basic structure of the template.
+    my @terms = (
+        { 'BioSource'         => {
+	    usage   => $ALL,
+	    help    => 'Unique IDs for biosources (experimental starting materials)',
+	    example => 'Cell culture 1',
+	} },
+        { 'BioSourceMaterial' => {
+	    usage   => $ALL,
+	    help    => 'MaterialType term from MGED ontology',
+	    example => $example_material ? $example_material->ontology_value() : q{},
+	} },
+        (
+            map {
+		my $charcol = {
+		    "BioMaterialCharacteristics[$_]" =>
+			{
+			    usage   => $ALL,
+			    help    => $bmc_help{$_}    || "Sample $_ value",
+			    example => $bmc_example{$_} || q{},
+			}
+		    };
+
+		# Some Characteristics are measurements, not OE values. Return
+		# value determined here:
+		($_ =~ m/\A Time|Age \z/ixms) ? ( $charcol,
+			{
+			    'BioMaterialCharacteristics[TimeUnit]' => {
+				usage   => $ALL,
+				help    => 'Time units for Characteristic',
+				example => 'hours',
+			    }
+			}
+		    )
+	      : ($_ =~ m/\A Dose \z/ixms) ? ( $charcol,
+		      {
+			  'BioMaterialCharacteristics[ConcentrationUnit]' => {
+			      usage   => $ALL,
+			      help    => 'Concentration units for Characteristic',
+			      example => 'mg_per_ml',
+			  }
+		      }
+		  )
+	       : $charcol;
+	    } @$characteristics
+        ),
+        { 'Protocol[grow]'              => {
+	    usage   => $ALL,
+	    help    => 'Growth protocol, where applicable',
+	    example => 'GROWTH',
+	} },
+        { 'Protocol[treatment]'         => {
+	    usage   => $ALL,
+	    help    => 'Sample treatment protocol',
+	    example => 'TREATMENT',
+	} },
+        { 'Sample'                      => {
+	    usage   => $DYESWAP,
+	    help    => 'Unique IDs for samples (after growth and treatment)',
+	    example => 'Cell pellet 1',
+	} },
+        { 'Protocol[extraction]'        => {
+	    usage   => $ALL,
+	    help    => 'RNA/DNA extraction',
+	    example => 'EXTRACTION',
+	} },
+        { 'Extract'                     => {
+	    usage   => $DYESWAP,
+	    help    => 'Unique IDs for extracts (RNA or DNA)',
+	    example => 'RNA Extract 1',
+	} },
+        { 'Protocol[immunoprecipitate]' => {
+	    usage   => $CHIPCHIP,
+	    help    => 'ChIP protocol',
+	} },
+        { 'Immunoprecipitate'           => {
+	    usage   => $CHIPCHIP,
+	    help    => 'Unique IDs for IPs',
+	    example => 'Immunoprecipitate 1'
+	} },
+        { 'Protocol[labeling]'          => {
+	    usage   => $ALL,
+	    help    => 'RNA/DNA labeling',
+	    example => 'LABELING',
+	} },
+        { 'LabeledExtract'              => {
+	    usage   => $DYESWAP,
+	    help    => 'Unique IDs for labeled extracts',
+	    example => 'Labeled Extract 1.' . $is_affymetrix ? 'biotin' : 'Cy3',
+	} },
+        { 'Dye'                         => {
+	    usage   => $DYESWAP,
+	    help    => 'Labeling dye, e.g. Cy3, biotin, 33P',
+	    example => $is_affymetrix ? 'biotin' : 'Cy3',
+	} },
+        { 'Protocol[hybridization]'     => {
+	    usage   => $DYESWAP,
+	    help    => 'Hyb protocol',
+	    example => 'HYBRIDIZATION',
+	} },
+        { 'Hybridization'               => {
+	    usage   => $ALL,
+	    help    => 'Unique IDs for hybs',
+	    example => 'Hybridization 1',
+	} },
+        (
+            map {
+		# Some FVs are measurements, not OE values:
+		{ ( ($_ =~ m/\A Time|Age|Dose \z/ixms)
+		      ? "FactorValue[$_(units)]" : "FactorValue[$_]") =>
+		      {
+			  usage   => $ALL,
+			  help    => $bmc_help{$_}    || "Value for $_ variable",
+			  example => $bmc_example{$_} || q{},
+		      }
+		  }
+	    }
+		@$factorvalues
+        ),
+        { 'Protocol[scanning]'       => {
+	    usage   => $DYESWAP,
+	    help    => 'Image acquisition',
+	    example => 'SCANNING',
+	} },
+        { 'Protocol[normalization]'  => {
+	    usage   => $DYESWAP,
+	    help    => 'Data normalization',
+	    example => 'NORMALIZATION',
+	} },
+        { 'Array[accession]'         => {
+	    usage   => $ALL,
+	    help    => 'ArrayExpress array accession, e.g. A-MEXP-27',
+	} },
+        { 'Array[serial]'            => {
+	    usage   => $ALL,
+	    help    => 'Array serial number/chip lot',
+	} },
+        { 'File[raw]'                => {
+	    usage   => $ALL,
+	    help    => 'Unprocessed data file, e.g. CEL, GPR',
+	} },
+        { 'File[normalized]'         => {
+	    usage   => $ALL,
+	    help    => 'Normalized data',
+	} },
+        { 'File[exp]'                => {
+	    usage   => $AFFY,
+	    help    => 'EXP file where available',
+	} },
+        { 'File[cdf]'                => {
+	    usage   => $AFFY,
+	    help    => 'CDF library file (do not upload CDF unless custom array used)',
+	} },
+        { 'Protocol[transformation]' => {
+	    usage   => $ALL,
+	    help    => 'Protocol producing final data matrix file',
+	    example => 'TRANSFORMATION',
+	} },
+        { 'File[transformed]'        => {
+	    usage   => $ALL,
+	    help    => 'Final data matrix file(s)',
+	} },
+
+    );
+
+    return \@terms;
+}
+
+sub _table_from_terms : PRIVATE {
+
+    my ( $self, $terms ) = @_;
+
+    my $experiment    = $self->get_experiment();
+    my $is_affymetrix = $experiment->is_affymetrix();
+
+    my %designs_include =
+      map { $_->ontology_value() => 1 } $experiment->designs();
+    my (@headings, @explanations, @examples);
+
+    foreach my $term ( @{ $terms } ) {
+        while ( my ( $name, $termhash ) = each %$term ) {
+            if (
+                   ( $termhash->{'usage'} & $ALL )
+                || ( $termhash->{'usage'} & $DYESWAP && !$is_affymetrix )
+                || ( $termhash->{'usage'} & $AFFY    && $is_affymetrix )
+                || (   $termhash->{'usage'} & $CHIPCHIP
+                    && $designs_include{$OE_VAL_CHIP_CHIP} )
+              )
+            {
+                push( @headings,     $name );
+                push( @explanations, "# $termhash->{'help'}" || q{} );
+                push( @examples,     $termhash->{'example'}  || q{} );
+            }
+        }
+    }
+
+    my $template = join( "\t", @headings )     . "\n"
+	         . join( "\t", @explanations ) . "\n"
+		 . join( "\t", @examples )     . "\n";
+
+    return $template;
+}
+
+sub _tab2mage_hyb_section : PRIVATE {
+
+    my ( $self ) = @_;
+
+    my $template = <<"HYBHEADER";
+
+# The Hybridization section contains all the information linking your samples to your
+# data files. Please feel free to add as many new BioMaterialCharacteristics[] or
+# FactorValue[] columns as necessary to completely describe your experiment. See this
+# web page for help with supported data file formats:
+#   http://tab2mage.sourceforge.net/docs/datafiles.html
+#
+# The first line below has been filled in with example terms, where possible:
+Hybridization section
+HYBHEADER
+
+    # Get the tab2mage-specific hyb section template data.
+    my $terms = $self->_get_tab2mage_hyb_terms();
+
+    # Generate the table.
+    $template .= $self->_table_from_terms( $terms );
+
+    return $template;
+}
+
+sub tab2mage {
+
+    my ( $self ) = @_;
+
+    # Build the spreadsheet.
+    # Also incorporate simplified Affy form.
+    # FIXME to use the terms from MAGE::Definitions.
+    my $template = $self->_tab2mage_expt_section()
+  	         . $self->_tab2mage_prot_section()
+	         . $self->_tab2mage_hyb_section();
+
+    # Return the spreadsheet as a string.
+    return $template;
+}
+
+sub _stylize_factor_name : PRIVATE {
+
+    my ( $self, $category ) = @_;
+
+    return uc($category);
+}
+
+sub _magetab_idf_factorvalue : PRIVATE {
+
+    my ( $self ) = @_;
+    
+    my $factorvalues    = $self->_retrieve_factorvalues();
+
+    my ( @factornames, @factortypes, @factortermsources );
+
+    # Note that the factorname convention below must be matched in the SDRF.
+    foreach my $category ( @$factorvalues ) {
+	push @factornames, $self->_stylize_factor_name($category);
+
+	# Switch from CamelCase category to underscore_word value
+	push @factortypes, decamelize($category);
+	push @factortermsources, $MGED_ONTOLOGY;
+    }
+    my $template = join("\t", 'Experimental Factor Name', @factornames) . "\n"
+	         . join("\t", 'Experimental Factor Type', @factortypes) . "\n";
+
+    return $template;
+}
+
+sub _magetab_idf_protocols : PRIVATE {
+
+    my ( $self ) = @_;
+
+    my $experiment = $self->get_experiment();
+    my $is_affymetrix = $experiment->is_affymetrix();
+
+    my @protocolnames = qw(
+			   GROWTH
+			   TREATMENT
+			   EXTRACTION
+			   LABELING
+		       );
+    my @protocoltypes = qw(
+			   grow
+			   specified_biomaterial_action
+			   nucleic_acid_extraction
+			   labeling
+		       );
+    my @protocoldescs = (
+	q{<your growth protocol text here>},
+	q{<your treatment protocol text here>},
+	q{<your nucleic acid extraction protocol text here>},
+	q{<your labeling protocol text here>},
+    );
+    my @protocoltermsources = (
+	$MGED_ONTOLOGY,
+	$MGED_ONTOLOGY,
+	$MGED_ONTOLOGY,
+	$MGED_ONTOLOGY,
+    );
+
+    # These will usually be automatically derived from CEL and EXP for
+    # affy subs:
+
+    unless ($is_affymetrix) {
+	push @protocolnames, qw(HYBRIDIZATION SCANNING);
+	push @protocoltypes, qw(hybridization image_acquisition);
+	push @protocoldescs, (
+	    q{<your hybridization protocol text here>},
+	    q{<your scanning protocol text here>},
+	);
+	push @protocoltermsources, (
+	    $MGED_ONTOLOGY,
+	    $MGED_ONTOLOGY,
+	);
+    }
+
+    push @protocolnames, qw(NORMALIZATION TRANSFORMATION);
+    push @protocoltypes, qw(bioassay_data_transformation bioassay_data_transformation);
+    push @protocoldescs, (
+	q{<your normalization protocol text here>},
+	q{<your transformation protocol text here>},
+    );
+    push @protocoltermsources, (
+	q{},
+	q{},
+    );
+
+    my $template = join("\t", 'Protocol Name',            @protocolnames      ) . "\n"
+	         . join("\t", 'Protocol Type',            @protocoltypes      ) . "\n"
+	         . join("\t", 'Protocol Description',     @protocoldescs      ) . "\n";
+
+    return $template;
+}
+
+sub _magetab_idf : PRIVATE {
+
+    my ( $self ) = @_;
+
+    my $experiment = $self->get_experiment();
+
+    my $experiment_name = $experiment->name();
+    my $experiment_type = $experiment->experiment_type();
+    my $submitter       = $experiment->user_id()->name();
+    my $submitter_email = $experiment->user_id()->email();
+
+    my $url  = $self->get_url()  || q{};
+    my $date = $self->get_date() || q{};
+    
+    my $design_string =
+      join( "\t", map { $_->ontology_value() } $experiment->designs() );
+
+    my $template = <<"EXPTSECTIONSTART";
+#
+# $experiment_type Submission '$experiment_name', generated at $date
+#
+# Please save this page to disk, and edit it using any spreadsheet program (e.g.,
+# Microsoft Excel, OpenOffice.org Calc). This file can be imported into either of
+# these applications as a tab-delimited text file. It is highly recommended that
+# you set the cell format to plain text throughout the spreadsheet, to avoid
+# 'helpful' changes made by the spreadsheet application. Once you have completed
+# this spreadsheet please upload it using the submissions webpage alongside your
+# data files:
+#    $url
+#
+# Please see http://tab2mage.sourceforge.net/docs/magetab_subs.html for
+# information on filling in the IDF and SDRF sections below.
+#
+# All lines beginning with the '#' character are treated as comments and ignored.
+
+
+# This section contains the top-level information for your experiment.
+Investigation Title\t$experiment_name
+Experiment Description\t
+Experimental Design\t$design_string
+
+# Please create as many Experimental Factors here as you need to
+# describe the variables investigated by your experiment.
+EXPTSECTIONSTART
+
+    $template .= $self->_magetab_idf_factorvalue();
+
+    $template .= <<"EXPTSECTIONMID";
+
+# Quality Control Type examples: dye_swap_quality_control, biological_replicate, technical_replicate
+Quality Control Type\t
+
+# Dates should be entered in the form YYYY-MM-DD. If you are using MS Excel,
+# it is recommended that you set your spreadsheet to 'text' format throughout
+# to help avoid any unwanted changes made by Excel.
+Public Release Date\t
+
+# Please list contact details in columns, below:
+Person Last Name\t$submitter\t
+Person First Name\t\t
+Person Mid Initials\t\t
+Person Email\t$submitter_email\t
+Person Phone\t\t
+Person Address\t\t
+Person Affiliation\t\t
+Person Roles\tsubmitter\t
+
+# Please list all publications associated with this submission, in columns:
+PubMed ID\t
+Publication Author List\t
+Publication Title\t
+Publication Status\t
+
+# The next section describes the protocols used in your
+# experiment. Please select your own Protocol Names (e.g. 'GROWTH',
+# 'TREATMENT') for use within this spreadsheet, or re-use ArrayExpress
+# protocols by using the assigned ArrayExpress protocol accession
+# numbers in your SDRF. If you have already submitted protocols to
+# ArrayExpress, you can retrieve these using this web page:
+#    http://www.ebi.ac.uk/aerep/
+EXPTSECTIONMID
+
+    $template .= $self->_magetab_idf_protocols();
+
+    return $template;
+}
+
+sub _magetab_sdrf : PRIVATE {
+
+    my ( $self ) = @_;
+
+    my $experiment = $self->get_experiment();
+
+    my $template = <<"HYBHEADER";
+# The SDRF section contains all the information linking your samples
+# to your data files. Please feel free to add as many new
+# Characteristics[] or FactorValue[] columns as necessary to
+# completely describe your experiment. See this web page for help with
+# supported data file formats:
+#   http://tab2mage.sourceforge.net/docs/datafiles.html
+#
+# The first line below has been filled in with example terms, where possible:
+HYBHEADER
+
+    # Get the magetab-specific hyb section template data.
+    my $terms = $self->_get_magetab_hyb_terms();
+
+    # Generate the table.
+    $template .= $self->_table_from_terms( $terms );
+
+    return $template;
+}
+
+sub _get_magetab_hyb_terms : PRIVATE {
+
+    my ( $self ) = @_;
+
+    my $experiment    = $self->get_experiment();
+    my $is_affymetrix = $experiment->is_affymetrix();
+
+    my $factorvalues    = $self->_retrieve_factorvalues();
+    my $characteristics = $self->_retrieve_bmcs();
+
+    # Custom help and example texts for BioMaterialCharacteristics.
+    my %bmc_help = (
+	'Organism' => 'Sample latin species name',
+	'Age'      => 'Sample Age value, e.g. 20 years',
+    );
+    my $example_organism = ($experiment->organisms())[0];
+    my $example_material = ($experiment->materials())[0];
+    my %bmc_example = (
+	'Organism' => $example_organism ? $example_organism->scientific_name() : q{},
+    );
+
+    # Here we define the basic structure of the template.
+    my @terms = (
+        { 'Source Name' => {
+	    usage   => $ALL,
+	    help    => 'Unique IDs for biosources (experimental starting materials)',
+	    example => 'Cell culture 1',
+	} },
+        { 'Material Type' => {
+	    usage   => $ALL,
+	    help    => 'MaterialType term from MGED ontology',
+	    example => $example_material ? $example_material->ontology_value() : q{},
+	} },
+        (
+            map {
+		my $charcol = {
+		    "Characteristics[$_]" =>
+			{
+			    usage   => $ALL,
+			    help    => $bmc_help{$_}    || "Sample $_ value",
+			    example => $bmc_example{$_} || q{},
+			}
+		    };
+
+		# Some Characteristics are measurements, not OE values. Return
+		# value determined here:
+		($_ =~ m/\A Time|Age \z/ixms) ? ( $charcol,
+			{
+			    'Unit[TimeUnit]' => {
+				usage   => $ALL,
+				help    => 'Time units for Characteristic',
+				example => 'hours',
+			    }
+			}
+		    )
+	      : ($_ =~ m/\A Dose \z/ixms) ? ( $charcol,
+		      {
+			  'Unit[ConcentrationUnit]' => {
+			      usage   => $ALL,
+			      help    => 'Concentration units for Characteristic',
+			      example => 'mg_per_ml',
+			  }
+		      }
+		  )
+	       : $charcol;
+	    } @$characteristics
+        ),
+        { 'Protocol REF' => {
+	    usage   => $ALL,
+	    help    => 'Growth protocol, where applicable',
+	    example => 'GROWTH',
+	} },
+        { 'Protocol REF' => {
+	    usage   => $ALL,
+	    help    => 'Sample treatment protocol',
+	    example => 'TREATMENT',
+	} },
+        { 'Sample Name' => {
+	    usage   => $DYESWAP,
+	    help    => 'Unique IDs for samples (after growth and treatment)',
+	    example => 'Cell pellet 1',
+	} },
+        { 'Protocol REF' => {
+	    usage   => $ALL,
+	    help    => 'RNA/DNA extraction',
+	    example => 'EXTRACTION',
+	} },
+        { 'Extract Name' => {   # all dye-swap except ChIP-chip.
+	    usage   => ($DYESWAP & ~ $CHIPCHIP),
+	    help    => 'Unique IDs for extracts (RNA or DNA)',
+	    example => 'RNA Extract 1',
+	} },
+        { 'Sample Name' => {
+	    usage   => $CHIPCHIP,
+	    help    => 'Unique IDs for extracts (RNA or DNA)',
+	    example => 'RNA Extract 1',
+	} },
+        { 'Protocol REF' => {
+	    usage   => $CHIPCHIP,
+	    help    => 'ChIP protocol',
+	} },
+        { 'Extract Name' => {
+	    usage   => $CHIPCHIP,
+	    help    => 'Unique IDs for IPs',
+	    example => 'Immunoprecipitate 1'
+	} },
+        { 'Protocol REF' => {
+	    usage   => $ALL,
+	    help    => 'RNA/DNA labeling',
+	    example => 'LABELING',
+	} },
+        { 'Labeled Extract Name' => {
+	    usage   => $ALL,
+	    help    => 'Unique IDs for labeled extracts',
+	    example => 'Labeled Extract 1.' . ($is_affymetrix ? 'biotin' : 'Cy3'),
+	} },
+        { 'Label' => {
+	    usage   => $ALL,
+	    help    => 'Labeling dye, e.g. Cy3, biotin, 33P',
+	    example => ($is_affymetrix ? 'biotin' : 'Cy3'),
+	} },
+        { 'Protocol REF' => {
+	    usage   => ($DYESWAP | $CHIPCHIP),
+	    help    => 'Hyb protocol',
+	    example => 'HYBRIDIZATION',
+	} },
+        { 'Hybridization Name' => {
+	    usage   => $ALL,
+	    help    => 'Unique IDs for hybs',
+	    example => 'Hybridization 1',
+	} },
+        { 'Array Design REF' => {
+	    usage   => $ALL,
+	    help    => 'ArrayExpress array accession, e.g. A-MEXP-27',
+	} },
+        { 'Comment[Array serial number]' => {
+	    usage   => $ALL,
+	    help    => 'Array serial number/chip lot',
+	} },
+        { 'Protocol REF' => {
+	    usage   => ($DYESWAP | $CHIPCHIP),
+	    help    => 'Image acquisition',
+	    example => 'SCANNING',
+	} },
+        { 'Scan Name' => {
+	    usage   => ($DYESWAP | $CHIPCHIP),
+	    help    => 'Unique IDs for scans',
+	    example => 'Scan 1',
+	} },
+        { 'Array Data File' => {
+	    usage   => $ALL,
+	    help    => 'Unprocessed data file, e.g. CEL, GPR',
+	} },
+        { 'Comment[EXP]' => {
+	    usage   => $AFFY,
+	    help    => 'EXP file where available',
+	} },
+        { 'Protocol REF' => {
+	    usage   => $ALL,
+	    help    => 'Data normalization',
+	    example => 'NORMALIZATION',
+	} },
+        { 'Derived Array Data File' => {
+	    usage   => $ALL,
+	    help    => 'Normalized data',
+	} },
+        { 'Protocol REF' => {
+	    usage   => $ALL,
+	    help    => 'Protocol producing final data matrix file',
+	    example => 'TRANSFORMATION',
+	} },
+        { 'Derived Array Data Matrix File' => {
+	    usage   => $ALL,
+	    help    => 'Final data matrix file(s)',
+	} },
+        { 'Comment[CDF]' => {
+	    usage   => $AFFY,
+	    help    => 'CDF library file (do not upload CDF unless custom array used)',
+	} },
+        (
+            map {
+
+		# $fvname convention must match that in the IDF.
+		my $fvname = $self->_stylize_factor_name($_);
+		my $fvcol = {
+		    "Factor Value[$fvname]" => {
+			usage   => $ALL,
+			help    => $bmc_help{$_}    || "Value for $fvname Experimental Factor",
+			example => $bmc_example{$_} || q{},
+		    }
+		};
+
+		# Some FVs are measurements, not OE values. Return
+		# value determined here:
+		($_ =~ m/\A Time|Age \z/ixms) ? ( $fvcol,
+			{
+			    'Unit[TimeUnit]' => {
+				usage   => $ALL,
+				help    => 'Time units for Factor Value',
+				example => 'hours',
+			    }
+			}
+		    )
+	      : ($_ =~ m/\A Dose \z/ixms) ? ( $fvcol,
+		      {
+			  'Unit[ConcentrationUnit]' => {
+			      usage   => $ALL,
+			      help    => 'Concentration units for Factor Value',
+			      example => 'mg_per_ml',
+			  }
+		      }
+		  )
+	       : $fvcol;
+	    }
+		@$factorvalues
+        ),
+
+    );
+
+    return \@terms;
+}
+
+sub magetab {
+
+    my ( $self ) = @_;
+
+    my $template = "#####################################################\n[IDF]\n"
+	         . $self->_magetab_idf()
+		 . "\n#####################################################\n[SDRF]\n"
+	         . $self->_magetab_sdrf();
+
+    return $template;
+}
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../index.html">
+                <img src="../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: Spreadsheet.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::AutoSubmission::Spreadsheet - A Tab2MAGE/MAGE-TAB
+document templating class.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::AutoSubmission::Spreadsheet;
+ my $ss = ArrayExpress::AutoSubmission::Spreadsheet->new({
+     experiment => $expt,
+     url        => $self->query()->url(),
+     date       => date_now(),
+ });
+
+ $ss_content = $spreadsheet->magetab();
+
+=head1 DESCRIPTION
+
+This module provides Tab2MAGE and MAGE-TAB document templating
+functions. The functions take Class::DBI experiment objects from the
+tracking database and generate the appropriate output strings which
+can then be printed to file.
+
+=head1 METHODS
+
+=over 2
+
+=item C<new>
+
+Object constructor, taking the following arguments:
+
+=over 4
+
+=item C<experiment>
+
+The Class::DBI experiment object to use in template generation.
+
+=item C<url>
+
+The URL of the page to which completed templates should be submitted
+(this is embedded in the template for convenience).
+
+=item C<date>
+
+The date of template generation.
+
+=back
+
+=item C<magetab>
+
+Return a string representing the MAGE-TAB template for this experiment.
+
+=item C<tab2mage>
+
+Return a string representing the Tab2MAGE template for this experiment.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2008.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments. 
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+1;
+
diff --git a/lib/ArrayExpress/AutoSubmission/WebForm.pm b/lib/ArrayExpress/AutoSubmission/WebForm.pm
new file mode 100644
index 0000000..c779aff
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/WebForm.pm
@@ -0,0 +1,1601 @@
+#!/usr/bin/env perl
+#
+# Module used to generate and control web submission forms
+#
+# Tim Rayner 2006, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: WebForm.pm 2069 2008-06-04 14:33:52Z tfrayner $
+#
+
+package ArrayExpress::AutoSubmission::WebForm;
+
+use strict;
+use warnings;
+
+use base 'CGI::Application';
+use CGI::Application::Plugin::Authentication;
+use CGI::Application::Plugin::Session;
+use CGI::Application::Plugin::ValidateRM qw(check_rm);
+use CGI::Upload;
+use Data::FormValidator::Constraints qw(:closures);
+use HTTP::BrowserDetect;
+use MIME::Lite;
+
+require ArrayExpress::AutoSubmission::DB::User;
+require ArrayExpress::AutoSubmission::DB::Experiment;
+require ArrayExpress::AutoSubmission::DB::Design;
+require ArrayExpress::AutoSubmission::DB::Material;
+require ArrayExpress::AutoSubmission::DB::Organism;
+require ArrayExpress::AutoSubmission::DB::Role;
+require ArrayExpress::AutoSubmission::DB::RoleUser;
+require ArrayExpress::AutoSubmission::DB::DataFile;
+require ArrayExpress::AutoSubmission::DB::Spreadsheet;
+
+require ArrayExpress::AutoSubmission::Spreadsheet;
+use ArrayExpress::Curator::Common qw(mage_date date_now untaint);
+use ArrayExpress::Curator::Config qw($CONFIG);
+use File::Spec;
+use File::Path;
+use File::Copy;
+use File::Basename;
+use IO::File;
+use Readonly;
+use Carp;
+
+# The DevPopup plugin can be commented out in production.
+#use CGI::Application::Plugin::DevPopup;
+#use CGI::Application::Plugin::HtmlTidy;
+
+#################
+# Runmode names #
+#################
+
+# N.B. these terms are used in the web interface.
+Readonly my $RM_INTRO       => 'Introduction';
+Readonly my $RM_SIGNUP      => 'New submitter';
+Readonly my $RM_CREATEUSER  => 'Create user';
+Readonly my $RM_LOGIN       => 'Log in';
+Readonly my $RM_LOGOUT      => 'Log out';
+Readonly my $RM_CHOOSE      => 'Experiment list';
+Readonly my $RM_CREATE      => 'Create new';
+Readonly my $RM_INSERT      => 'Save experiment';
+Readonly my $RM_EDIT        => 'Edit';
+Readonly my $RM_UPDATE      => 'Update';
+Readonly my $RM_DELETE      => 'Delete';
+Readonly my $RM_TEMPLATE    => 'Generate template';
+Readonly my $RM_UPLOAD      => 'Upload files';
+Readonly my $RM_FILE_UPLOAD => 'Upload';
+Readonly my $RM_SUBMIT      => 'Submit experiment';
+Readonly my $RM_ERROR       => 'Error';
+
+Readonly my $ERROR_FORMAT   => q{<span style="color:red;font-weight:bold" class="dfv_errors">- %s</span>};
+
+#########################
+# Package configuration #
+#########################
+
+__PACKAGE__->authen->config(
+    DRIVER => [
+        'CDBI',
+        CLASS => 'ArrayExpress::AutoSubmission::DB::User',
+
+        # Use MD5_hex:password for greater security (this would also
+        # need fixing in the ruby code).
+        FIELD_METHODS => [qw(login password)],
+    ],
+    CREDENTIALS           => [qw(auth_username auth_password)],
+    LOGIN_RUNMODE         => $RM_LOGIN,
+    POST_LOGIN_RUNMODE    => $RM_CHOOSE,
+    POST_LOGIN_CALLBACK   => \&update_user_access,
+    LOGOUT_RUNMODE        => $RM_LOGOUT,
+    LOGIN_SESSION_TIMEOUT => {
+        IDLE_FOR => '60m',
+        EVERY    => '1d'
+    },
+);
+
+__PACKAGE__->authen->protected_runmodes(
+    qw(
+      choose
+      create
+      insert
+      edit
+      update
+      delete_expt
+      template
+      upload
+      file_upload
+      )
+);
+
+sub setup {
+    my $self = shift;
+    $self->start_mode($RM_INTRO);
+    $self->error_mode('error');
+    $self->run_modes(
+        $RM_INTRO       => 'intro',
+        $RM_SIGNUP      => 'signup',
+	$RM_CREATEUSER  => 'create_user',
+        $RM_LOGIN       => 'login',
+        $RM_LOGOUT      => 'logout',
+        $RM_CHOOSE      => 'choose',
+	$RM_CREATE      => 'create',
+	$RM_INSERT      => 'insert',
+        $RM_EDIT        => 'edit',
+        $RM_UPDATE      => 'update',
+	$RM_DELETE      => 'delete_expt',
+        $RM_TEMPLATE    => 'spreadsheet_template',
+        $RM_UPLOAD      => 'upload',
+	$RM_FILE_UPLOAD => 'file_upload',
+	$RM_SUBMIT      => 'submit',
+        $RM_ERROR       => 'error',
+    );
+    $self->mode_param('rm');
+
+    # Templates are kept in a subdirectory within the package, but can
+    # be overridden using the 'template_path' param..
+    my @module_dir_array = File::Spec->splitpath(__FILE__);
+    my $template_path    =
+      File::Spec->catpath( @module_dir_array[ 0, 1 ], 'templates' );
+    $self->tmpl_path($self->param('template_path') || $template_path);
+
+    return;
+}
+
+##################
+# Shared methods #
+##################
+
+sub cgiapp_init {
+
+    my $self = shift;
+
+    # Data::FormValidator defaults.
+    $self->param('dfv_defaults') ||
+	$self->param('dfv_defaults', {
+	    msgs => {
+		any_errors  => 'some_errors',
+		prefix      => 'err_',
+		invalid_separator => ' <br/> ',
+		missing  => q{Missing value(s)},
+		invalid  => q{Invalid value(s)},
+		format   => $ERROR_FORMAT
+		}
+	});
+
+    return;
+}
+
+sub cgiapp_postrun {
+
+    my ($self, $contentref) = @_;
+
+# Uncomment this for production code.
+#  $self->htmltidy_clean($contentref);
+
+    return;
+}
+
+sub _shared_html_params {
+    my $self = shift;
+
+    # Set the HTML::Template params sent to all runmodes.
+    return {
+	CGI_BASE   => $self->param('cgi_base')          || '/tab2mage.cgi',
+        STYLESHEET => $self->param('stylesheet')        || undef,
+	SIDEBAR_ICONS => $self->param('sidebar_icons'),
+	SIDEBAR_LINKS => $self->param('sidebar_links'),
+        RM         => $self->mode_param(),
+	AUTHEN     => $self->authen->is_authenticated() || undef,
+        EXPT_TYPE  => $self->param('experiment_type')   || undef,
+	LOGOUT     => $RM_LOGOUT,
+	CURATOR_EMAIL => $CONFIG->get_AUTOSUBS_CURATOR_EMAIL(),
+    };
+}
+
+sub _load_my_tmpl {
+    my ($self, $tmpl_file, $errors, $params) = @_;
+
+    # Convenience method for generating templates.
+
+    # Filter to allow for dynamic TMPL_INCLUDEs.
+    my $header = $self->param('header_html_template') || '_head.html';
+    my $footer = $self->param('footer_html_template') || '_foot.html';
+    my $filter = sub {
+	my $text_ref = shift;
+	$$text_ref =~ s{<(!--)? *TMPL_INCLUDE *HEADER *(--)?>}
+	               {<!-- TMPL_INCLUDE NAME="$header" -->}i;
+	$$text_ref =~ s{<(!--)? *TMPL_INCLUDE *FOOTER *(--)?>}
+	               {<!-- TMPL_INCLUDE NAME="$footer" -->}i;
+    };
+
+    # Create the actual HTML::Template object.
+    my $template = $self->load_tmpl(
+	$tmpl_file,
+	filter            => $filter,
+	die_on_bad_params => 1,
+    );
+    $template->param($errors) if $errors;
+    $template->param(
+	%{$self->_shared_html_params()},
+	%{$params},
+    );
+
+    return $template;
+}
+
+sub _annotation_labels {
+    my ($self, $experiment) = @_;
+
+    # Get the design, material and organism info from the database.
+
+    # Designs:
+    my %selected_design = $experiment
+	? map { $_->id() => 1 } $experiment->designs()
+	: ();
+    my @biol_design_labels  = map {
+	{ name      => $_->display_label(),
+	  id        => $_->id(),
+	  selected  => $selected_design{$_->id()}}
+    } ArrayExpress::AutoSubmission::DB::Design->search(
+	design_type => 'biological',
+	is_deleted  => 0,
+	{order_by   => 'display_label'},
+    );
+    my @meth_design_labels  = map {
+	{ name      => $_->display_label(),
+	  id        => $_->id(),
+	  selected  => $selected_design{$_->id()}}
+    } ArrayExpress::AutoSubmission::DB::Design->search(
+	design_type => 'methodological',
+	is_deleted  => 0,
+	{order_by   => 'display_label'},
+    );
+    my @tech_design_labels  = map {
+	{ name      => $_->display_label(),
+	  id        => $_->id(),
+	  selected  => $selected_design{$_->id()}}
+    } ArrayExpress::AutoSubmission::DB::Design->search(
+	design_type => 'technological',
+	is_deleted  => 0,
+	{order_by   => 'display_label'},
+    );
+
+    # Materials:
+    my %selected_material = $experiment
+	? map { $_->id() => 1 } $experiment->materials()
+	: ();
+    my @material_labels  = map {
+	{ name      => $_->display_label(),
+	  id        => $_->id(),
+	  selected  => $selected_material{$_->id()}}
+    } ArrayExpress::AutoSubmission::DB::Material->search_like(
+	id          => '%',
+	is_deleted  => 0,
+	{order_by   => 'display_label'},
+    );
+
+    # Organisms (only list those with taxon ids):
+    my %selected_organism = $experiment
+	? map { $_->id() => 1 } $experiment->organisms()
+	: ();
+    my @organism_labels  = map {
+	{ name      => $_->scientific_name(),
+	  id        => $_->id(),
+	  selected  => $selected_organism{$_->id()}}
+    } ArrayExpress::AutoSubmission::DB::Organism->search_like(
+	id          => '%',
+	taxon_id    => '_%',
+	is_deleted  => 0,
+	{order_by   => 'scientific_name'},
+    );
+
+    return {
+	BIOL_DESIGNS     => \@biol_design_labels,
+	METH_DESIGNS     => \@meth_design_labels,
+	TECH_DESIGNS     => \@tech_design_labels,
+	MATERIALS        => \@material_labels,
+	ORGANISMS        => \@organism_labels,
+    };
+}
+
+sub _is_not_duplicate_filename {
+    my ($name, $experiment_id) = @_;
+
+    # Ensure name is stringified - this sub is passed CGI upload
+    # params; they do not stringify correctly with all versions of the
+    # underlying modules.
+    $name = sprintf("%s", $name);
+
+    # Check the database for entries, return false if found.
+    my $spreadsheet
+	= ArrayExpress::AutoSubmission::DB::Spreadsheet->retrieve(
+	    name          => $name,
+	    experiment_id => $experiment_id,
+	    is_deleted    => 0,
+	);
+    my $data_file
+	= ArrayExpress::AutoSubmission::DB::DataFile->retrieve(
+	    name          => $name,
+	    experiment_id => $experiment_id,
+	    is_deleted    => 0,
+	);
+    if ($spreadsheet || $data_file) {
+	return 0;
+    }
+
+    # Check the filesystem, return true if found, false otherwise.
+    my $experiment
+	= ArrayExpress::AutoSubmission::DB::Experiment->retrieve(
+	    id => $experiment_id,
+	);
+    my $subs_dir     = $experiment->filesystem_directory();
+    my $upload_file  = untaint(basename( $name ));
+    my $filesys_test = -e File::Spec->catfile($subs_dir, $upload_file);
+
+    return $filesys_test ? 0 : 1;
+}
+
+################################
+# Data::FormValidater profiles #
+################################
+
+sub _user_profile {
+    my $self = shift;
+
+    return {
+	required           => [qw(username password realname email)],
+	optional           => [qw()],
+	dependency_groups  => {password_group => [qw/password password2/]},
+	filters            => ['trim'],
+	constraints        => {
+	    email    => 'email',
+	    username => [
+		{
+		    constraint => qr/^\w{6,10}$/,
+		},
+		{
+		    name       => 'duplicate_username',
+		    constraint => sub {
+			my $user
+			    = ArrayExpress::AutoSubmission::DB::User->retrieve(
+				login => shift,
+			    );
+			return $user ? 0 : 1;
+		    },
+		},
+	    ],
+	    password => [
+		{
+		    constraint => qr/^\w{6,10}$/,
+		},
+		{
+		    name       => 'password_mismatch',
+		    constraint => sub {
+			my ($password, $password2) = @_;
+			return ($password eq $password2);
+		    },
+		    params => [qw(password password2)],
+		},
+	    ],
+	},
+	msgs => {
+	    constraints => {
+		'duplicate_username'
+		    => q{Username is already taken},
+		'password_mismatch'
+		    => q{Passwords do not match},
+	    },
+	},
+    };
+}
+
+sub _choose_profile {
+    my $self = shift;
+
+    return {
+	required => [qw(experiment)],
+	msgs     => {
+	    missing => q{No experiment selected.},
+	}
+    };
+}
+
+sub _delete_profile {
+    my $self = shift;
+
+    return {
+	required           => [qw(experiment)],
+	constraints        => {
+	    experiment     => [
+		{
+		    name   => 'files_exist',
+		    constraint => sub {
+			my $experiment
+			    = ArrayExpress::AutoSubmission::DB::Experiment->retrieve(shift);
+			my $files = scalar($experiment->data_files(is_deleted => 0))
+			    + scalar($experiment->spreadsheets(is_deleted => 0));
+			return $files ? 0 : 1;
+		    },
+		},
+	    ],
+	},
+	msgs => {
+	    constraints => {
+		'files_exist'
+		    => q{Submission includes uploaded files and cannot be deleted.},
+	    },
+	    missing => q{No experiment selected.},
+	},
+    };
+}
+
+sub _edit_profile {
+    my $self = shift;
+
+    return {
+	required           => [qw(experiment_name)],
+	filters            => ['trim'],
+	dependency_groups  => {annotation_group => [qw/biol_designs meth_designs tech_designs materials organisms/]},
+	constraints        => {
+	    experiment_name => [
+		{
+		    name       => 'duplicate_submission',
+		    constraint => sub {
+			my ($name, $id) = @_;
+			my $experiment
+			    = ArrayExpress::AutoSubmission::DB::Experiment->retrieve(
+				name       => $name,
+				is_deleted => 0,
+			    );
+			# No experiment found, or experiment has our
+			# id (i.e. this is an update).
+			return (!$experiment || $experiment->id() == $id);
+		    },
+		    params => [qw(experiment_name experiment)],
+		},
+	    ],
+	},
+	msgs => {
+	    constraints => {
+		'duplicate_submission'
+		    => q{Experiment name is already taken},
+	    },
+	},
+    };
+}
+
+sub _upload_profile {
+    my $self = shift;
+
+    return {
+	require_some       => {
+	    upload_files => [ 1, qw(data_file spreadsheet) ],
+	},
+    };
+}
+
+sub _submit_profile {
+
+    return {
+	required => [qw(experiment)],
+        constraints => {
+            experiment => [
+                {
+                    name       => 'missing_uploads',
+                    constraint => sub {
+                        my $id = shift;
+                        my $experiment
+                            = ArrayExpress::AutoSubmission::DB::Experiment->retrieve($id)
+				or croak("Internal error: No experiment found in database");
+                        my $allfiles = scalar($experiment->data_files(is_deleted => 0))
+                            && scalar($experiment->spreadsheets(is_deleted => 0));
+                        return $allfiles ? 1 : 0;
+                    },
+                    params     => [qw(experiment)],
+                },
+            ],
+        },
+	msgs     => {
+	    constraints => {
+		'missing_uploads' => q{Some file(s) have not been uploaded yet},
+	    },
+	    missing => q{Internal error: No experiment parameter defined},
+	},
+    };
+}
+
+#############################
+# Other convenience methods #
+#############################
+
+sub update_user_access {
+    my $self = shift;
+
+    return unless ($self->authen->is_authenticated());
+
+    my $user
+	= ArrayExpress::AutoSubmission::DB::User->retrieve(
+	    login => $self->authen->username(),
+	);
+
+    $user->set(
+	access => mage_date($self->authen->last_access()),
+    );
+
+    $user->update();
+
+    return;
+}
+
+sub get_secure_experiment {
+    my ($self, $id) = @_;
+    
+    my $experiment =
+      ArrayExpress::AutoSubmission::DB::Experiment->retrieve($id);
+
+    # Double-check that the experiment does actually belong to the user.
+    unless ( $experiment->user_id->login() eq $self->authen->username() ) {
+        return (undef, $self->error('Attempt to access forbidden page.'));
+    }
+
+    return $experiment;
+};
+
+sub email_confirm_signup {
+
+    # $user is a Class::DBI object.
+    my ($self, $user) = @_;
+
+    # No point trying to send to a null address.
+    return unless $user->email();
+
+    my $template = $self->load_tmpl('email_signup_confirmation.txt');
+    $template->param(
+	TYPE     => $self->param('experiment_type'),
+	USERNAME => $user->login(),
+	PASSWORD => $user->password(),
+	REALNAME => $user->name(),
+	URL      => $self->query()->url(),
+    );
+
+    my $mail = MIME::Lite->new(
+	From     => $CONFIG->get_AUTOSUBS_CURATOR_EMAIL(),
+	To       => $user->email(),
+	Subject  => 'Welcome to ArrayExpress '
+	    . $self->param('experiment_type') . ' Submissions',
+	Encoding => 'quoted-printable',
+	Data     => $template->output,
+	Type     => 'text/plain',
+    );
+
+    # Must use SMTP if running under CGI::Application::Server
+    if ( my $server = $CONFIG->get_AUTOSUBS_SMTP_SERVER() ) {
+	$mail->send('smtp', $server)
+	    or die("Error sending signup email: $!");
+    }
+    else {
+	$mail->send()
+	    or die("Error sending signup email: $!");
+    }
+
+    return;
+}
+
+sub email_confirm_submission {
+    my ($self, $experiment) = @_;
+
+    # No point trying to send to a null address.
+    return unless $experiment->user_id()->email();
+
+    # $experiment is a Class::DBI object.
+    my $template = $self->load_tmpl('email_subs_confirmation.txt');
+    $template->param(
+	TYPE          => $self->param('experiment_type'),
+	EXPTNAME      => $experiment->name(),
+	REALNAME      => $experiment->user_id()->name(),
+	SUBSDATE      => $experiment->date_submitted(),
+	CURATOR_EMAIL => $CONFIG->get_AUTOSUBS_CURATOR_EMAIL(),
+    );
+
+    my $submitter_mail = MIME::Lite->new(
+	From     => $CONFIG->get_AUTOSUBS_CURATOR_EMAIL(),
+	To       => $experiment->user_id()->email(),
+	Subject  => 'Confirming '
+	    . $self->param('experiment_type')
+		. ' submission: '
+		    . $experiment->name(),
+	Encoding => 'quoted-printable',
+	Data     => $template->output,
+	Type     => 'text/plain',
+    );
+
+    my $curator_tmpl = $self->load_tmpl('subs_notify_curator.txt');
+    $curator_tmpl->param(
+	TYPE          => $self->param('experiment_type'),
+	EXPTNAME      => $experiment->name(),
+	EXPTDIR       => $experiment->filesystem_directory(),
+	REALNAME      => $experiment->user_id()->name(),
+	LOGIN         => $experiment->user_id()->login(),
+	SUBSDATE      => $experiment->date_submitted(),
+	SUBMTR_EMAIL  => $experiment->user_id()->email(),
+    );
+
+    # Check whether this is a resubmission, and set the subject line
+    # appropriately.
+    my $subject;
+    if ( $experiment->num_submissions() ) {
+	$subject = sprintf(
+	    "%s RESUBMISSION: %s",
+	    $self->param('experiment_type'),
+	    $experiment->name(),
+	);
+    }
+    else {
+	$subject = sprintf(
+	    "New %s submission: %s",
+	    $self->param('experiment_type'),
+	    $experiment->name(),
+	);
+    }
+    my $curator_mail = MIME::Lite->new(
+	From     => $CONFIG->get_AUTOSUBS_ADMIN(),
+	To       => $CONFIG->get_AUTOSUBS_CURATOR_EMAIL(),
+	Subject  => $subject,
+	Encoding => 'quoted-printable',
+	Data     => $curator_tmpl->output,
+	Type     => 'text/plain',
+    );
+
+    # Must use SMTP if running under CGI::Application::Server
+    if ( my $server = $CONFIG->get_AUTOSUBS_SMTP_SERVER() ) {
+	foreach my $missive ($submitter_mail, $curator_mail) {
+	    $missive->send('smtp', $server)
+		or die("Error sending signup email: $!");
+	}
+    }
+    else {
+	foreach my $missive ($submitter_mail, $curator_mail) {
+	    $missive->send()
+		or die("Error sending signup email: $!");
+	}
+    }
+
+    return;
+}
+
+###################
+# Runmode methods #
+###################
+
+sub intro {
+    my ($self, $errors) = @_;
+
+    my $template = $self->_load_my_tmpl(
+	$self->param('intro_html_template') || 'intro.html',
+	$errors,
+	{
+	    TITLE      => $self->param('experiment_type')
+	                . ' ArrayExpress submissions',
+	    LOGIN      => $RM_LOGIN,
+	    SIGNUP     => $RM_SIGNUP,
+	},
+    );
+
+    return $template->output();
+}
+
+sub signup {
+    my ($self, $errors) = @_;
+
+    my $template = $self->_load_my_tmpl(
+	$self->param('signup_html_template') || 'signup.html',
+	$errors,
+	{
+	    TITLE      => 'Create new account',
+	    USERNAME   => 'username',
+	    PASSWORD   => 'password',
+	    PASSWORD2  => 'password2',
+	    REALNAME   => 'realname',
+	    EMAIL      => 'email',
+	    CREATEUSER => $RM_CREATEUSER,
+	},
+    );
+
+    return $template->output();
+}
+
+sub create_user {
+    my ($self, $errors) = @_;
+
+    # Validate the input.
+    my ($results, $err_page) =
+	$self->check_rm('signup', '_user_profile');
+    return $err_page if $err_page;
+
+    # Get CGI query object.
+    my $q = $self->query();
+
+    # FIXME we also need a confirmation email sent to the user. FIXME
+    # also strip surrounding whitespace.
+    my $user
+	= ArrayExpress::AutoSubmission::DB::User->insert({
+	login      => $q->param('username'),
+	password   => $q->param('password'),
+	name       => $q->param('realname'),
+	email      => $q->param('email'),
+	created_at => date_now(),
+	is_deleted => 0,
+    });
+
+    # All users signing up here are just submitters.
+    my $role
+	= ArrayExpress::AutoSubmission::DB::Role->retrieve(
+	    name => 'submitter',
+	);
+    ArrayExpress::AutoSubmission::DB::RoleUser->insert({
+	role_id => $role,
+	user_id => $user,
+    });
+
+    $self->email_confirm_signup($user);
+
+    my $template = $self->_load_my_tmpl(
+	$self->param('create_user_html_template') || 'create_user.html',
+	$errors,
+	{
+	    TITLE      => 'User successfully created',
+	    USERNAME   => 'auth_username',
+	    PASSWORD   => 'auth_password',
+	    USER       => $user->login(),
+	    PASS       => $user->password(),
+	    REAL       => $user->name(),
+	    EMAIL      => $user->email(),
+	    CHOOSE     => $RM_CHOOSE,
+	},
+    );
+
+    return $template->output();
+}
+
+sub login {
+    my ($self, $errors) = @_;
+
+    # Get CGI query object
+    my $q = $self->query();
+
+    # N.B. CGI 'login' param should only be set if the previous login
+    # failed.
+    my $template = $self->_load_my_tmpl(
+	$self->param('login_html_template') || 'login.html',
+	$errors,
+	{
+	    TITLE => 'Log in to '
+		. $self->param('experiment_type')
+		. ' submissions',
+	    LOGIN      => $RM_LOGIN,
+	    SIGNUP     => $RM_SIGNUP,
+	    USERNAME   => 'auth_username',
+	    PASSWORD   => 'auth_password',
+	    FAILED     => $q->param('login') || 0,
+	},
+    );
+
+    return $template->output();
+}
+
+sub logout {
+    my ($self, $errors) = @_;
+
+    # Log the user out. Check for success FIXME.
+    $self->authen->logout();
+
+    my $template = $self->_load_my_tmpl(
+	$self->param('logout_html_template') || 'logout.html',
+	$errors,
+	{
+	    LOGIN => $RM_LOGIN,
+	},
+    );
+
+    return $template->output();
+}
+
+sub choose {
+    my ( $self, $errors ) = @_;
+    $self->authen->is_authenticated() or return $self->login();
+
+    my $user =
+      ArrayExpress::AutoSubmission::DB::User->retrieve(
+        login => $self->authen->username(),
+    );
+    my @experiments = ArrayExpress::AutoSubmission::DB::Experiment->search(
+        user_id         => $user->id(),
+        experiment_type => $self->param('experiment_type'),
+        in_curation     => 0,
+	is_deleted      => 0,
+    );
+    my @submitted = ArrayExpress::AutoSubmission::DB::Experiment->search(
+        user_id         => $user->id(),
+        experiment_type => $self->param('experiment_type'),
+        in_curation     => 1,
+	is_deleted      => 0,
+    );
+
+    my $template = $self->_load_my_tmpl(
+        $self->param('choose_html_template') || 'choose.html',
+        $errors,
+        {
+            EXPERIMENTS =>
+              [ map { { name => $_->name(), id => $_->id() } } @experiments ],
+            SUBMITTED => [
+                map { { name => $_->name(), date => $_->date_submitted() } }
+                  @submitted
+            ],
+            TITLE => 'Your ' . $self->param('experiment_type') . ' submissions',
+            CREATE => $RM_CREATE,
+            UPLOAD => $RM_UPLOAD,
+            EDIT   => $RM_EDIT,
+            DELETE => $RM_DELETE,
+        },
+    );
+
+    return $template->output();
+}
+
+sub create {
+    my ($self, $errors) = @_;
+    $self->authen->is_authenticated() or return $self->login();
+
+    # Get CGI query object.
+    my $q = $self->query();
+
+    # Build the page and return it.
+    my $template = $self->_load_my_tmpl(
+	$self->param('create_html_template') || 'create.html',
+	$errors,
+	{
+	    %{$self->_annotation_labels()},
+	    EXPT_NAME        => undef,
+	    EXPT_NAME_PARAM  => 'experiment_name',
+	    AFFYMETRIX       => undef,
+	    AFFYMETRIX_PARAM => 'is_affymetrix',
+	    TITLE            => 'Create experiment',
+	    INSERT           => $RM_INSERT,
+	},
+    );
+
+    return $template->output();
+}
+
+sub insert {
+    my ($self, $errors) = @_;
+    $self->authen->is_authenticated() or return $self->login();
+
+    # Validate the input.
+    my ($results, $err_page) =
+	$self->check_rm('create', '_edit_profile');
+    return $err_page if $err_page;
+
+    # Get CGI query object
+    my $q = $self->query();
+
+    my $user = ArrayExpress::AutoSubmission::DB::User->retrieve(
+	login => $self->authen->username()
+    );
+    my @designs   = map {
+	ArrayExpress::AutoSubmission::DB::Design->retrieve($_)
+    } $q->param('biol_designs'),
+	$q->param('meth_designs'),
+	    $q->param('tech_designs');
+    my @materials   = map {
+	ArrayExpress::AutoSubmission::DB::Material->retrieve($_)
+    } $q->param('materials');
+    my @organisms   = map {
+	ArrayExpress::AutoSubmission::DB::Organism->retrieve($_)
+    } $q->param('organisms');
+
+    my $experiment =
+      ArrayExpress::AutoSubmission::DB::Experiment->insert({
+	  name             => $q->param('experiment_name'),
+	  user_id          => $user->id(),
+	  experiment_type  => $self->param('experiment_type'),
+	  is_affymetrix    => $q->param('is_affymetrix') ? 1 : 0,
+	  date_last_edited => date_now(),
+	  in_curation      => 0,
+	  num_submissions  => 0,
+	  is_deleted       => 0,
+      });
+    foreach my $design (@designs) {
+	$experiment->add_to_design_instances({
+	    experiment_id => $experiment,
+	    design_id     => $design,
+	});
+    }
+    foreach my $material (@materials) {
+	$experiment->add_to_material_instances({
+	    experiment_id => $experiment,
+	    material_id   => $material,
+	});
+    }
+    foreach my $organism (@organisms) {
+	$experiment->add_to_organism_instances({
+	    experiment_id => $experiment,
+	    organism_id   => $organism,
+	});
+    }
+
+    my $is_annotated = scalar(@designs)
+	            && scalar(@materials)
+	            && scalar(@organisms);
+
+    # Build the page and return it.
+    my $template = $self->_load_my_tmpl(
+	$self->param('insert_html_template') || 'insert.html',
+	$errors,
+	{
+	    EXPT_ID    => $experiment->id(),
+	    EXPT_NAME  => $experiment->name(),
+	    TITLE      => 'Experiment created',
+	    TEMPLATE   => $RM_TEMPLATE,
+	    ANNOTATED  => $is_annotated,
+	    UPLOAD     => $RM_UPLOAD,
+	    SUBMIT     => $RM_SUBMIT,
+	    CHOOSE     => $RM_CHOOSE,
+	    AFFYMETRIX => $q->param('is_affymetrix') ? 1 : 0,
+	    DESIGNS    => [map { {name => $_->display_label()} } @designs],
+	    MATERIALS  => [map { {name => $_->display_label()} } @materials],
+	    ORGANISMS  => [map { {name => $_->scientific_name()} } @organisms],
+	},
+    );
+
+    return $template->output();
+}
+
+sub edit {
+    my ($self, $errors) = @_;
+    $self->authen->is_authenticated() or return $self->login();
+
+    # Validate the input.
+    my ($results, $err_page) =
+	$self->check_rm('choose', '_choose_profile');
+    return $err_page if $err_page;
+
+    # Get CGI query object.
+    my $q = $self->query();
+
+    # Get the experiment, checking that the user is the owner.
+    my ($experiment, $illegal_access) = $self->get_secure_experiment(
+	$q->param('experiment'),
+    );
+    return $illegal_access unless $experiment;
+
+    # Build the page and return it.
+    my $template = $self->_load_my_tmpl(
+	$self->param('edit_html_template') || 'edit.html',
+	$errors,
+	{
+	    %{$self->_annotation_labels($experiment)},
+	    EXPT_ID          => $experiment->id(),
+	    EXPT_NAME        => $experiment->name(),
+	    EXPT_NAME_PARAM  => 'experiment_name',
+	    AFFYMETRIX       => $experiment->is_affymetrix(),
+	    AFFYMETRIX_PARAM => 'is_affymetrix',
+	    TITLE            => 'Edit submission',
+	    UPDATE           => $RM_UPDATE,
+	},
+    );
+
+    return $template->output();
+}
+
+sub update {
+    my ($self, $errors) = @_;
+    $self->authen->is_authenticated() or return $self->login();
+
+    # Validate the input.
+    my ($results, $err_page) =
+	$self->check_rm('edit', '_edit_profile');
+    return $err_page if $err_page;
+
+    # Get CGI query object
+    my $q = $self->query();
+
+    # Get the experiment, checking that the user is the owner.
+    my ($experiment, $illegal_access) = $self->get_secure_experiment(
+	$q->param('experiment'),
+    );
+    return $illegal_access unless $experiment;
+
+    $experiment->set(
+	name             => $q->param('experiment_name'),
+	is_affymetrix    => $q->param('is_affymetrix') ? 1 : 0,
+	date_last_edited => date_now(),
+    );
+    $experiment->update();
+
+    my @designs   = map {
+	ArrayExpress::AutoSubmission::DB::Design->retrieve($_)
+    } $q->param('biol_designs'),
+	$q->param('meth_designs'),
+	    $q->param('tech_designs');
+    $experiment->update_designs(\@designs);
+
+    my @materials   = map {
+	ArrayExpress::AutoSubmission::DB::Material->retrieve($_)
+    } $q->param('materials');
+    $experiment->update_materials(\@materials);
+
+    my @organisms   = map {
+	ArrayExpress::AutoSubmission::DB::Organism->retrieve($_)
+    } $q->param('organisms');
+    $experiment->update_organisms(\@organisms);
+
+    my $is_annotated = scalar(@designs)
+	            && scalar(@materials)
+	            && scalar(@organisms);
+
+    # Build the page and return it.
+    my $template = $self->_load_my_tmpl(
+	$self->param('update_html_template') || 'update.html',
+	$errors,
+	{
+	    EXPT_ID    => $experiment->id(),
+	    EXPT_NAME  => $experiment->name(),
+	    AFFYMETRIX => $q->param('is_affymetrix') ? 1 : 0,
+	    TITLE      => 'Experiment updated',
+	    TEMPLATE   => $RM_TEMPLATE,
+	    ANNOTATED  => $is_annotated,
+	    UPLOAD     => $RM_UPLOAD,
+	    SUBMIT     => $RM_SUBMIT,
+	    CHOOSE     => $RM_CHOOSE,
+	    DESIGNS    => [map { {name => $_->display_label()} } @designs],
+	    MATERIALS  => [map { {name => $_->display_label()} } @materials],
+	    ORGANISMS  => [map { {name => $_->scientific_name()} } @organisms],
+	},
+    );
+
+    return $template->output();
+}
+
+sub delete_expt {
+    my ($self, $errors) = @_;
+    $self->authen->is_authenticated() or return $self->login();
+
+    # Validate the input. This checks if files have been uploaded, and
+    # forbids deletion if so.
+    my ($results, $err_page) =
+	$self->check_rm('choose', '_delete_profile');
+    return $err_page if $err_page;
+
+    # Get CGI query object.
+    my $q = $self->query();
+
+    # Get the experiment, checking that the user is the owner.
+    my ($experiment, $illegal_access) = $self->get_secure_experiment(
+	$q->param('experiment'),
+    );
+    return $illegal_access unless $experiment;
+
+    # Delete the object. We just mark it as is_deleted, rather than
+    # removing it from the database.
+    my $experiment_name = $experiment->name();
+    $experiment->set(is_deleted => 1);
+    $experiment->update();
+
+    # Build the page and return it.
+    my $template = $self->_load_my_tmpl(
+	$self->param('delete_html_template') || 'delete.html',
+	$errors,
+	{
+	    EXPT_NAME  => $experiment_name,
+	    TITLE      => 'Experiment deleted',
+	    CHOOSE     => $RM_CHOOSE,
+	},
+    );
+
+    return $template->output();
+}
+
+sub spreadsheet_template {
+    my ($self, $errors) = @_;
+    $self->authen->is_authenticated() or return $self->login();
+
+    # Get CGI query object
+    my $q = $self->query();
+
+    # Get the experiment, checking that the user is the owner.
+    my ($experiment, $illegal_access) = $self->get_secure_experiment(
+	$q->param('experiment'),
+    );
+    return $illegal_access unless $experiment;
+
+    # Generate the spreadsheet and return it.
+    my $spreadsheet = ArrayExpress::AutoSubmission::Spreadsheet->new({
+	experiment => $experiment,
+	url        => $self->query()->url(),
+	date       => date_now(),
+    });
+
+    my $basename = sprintf(
+	"%s_%s_%s_%s.txt",
+	untaint($experiment->user_id()->login()),
+	$self->param('experiment_type'),
+	untaint($experiment->name()),
+	$spreadsheet->get_date(),
+    );
+
+    # Strip some unwanted characters from the filename.
+    $basename =~ s/[ :\\\/]/_/g;
+
+    my $filename = File::Spec->catfile($CONFIG->get_WEBFORM_TEMPLATE_DIR(), $basename);
+    my $fileurl  = $CONFIG->get_WEBFORM_TEMPLATE_URL();
+    $fileurl =~ s/[\/]?$/\/$basename/;
+
+    my $ss_fh = IO::File->new($filename, q{>})
+	or croak("Error: unable to open spreadsheet template file $filename for writing: $!");
+    my $ss_content;
+    if ( $self->param('spreadsheet_type') =~ m/\A mage-?tab \z/ixms ) {
+	$ss_content = $spreadsheet->magetab();
+    }
+    else {
+	$ss_content = $spreadsheet->tab2mage();
+    }
+	   
+    print $ss_fh $ss_content
+	or croak("Error: unable to write output to spreadsheet template file $filename: $!");
+
+    # Build the page and return it.
+    my $template = $self->_load_my_tmpl(
+	$self->param('template_html_template') || 'template.html',
+	$errors,
+	{
+	    EXPT_ID    => $experiment->id(),
+	    EXPT_NAME  => $experiment->name(),
+	    TITLE      => 'Template download',
+	    SS_LINK    => $fileurl,
+	    UPLOAD     => $RM_UPLOAD,
+	    CHOOSE     => $RM_CHOOSE,
+	},
+    );
+
+    return $template->output();
+}
+
+sub upload {
+    my ($self, $errors) = @_;
+    $self->authen->is_authenticated() or return $self->login();
+
+    # Validate the input.
+    my ($results, $err_page) =
+	$self->check_rm('choose', '_choose_profile');
+    return $err_page if $err_page;
+
+    # Get CGI query object
+    my $q = $self->query();
+
+    # Get the experiment, checking that the user is the owner.
+    my ($experiment, $illegal_access) = $self->get_secure_experiment(
+	$q->param('experiment'),
+    );
+    return $illegal_access unless $experiment;
+
+    my @spreadsheets = map {
+	{ name      => $_->name(), }
+    } $experiment->spreadsheets(is_deleted => 0);
+
+    my @data_files = map {
+	{ name      => $_->name(), }
+    } $experiment->data_files(is_deleted => 0);
+
+    # Build the page and return it.
+    my $template = $self->_load_my_tmpl(
+	$self->param('upload_html_template') || 'upload.html',
+	$errors,
+	{
+	    EXPT_NAME       => $experiment->name(),
+	    EXPT_ID         => $experiment->id(),
+	    TITLE           => 'File upload',
+	    CHOOSE          => $RM_CHOOSE,
+	    FILE_UPLOAD     => $RM_FILE_UPLOAD,
+	    SUBMIT          => $RM_SUBMIT,
+	    DATA_FILE       => 'data_file',
+	    SPREADSHEET     => 'spreadsheet',
+	    SHEETS_LOADED   => \@spreadsheets,
+	    FILES_LOADED    => \@data_files,
+	},
+    );
+
+    return $template->output();
+}
+
+sub detect_platform {
+
+    # Return a string that fileparse_set_fstype can understand. Note
+    # that only MSWin32 and MacOS are really understood, and the rest
+    # are assumed to be flavours of Unix.
+    my $self = shift;
+
+    # Attempt to detect non-unix platforms. Windows is the main one at
+    # the moment - MacOSX conforms to unix path structures.
+    my $ua = $self->query()->user_agent();
+    my $browser = HTTP::BrowserDetect->new($ua);
+
+    if ( $browser->windows() ) {
+	return 'MSWin32';
+    }
+    elsif ( $browser->mac() ) {
+	if ( $browser->macosx() ) {
+	    return 'MacOSX';
+	}
+	else {
+	    return 'MacOS';
+	}
+    }
+    else {
+
+	# Standardise on a default. I happen to be partial to linux...
+	return 'linux';
+    }
+
+}
+
+sub file_upload {
+
+    my ($self, $errors) = @_;
+    $self->authen->is_authenticated() or return $self->login();
+
+    # Set the platform before attempting validation.
+    fileparse_set_fstype( $self->detect_platform() );
+
+    # Validate the input.
+    my ($results, $err_page) =
+	$self->check_rm('upload', '_upload_profile');
+    return $err_page if $err_page;
+
+    # Get CGI query object
+    my $q = $self->query();
+
+    # Get the experiment, checking that the user is the owner.
+    my ($experiment, $illegal_access) = $self->get_secure_experiment(
+	$q->param('experiment'),
+    );
+    return $illegal_access unless $experiment;
+
+    # Uploads are particularly fraught.
+    if ( my $error = $q->cgi_error() ) {
+	return $self->error('CGI error triggered: ', $error);
+    }
+
+    # Create the submissions directory.
+    my $submissions_dir = $experiment->filesystem_directory();
+
+    # Upload the files. Spreadsheet first:
+    if ( my $upload_path = $q->param('spreadsheet') ){
+
+	# Trim off any directories (Win, Mac or Unix).
+	my $upload_file = untaint(basename( $upload_path ));
+
+	# Input file.
+	my $upload    = CGI::Upload->new;
+	my $upload_fh = $upload->file_handle('spreadsheet');
+	$upload_fh->binmode();
+
+	unless ($upload->mime_type('spreadsheet') eq 'text/plain') {
+	    return $self->upload({
+		'err_spreadsheet' =>
+		    sprintf (
+			$ERROR_FORMAT,
+			  q{Error: the spreadsheet file must be in}
+			. q{ plain ASCII tab-delimited text format.},
+		    ),
+		},
+	    );
+	}
+
+	# Output file.
+	my $spreadsheet = File::Spec->catfile($submissions_dir, $upload_file);
+	my $out_fh      = IO::File->new($spreadsheet, '>')
+	    or die("Error: unable to upload file: $!");
+	$out_fh->binmode();
+
+	# Backup spreadsheet.
+	my $backup = File::Spec->catfile(
+	    $submissions_dir,
+	    "$upload_file.backup." . untaint(date_now())
+	);
+	my $backup_fh      = IO::File->new($backup, '>')
+	    or die("Error: unable to upload file: $!");
+	$backup_fh->binmode();
+
+	# Copy the file somewhere useful.
+	my ($buffer, $bytes_read);
+	while (my $bytes = read($upload_fh, $buffer, 1024)){
+	    $bytes_read += $bytes;
+	    print $out_fh $buffer
+		or die("Error writing spreadsheet file to filesystem");
+
+	    # Do we really need to croak on error here FIXME?
+	    print $backup_fh $buffer
+		or die("Error writing backup spreadsheet file to filesystem");
+	}
+
+	# We don't set permissions on the backup file as curators
+	# should not need to change it. Note that setting permissions
+	# fails if a curator owns the spreadsheet, so we can't afford
+	# to croak indiscriminately. In such cases it's up to the
+	# curator to fix the problem, we only handle the files we own.
+	if ( -o $spreadsheet ) {
+	    chmod(oct($CONFIG->get_FILE_PERMISSIONS()), $spreadsheet)
+		or croak("Error changing permissions on $spreadsheet: $!");
+	}
+
+	# Save to database. Note that we leave any previous upload in
+	# place; we may want to revisit that at some point FIXME.
+	my $record = ArrayExpress::AutoSubmission::DB::Spreadsheet->find_or_create(
+	    experiment_id => $experiment->id(),
+	    is_deleted    => 0,
+	);
+	$record->set(name => $upload_file);
+	$record->update();
+	$experiment->set(
+	    date_last_edited => date_now(),
+	);
+	$experiment->update();
+    }
+
+    # Now upload the data file:
+    if ( my $upload_path = $q->param('data_file') ){
+
+	# Trim off any directories (Win, Mac or Unix).
+	my $upload_file = untaint(basename( $upload_path ));
+
+	# Input file.
+	my $upload      = CGI::Upload->new;
+	my $upload_fh   = $upload->file_handle('data_file');
+	$upload_fh->binmode();
+
+	# Output file.
+	my $data_file   = File::Spec->catfile($submissions_dir, $upload_file);
+	my $out_fh      = IO::File->new($data_file, '>')
+	    or die("Error: unable to upload file: $!");
+	$out_fh->binmode();
+
+	# Copy the file somewhere useful.
+	my ($buffer, $bytes_read);
+	while (my $bytes = read($upload_fh, $buffer, 1024)){
+	    $bytes_read += $bytes;
+	    print $out_fh $buffer
+		or die("Error writing data archive file to filesystem");
+	}
+
+	if ( -o $data_file ) {
+	    chmod(oct($CONFIG->get_FILE_PERMISSIONS()), $data_file)
+		or croak("Error changing permissions on $data_file: $!");
+	}
+
+	# Save to database, overwriting any uploaded files with the
+	# same name.
+	my $record = ArrayExpress::AutoSubmission::DB::DataFile->find_or_create(
+	    name          => $upload_file,
+	    experiment_id => $experiment->id(),
+	    is_deleted    => 0,
+	);
+	$record->set(is_unpacked => 0);
+	$record->update();
+	$experiment->set(
+	    date_last_edited => date_now(),
+	);
+	$experiment->update();
+    }
+
+    my @spreadsheets = map {
+	{ name      => $_->name(), }
+    } $experiment->spreadsheets(is_deleted => 0);
+
+    my @data_files = map {
+	{ name      => $_->name(), }
+    } $experiment->data_files(is_deleted => 0);
+    
+    # Build the page and return it.
+    my $template = $self->_load_my_tmpl(
+	$self->param('file_upload_html_template') || 'file_upload.html',
+	$errors,
+	{
+	    EXPT_NAME       => $experiment->name(),
+	    EXPT_ID         => $experiment->id(),
+	    TITLE           => 'Uploads complete',
+	    UPLOAD          => $RM_UPLOAD,
+	    SUBMIT          => $RM_SUBMIT,
+	    CHOOSE          => $RM_CHOOSE,
+	    SPREADSHEET     => 'spreadsheet',
+	    DATA_FILE       => 'data_file',
+	    SHEETS_LOADED   => \@spreadsheets,
+	    FILES_LOADED    => \@data_files,
+	    SHEETS_OK       => scalar @spreadsheets || undef,
+	    FILES_OK        => scalar @data_files || undef,
+	},
+    );
+
+    return $template->output();
+}
+
+sub submit {
+    my ($self, $errors) = @_;
+    $self->authen->is_authenticated() or return $self->login();
+
+    # Validate the input.
+    my ($results, $err_page) =
+	$self->check_rm('upload', '_submit_profile');
+    return $err_page if $err_page;
+
+    # Get CGI query object
+    my $q = $self->query();
+
+    # Get the experiment, checking that the user is the owner.
+    my ($experiment, $illegal_access) = $self->get_secure_experiment(
+	$q->param('experiment'),
+    );
+    return $illegal_access unless $experiment;
+
+    # in_curation determines web interface visibility; status is used
+    # only by the back-end processing.
+    $experiment->set(
+	in_curation    => 1,
+	status         => $CONFIG->get_STATUS_PENDING(),
+	date_submitted => date_now(),
+    );
+    $experiment->update();
+
+    # Email the user to confirm submission (but not if it's from the
+    # test user).
+    unless ($experiment->user_id()->login() eq 'test') {
+	$self->email_confirm_submission($experiment);
+    }
+
+    # Increment the number of submissions (this must happen after the
+    # confirmation emails are sent).
+    $experiment->set(
+	num_submissions => ( $experiment->num_submissions() + 1 ),
+    );
+    $experiment->update();
+    
+    # Build the page and return it.
+    my $template = $self->_load_my_tmpl(
+	$self->param('submitted_html_template') || 'submitted.html',
+	$errors,
+	{
+	    EXPT_NAME       => $experiment->name(),
+	    EXPT_ID         => $experiment->id(),
+	    TITLE           => 'Experiment submitted',
+	    CHOOSE          => $RM_CHOOSE,
+	},
+    );
+
+    return $template->output();
+}
+
+sub error {
+
+    # Generate a generic error page that doesn't look too bad.
+    my ( $self, @messages ) = @_;
+
+    my $template = $self->_load_my_tmpl(
+	'error.html',
+	undef,		
+	{
+	    MESSAGE => (join(q{}, @messages)
+			    || 'Unknown application crash.'),
+	    RETURN  => $self->authen->is_authenticated()
+		       ? $RM_CHOOSE
+		       : $RM_INTRO,
+	},
+    );
+
+    return $template->output();
+}
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../index.html">
+                <img src="../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: WebForm.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::AutoSubmission::WebForm - A CGI::Application web form
+for Tab2MAGE/MAGE-TAB data submissions.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::AutoSubmission::WebForm;
+ my $webapp = ArrayExpress::AutoSubmission::WebForm->new(
+     PARAMS => {
+	experiment_type  => 'MAGE-TAB',
+	spreadsheet_type => 'MAGE-TAB',
+	cgi_base         => '/cgi-bin/microarray/magetab.cgi',
+	stylesheet       => '/microarray/tab2mage.css',
+	intro_html_template    => 'magetab_intro.html',
+	upload_html_template   => 'magetab_upload.html',
+	template_html_template => 'magetab_template.html',
+	sidebar_icons   => [
+	    {
+		image       => '/microarray/aelogo.png',
+		destination => 'http://www.ebi.ac.uk/arrayexpress/',
+		alt         => 'ArrayExpress',
+	    },
+	],
+	sidebar_links   => [
+	    {
+		text        => 'Microarray group',
+		destination => 'http://www.ebi.ac.uk/microarray/',
+	    },
+	],
+    }
+ );
+
+ # Start the CGI script.
+ $webapp->run();
+
+=head1 DESCRIPTION
+
+This module is the core of the web application used in the Tab2MAGE
+and MAGE-TAB data submission web forms. Objects are instantiated with
+a PARAMS attribute which can be used to customise various aspects of
+the web form behaviour.
+
+=head1 METHODS
+
+=over 2
+
+=item C<new>
+
+Object constructor. See the CGI::Application documentation for details.
+
+=item C<run>
+
+The method used to start the web application.
+
+=back
+
+=head1 TODO
+
+Documentation of the options which can be customised using the
+PARAMS attribute.
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2008.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments. 
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/templates/_annotate.html b/lib/ArrayExpress/AutoSubmission/templates/_annotate.html
new file mode 100644
index 0000000..84a5ec2
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/_annotate.html
@@ -0,0 +1,183 @@
+      <!-- TMPL_IF NAME=SOME_ERRORS -->
+      <p>There were some errors:</p>
+      <!-- /TMPL_IF -->
+
+      <!-- TMPL_IF NAME=ERR_EXPERIMENT_NAME -->
+      <!-- TMPL_VAR NAME=ERR_EXPERIMENT_NAME --><br/>
+      <!-- /TMPL_IF -->
+
+      <!-- TMPL_IF NAME=ERR_BIOL_DESIGNS -->
+      <!-- TMPL_VAR NAME=ERR_BIOL_DESIGNS --><br/>
+      <!-- /TMPL_IF -->
+
+      <!-- TMPL_IF NAME=ERR_METH_DESIGNS -->
+      <!-- TMPL_VAR NAME=ERR_METH_DESIGNS --><br/>
+      <!-- /TMPL_IF -->
+
+      <!-- TMPL_IF NAME=ERR_TECH_DESIGNS -->
+      <!-- TMPL_VAR NAME=ERR_TECH_DESIGNS --><br/>
+      <!-- /TMPL_IF -->
+
+      <!-- TMPL_IF NAME=ERR_MATERIALS -->
+      <!-- TMPL_VAR NAME=ERR_MATERIALS --><br/>
+      <!-- /TMPL_IF -->
+
+      <!-- TMPL_IF NAME=ERR_ORGANISMS -->
+      <!-- TMPL_VAR NAME=ERR_ORGANISMS --><br/>
+      <!-- /TMPL_IF -->
+
+      <table summary="experiment top-level info">
+        <tr>
+          <td>Name:</td>
+          <td<!-- TMPL_IF NAME=ERR_EXPERIMENT_NAME --> class="fieldWithErrors"<!-- /TMPL_IF -->>
+            <input type="text"
+	           name="<!-- TMPL_VAR NAME=EXPT_NAME_PARAM -->"
+		   size="40"
+	           value="<!-- TMPL_VAR NAME=EXPT_NAME -->"/></td>
+        </tr>
+        <tr>
+	  <td>Affymetrix experiment?</td>
+	  <td>
+	    <input type="checkbox"
+	           name="<!-- TMPL_VAR NAME=AFFYMETRIX_PARAM -->"
+	           <!-- TMPL_IF NAME=AFFYMETRIX -->
+	           checked="checked"
+	           <!-- /TMPL_IF --> />
+	  </td>
+	</tr>
+      </table>
+
+    <p class="text">If you wish to have a spreadsheet template to be
+    constructed for you, please select one or more items from each of
+    the following lists. Please note that you can omit this step if
+    you have already created your own spreadsheet. Once you have
+    entered the appropriate information, please click the button at
+    the bottom of the page.</p>
+
+  <div class="text">
+    <div align="center">
+      <table summary="experiment design annotation">
+	<tr>
+	  <th>
+            <!-- TMPL_IF NAME=ERR_BIOL_DESIGNS -->
+	      <span style="font-size: 150%; color: red;">*</span>
+	    <!-- /TMPL_IF -->
+	    Biological design:
+	  </th>
+	  <th>
+            <!-- TMPL_IF NAME=ERR_METH_DESIGNS -->
+	      <span style="font-size: 150%; color: red;">*</span>
+	    <!-- /TMPL_IF -->
+	    Methodology:
+	  </th>
+	  <th>
+            <!-- TMPL_IF NAME=ERR_TECH_DESIGNS -->
+	      <span style="font-size: 150%; color: red;">*</span>
+	    <!-- /TMPL_IF -->
+	    Technology used:
+	  </th>
+	</tr>
+        <tr>
+	<td>
+	<select name="biol_designs" size="10" multiple="multiple">
+	<!-- TMPL_LOOP NAME=BIOL_DESIGNS -->
+	  <option <!-- TMPL_IF NAME=SELECTED -->selected="selected"<!-- /TMPL_IF -->
+	      value="<!-- TMPL_VAR NAME=ID -->">
+	      <!-- TMPL_VAR NAME=NAME -->
+	    </option>
+	<!-- /TMPL_LOOP -->
+	</select>
+	</td><td>
+	<select name="meth_designs" size="10" multiple="multiple">
+	<!-- TMPL_LOOP NAME=METH_DESIGNS -->
+	  <option <!-- TMPL_IF NAME=SELECTED -->selected="selected"<!-- /TMPL_IF -->
+	      value="<!-- TMPL_VAR NAME=ID -->">
+	      <!-- TMPL_VAR NAME=NAME -->
+	    </option>
+	<!-- /TMPL_LOOP -->
+	</select>
+	</td><td>
+	<select name="tech_designs" size="10" multiple="multiple">
+	<!-- TMPL_LOOP NAME=TECH_DESIGNS -->
+	  <option <!-- TMPL_IF NAME=SELECTED -->selected="selected"<!-- /TMPL_IF -->
+	      value="<!-- TMPL_VAR NAME=ID -->">
+	      <!-- TMPL_VAR NAME=NAME -->
+	    </option>
+	<!-- /TMPL_LOOP -->
+	</select>
+	</td>
+	</tr>
+	  <tr>
+	    <td align="center">
+	      <a href="http://mged.sourceforge.net/ontologies/MGEDontology.php#BiologicalProperty"
+		 target="content">MGED Ontology link</a>
+	    </td>
+	    <td align="center">
+	      <a href="http://mged.sourceforge.net/ontologies/MGEDontology.php#MethodologicalDesign"
+		 target="content">MGED Ontology link</a>
+	    </td>
+	    <td align="center">
+	      <a href="http://mged.sourceforge.net/ontologies/MGEDontology.php#TechnologicalDesign"
+		 target="content">MGED Ontology link</a>
+	    </td>
+	  </tr>
+      </table>
+    </div>
+    <div align="center">
+      <table summary="materials and species annotation">
+	<tr>
+	  <th>
+            <!-- TMPL_IF NAME=ERR_MATERIALS -->
+	      <span style="font-size: 150%; color: red;">*</span>
+	    <!-- /TMPL_IF -->
+	    Materials used:
+	  </th>
+	  <th>
+            <!-- TMPL_IF NAME=ERR_ORGANISMS -->
+	      <span style="font-size: 150%; color: red;">*</span>
+            <!-- /TMPL_IF -->
+	    Organisms used:
+	  </th>
+	</tr>
+	<tr>
+	<td>
+	<select name="materials" size="10" multiple="multiple">
+	<!-- TMPL_LOOP NAME=MATERIALS -->
+	  <option <!-- TMPL_IF NAME=SELECTED -->selected="selected"<!-- /TMPL_IF -->
+	      value="<!-- TMPL_VAR NAME=ID -->">
+	      <!-- TMPL_VAR NAME=NAME -->
+	    </option>
+	<!-- /TMPL_LOOP -->
+	</select>
+	</td>
+	<td>
+	<select name="organisms" size="10" multiple="multiple">
+	<!-- TMPL_LOOP NAME=ORGANISMS -->
+	  <option <!-- TMPL_IF NAME=SELECTED -->selected="selected"<!-- /TMPL_IF -->
+	      value="<!-- TMPL_VAR NAME=ID -->">
+	      <!-- TMPL_VAR NAME=NAME -->
+	    </option>
+	<!-- /TMPL_LOOP -->
+	</select>
+	</td>
+        </tr>
+	  <tr>
+	    <td align="center">
+	      <a href="http://mged.sourceforge.net/ontologies/MGEDontology.php#MaterialType"
+		 target="content">MGED Ontology link</a>
+	    </td>
+	    <td align="center">
+	      <a href="http://www.ncbi.nlm.nih.gov/Taxonomy/taxonomyhome.html/"
+		 target="content">NCBI Taxonomy link</a>
+	    </td>
+	  </tr>
+      </table>
+	
+	<p class="text">If your organism is not listed, please <a
+	href="mailto:<!-- TMPL_VAR NAME=CURATOR_EMAIL -->?subject=Add New Species (<!-- TMPL_VAR NAME=EXPT_TYPE -->)"> contact us</a>.
+	  Note that you may still upload
+	files without using this template generation tool, by leaving
+	all the selection boxes blank.</p>
+
+    </div>
+  </div>
diff --git a/lib/ArrayExpress/AutoSubmission/templates/_display.html b/lib/ArrayExpress/AutoSubmission/templates/_display.html
new file mode 100644
index 0000000..7e865b0
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/_display.html
@@ -0,0 +1,48 @@
+  <!-- TMPL_IF NAME=ANNOTATED -->
+    <!-- TMPL_INCLUDE NAME="_display_annotation.html" -->
+  <!-- /TMPL_IF -->
+
+  <!-- TMPL_IF NAME=ANNOTATED -->
+
+  <p class="text">If you wish to use the automatically generated
+  spreadsheet template, please click on the button below to open the
+  template in a new window. You will then be able to save the template
+  to your hard drive.</p>
+  
+    <div class="navbar">
+    <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+      <input type="hidden"
+	     name="experiment"
+	     value="<!-- TMPL_VAR NAME=EXPT_ID -->"/>
+      <input type="submit"
+	     name="<!-- TMPL_VAR NAME=RM -->"
+             value="<!-- TMPL_VAR NAME=TEMPLATE -->"/>
+    </form>
+    </div>
+  <!-- TMPL_ELSE -->
+
+    <p class="text">Optional spreadsheet template construction
+    requires design, organism and material to be entered.</p>
+
+  <!-- /TMPL_IF -->
+
+  <p class="text">Please use the following links to upload your data
+  files and completed spreadsheet.</p>
+  
+    <div class="navbar">
+    <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+      <input type="hidden"
+	     name="experiment"
+	     value="<!-- TMPL_VAR NAME=EXPT_ID -->"/>
+      <input type="submit"
+	     name="<!-- TMPL_VAR NAME=RM -->"
+             value="<!-- TMPL_VAR NAME=UPLOAD -->"/>
+      <input type="submit"
+	     name="<!-- TMPL_VAR NAME=RM -->"
+	     onclick="return confirm('Are you sure? You will be unable to make further changes.');"
+             value="<!-- TMPL_VAR NAME=SUBMIT -->"/>
+      <input type="submit"
+	     name="<!-- TMPL_VAR NAME=RM -->"
+             value="<!-- TMPL_VAR NAME=CHOOSE -->"/>
+    </form>
+    </div>
\ No newline at end of file
diff --git a/lib/ArrayExpress/AutoSubmission/templates/_display_annotation.html b/lib/ArrayExpress/AutoSubmission/templates/_display_annotation.html
new file mode 100644
index 0000000..b334585
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/_display_annotation.html
@@ -0,0 +1,28 @@
+        <!-- TMPL_IF NAME=AFFYMETRIX --><p>This is an Affymetrix experiment.</p><!-- /TMPL_IF -->
+
+	<table summary="annotation list">
+          <tr>
+            <td>Experiment designs:</td>
+            <td>
+              <!-- TMPL_LOOP NAME=DESIGNS -->
+	        <!-- TMPL_VAR NAME=NAME --><br/>
+   	      <!-- /TMPL_LOOP -->
+            </td>
+          </tr>
+          <tr>
+            <td>Materials used:</td>
+            <td>
+ 	      <!-- TMPL_LOOP NAME=MATERIALS -->
+	        <!-- TMPL_VAR NAME=NAME --><br/>
+	      <!-- /TMPL_LOOP -->
+	    </td>
+          </tr>
+          <tr>
+            <td>Species studied:</td>
+            <td>
+	      <!-- TMPL_LOOP NAME=ORGANISMS -->
+	        <!-- TMPL_VAR NAME=NAME --><br/>
+	      <!-- /TMPL_LOOP -->
+	    </td>
+          </tr>
+        </table>
diff --git a/lib/ArrayExpress/AutoSubmission/templates/_foot.html b/lib/ArrayExpress/AutoSubmission/templates/_foot.html
new file mode 100644
index 0000000..4463d8e
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/_foot.html
@@ -0,0 +1,3 @@
+  </div>
+ </body>
+</html>
\ No newline at end of file
diff --git a/lib/ArrayExpress/AutoSubmission/templates/_head.html b/lib/ArrayExpress/AutoSubmission/templates/_head.html
new file mode 100644
index 0000000..bcc3470
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/_head.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html
+	PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+	 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US">
+  <head>
+    <title><!-- TMPL_VAR NAME=TITLE --></title>
+    <link rel="stylesheet" type="text/css" href="<!-- TMPL_VAR NAME=STYLESHEET -->" />
+      <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+  </head>
+  <body>
+    <div class="link-list" id="sidebar">
+
+      <div align="center">
+      <p class="sidebar-title"><!-- TMPL_VAR NAME=EXPT_TYPE --> submissions</p>
+
+      <div align="left">
+      <ul>
+      <!-- TMPL_LOOP NAME=SIDEBAR_LINKS -->
+	<li>
+	  <a href="<!-- TMPL_VAR NAME=DESTINATION -->">
+	    <!-- TMPL_VAR NAME=TEXT -->
+	  </a>
+	</li>
+      <!-- /TMPL_LOOP -->
+      </ul>
+      </div>
+
+      <!-- TMPL_LOOP NAME=SIDEBAR_ICONS -->
+      <p>
+	<a href="<!-- TMPL_VAR NAME=DESTINATION -->">
+	  <img src="<!-- TMPL_VAR NAME=IMAGE -->"
+	       border="0"
+	    <!-- TMPL_IF NAME=WIDTH -->
+	       width="<!-- TMPL_VAR NAME=WIDTH -->"
+	    <!-- /TMPL_IF -->
+	       alt="<!-- TMPL_VAR NAME=ALT -->"/>
+	</a>
+      </p>
+      <!-- /TMPL_LOOP -->
+      </div>
+
+      <!-- TMPL_IF NAME=AUTHEN -->
+      <div align="center">
+      <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+        <input type="submit"
+	       name="<!-- TMPL_VAR NAME=RM -->"
+	       value="<!-- TMPL_VAR NAME=LOGOUT -->"/>
+      </form>
+      </div>
+      <!-- /TMPL_IF -->
+
+      <div align="left">
+	<ul>
+	  <li>
+	    <a href="mailto:<!-- TMPL_VAR NAME=CURATOR_EMAIL
+	      -->?subject=<!-- TMPL_VAR NAME=EXPT_TYPE --> Submissions Help">Contact us</a>
+	  </li>
+	</ul>
+      </div>
+
+    </div>
+  <div id="main">
diff --git a/lib/ArrayExpress/AutoSubmission/templates/choose.html b/lib/ArrayExpress/AutoSubmission/templates/choose.html
new file mode 100644
index 0000000..d819143
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/choose.html
@@ -0,0 +1,49 @@
+<!-- TMPL_INCLUDE HEADER -->
+    <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+    <p class="text">Please select the experiment to edit from the
+    following list, or create a new submission:</p>
+
+    <!-- TMPL_IF NAME=SOME_ERRORS -->
+    <!-- /TMPL_IF -->
+    <!-- TMPL_IF NAME=ERR_EXPERIMENT -->
+    <p class="text"><!-- TMPL_VAR NAME=ERR_EXPERIMENT --></p>
+    <!-- /TMPL_IF -->
+
+    <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+    <div>
+      <select name="experiment" size="5">
+	<!-- TMPL_LOOP NAME=EXPERIMENTS -->
+	  <option value="<!-- TMPL_VAR NAME=ID -->"><!-- TMPL_VAR NAME=NAME --></option>
+	<!-- /TMPL_LOOP -->
+      </select>
+    </div>
+    <br/>
+      <div class="navbar">
+	<input type="submit" name="<!-- TMPL_VAR NAME=RM -->"
+	  value="<!-- TMPL_VAR NAME=CREATE -->"/>
+	<input type="submit" name="<!-- TMPL_VAR NAME=RM -->"
+	  value="<!-- TMPL_VAR NAME=EDIT -->"/>
+	<input type="submit" name="<!-- TMPL_VAR NAME=RM -->"
+	  value="<!-- TMPL_VAR NAME=UPLOAD -->"/>
+	<input type="submit" name="<!-- TMPL_VAR NAME=RM -->"
+	  value="<!-- TMPL_VAR NAME=DELETE -->"/>
+      </div>
+      <div>
+	<p>Previously submitted experiments:</p>
+	<table summary="previous experiments list">
+	  <tr>
+	    <th>Experiment name</th>
+	    <th>Submission date</th>
+	  </tr>
+	<!-- TMPL_LOOP NAME=SUBMITTED -->
+	  <tr>
+	    <td><!-- TMPL_VAR NAME=NAME --></td>
+	    <td><!-- TMPL_VAR NAME=DATE --></td>
+	  </tr>
+	<!-- /TMPL_LOOP -->
+	</table>
+      </div>
+
+    </form>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/create.html b/lib/ArrayExpress/AutoSubmission/templates/create.html
new file mode 100644
index 0000000..70ed97f
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/create.html
@@ -0,0 +1,16 @@
+<!-- TMPL_INCLUDE HEADER -->
+    <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+    <p class="text">Please enter some summary information for your
+    experiment here.</p>
+
+    <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+	<!-- TMPL_INCLUDE NAME="_annotate.html" -->
+    <div class="navbar">
+      <input type="submit"
+	     name="<!-- TMPL_VAR NAME=RM -->"
+             value="<!-- TMPL_VAR NAME=INSERT -->"/>
+    </div>
+
+    </form>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/create_user.html b/lib/ArrayExpress/AutoSubmission/templates/create_user.html
new file mode 100644
index 0000000..6c43fe1
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/create_user.html
@@ -0,0 +1,29 @@
+<!-- TMPL_INCLUDE HEADER -->
+    <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+    <p class="text">Your account has been successfully created:</p>
+
+    <table summary="user details">
+      <tr><td>Real name:</td><td><!-- TMPL_VAR NAME=REAL --></td></tr>
+      <tr><td>Username:</td><td><!-- TMPL_VAR NAME=USER --></td></tr>
+      <tr><td>Password:</td><td><!-- TMPL_VAR NAME=PASS --></td></tr>
+      <tr><td>Email:</td><td><!-- TMPL_VAR NAME=EMAIL --></td></tr>
+    </table>
+
+    <p class="text">Please now continue to the experiment creation page:</p>
+  
+    <div class="navbar">
+    <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+      <input type="hidden"
+	     name="<!-- TMPL_VAR NAME=USERNAME -->"
+	     value="<!-- TMPL_VAR NAME=USER -->"/>
+      <input type="hidden"
+	     name="<!-- TMPL_VAR NAME=PASSWORD -->"
+	     value="<!-- TMPL_VAR NAME=PASS -->"/>
+      <input type="submit"
+	     name="<!-- TMPL_VAR NAME=RM -->"
+	     value="<!-- TMPL_VAR NAME=CHOOSE -->"
+	     class="button" />
+    </form>
+    </div>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/delete.html b/lib/ArrayExpress/AutoSubmission/templates/delete.html
new file mode 100644
index 0000000..cbb6d95
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/delete.html
@@ -0,0 +1,14 @@
+<!-- TMPL_INCLUDE HEADER -->
+    <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+    <p class="text">Successfully deleted experiment "<!-- TMPL_VAR NAME=EXPT_NAME -->"</p>
+
+    <div class="navbar">
+    <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+          <input type="submit"
+	         name="<!-- TMPL_VAR NAME=RM -->"
+	         value="<!-- TMPL_VAR NAME=CHOOSE -->"
+	         class="button" />
+    </form>
+    </div>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/edit.html b/lib/ArrayExpress/AutoSubmission/templates/edit.html
new file mode 100644
index 0000000..84a310a
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/edit.html
@@ -0,0 +1,15 @@
+<!-- TMPL_INCLUDE HEADER -->
+    <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+    <p class="text">Editing experiment "<!-- TMPL_VAR NAME=EXPT_NAME -->"</p>
+
+    <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+      <input type="hidden" name="experiment" value="<!-- TMPL_VAR NAME=EXPT_ID -->"/>
+	<!-- TMPL_INCLUDE NAME="_annotate.html" -->
+      <div class="navbar">
+      <input type="submit" name="<!-- TMPL_VAR NAME=RM -->"
+      value="<!-- TMPL_VAR NAME=UPDATE -->"/>
+      </div>
+
+    </form>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/email_signup_confirmation.txt b/lib/ArrayExpress/AutoSubmission/templates/email_signup_confirmation.txt
new file mode 100644
index 0000000..9dd48d4
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/email_signup_confirmation.txt
@@ -0,0 +1,16 @@
+Dear <TMPL_VAR NAME=REALNAME>,
+    
+Welcome to the ArrayExpress submissions system for <TMPL_VAR NAME=TYPE> experiments.
+
+The following account has been created at your request:
+
+    username: <TMPL_VAR NAME=USERNAME>
+    password: <TMPL_VAR NAME=PASSWORD>
+
+You may now submit your experimental data using the following web page:
+
+    <TMPL_VAR NAME=URL>
+
+        Best regards,
+
+        The ArrayExpress team
diff --git a/lib/ArrayExpress/AutoSubmission/templates/email_subs_confirmation.txt b/lib/ArrayExpress/AutoSubmission/templates/email_subs_confirmation.txt
new file mode 100644
index 0000000..24d11e3
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/email_subs_confirmation.txt
@@ -0,0 +1,14 @@
+Dear <TMPL_VAR NAME=REALNAME>,
+    
+Thank you for your <TMPL_VAR NAME=TYPE> submission to ArrayExpress.
+Our curation team will contact you shortly regarding your
+experiment "<TMPL_VAR NAME=EXPTNAME>".
+
+In the meantime, please contact us at <TMPL_VAR NAME=CURATOR_EMAIL>
+with any questions.
+
+        Best regards,
+
+        The ArrayExpress team
+
+Submission date: <TMPL_VAR NAME=SUBSDATE>
diff --git a/lib/ArrayExpress/AutoSubmission/templates/error.html b/lib/ArrayExpress/AutoSubmission/templates/error.html
new file mode 100644
index 0000000..57c8420
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/error.html
@@ -0,0 +1,22 @@
+<!-- TMPL_INCLUDE HEADER -->
+    <h1>An internal error has occurred:</h1>
+
+    <p class="text"><!-- TMPL_VAR NAME=MESSAGE --></p>
+
+    <p class="text">Please report this error to <!-- TMPL_VAR
+    NAME=CURATOR_EMAIL -->. You may use the button below to return to
+    the submission forms:</p>
+    
+    <form method="post"
+	  action="<!-- TMPL_VAR NAME=CGI_BASE -->"
+	  enctype="multipart/form-data">
+
+      <div class="navbar">
+      <input type="submit"
+	     name="<!-- TMPL_VAR NAME=RM -->"
+	     value="<!-- TMPL_VAR NAME=RETURN -->"
+	     class="button" />
+      </div>
+
+    </form>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/file_upload.html b/lib/ArrayExpress/AutoSubmission/templates/file_upload.html
new file mode 100644
index 0000000..17055ca
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/file_upload.html
@@ -0,0 +1,63 @@
+<!-- TMPL_INCLUDE HEADER -->
+    <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+    <p class="text">The following files have been successfully
+    uploaded for your experiment "<!-- TMPL_VAR NAME=EXPT_NAME -->"</p>
+
+      <!-- TMPL_IF NAME=SOME_ERRORS -->
+      <p>There were some errors:</p>
+      <!-- /TMPL_IF -->
+
+    <form method="post"
+	  action="<!-- TMPL_VAR NAME=CGI_BASE -->"
+	  enctype="multipart/form-data">
+      <table summary="file upload summary">
+	<tr>
+	  <th>
+	    Spreadsheet
+	  </th>
+	  <td>
+	    <!-- TMPL_LOOP NAME=SHEETS_LOADED -->
+	      <!-- TMPL_VAR NAME=NAME --><br/>
+            <!-- /TMPL_LOOP -->
+	  </td>
+	</tr>
+        <tr>
+	  <th>
+	    Data file archives
+	  </th>
+	  <td>
+	    <!-- TMPL_LOOP NAME=FILES_LOADED -->
+	      <!-- TMPL_VAR NAME=NAME --><br/>
+            <!-- /TMPL_LOOP -->
+	  </td>
+        </tr>
+      </table>
+
+      <div class="navbar">
+      <input type="hidden"
+	     name="experiment"
+	     value="<!-- TMPL_VAR NAME=EXPT_ID -->"/>
+      <input type="hidden"
+	     name="<!-- TMPL_VAR NAME=DATA_FILE -->"
+	     value="<!-- TMPL_VAR NAME=FILES_OK -->"/>
+      <input type="hidden"
+	     name="<!-- TMPL_VAR NAME=SPREADSHEET -->"
+	     value="<!-- TMPL_VAR NAME=SHEETS_OK -->"/>
+      <input type="submit"
+	     name="<!-- TMPL_VAR NAME=RM -->"
+	     value="<!-- TMPL_VAR NAME=UPLOAD -->"
+	     class="button" />
+      <input type="submit"
+	     name="<!-- TMPL_VAR NAME=RM -->"
+	     value="<!-- TMPL_VAR NAME=SUBMIT -->"
+	     onclick="return confirm('Are you sure? You will be unable to make further changes.');"
+	     class="button" />
+      <input type="submit"
+	     name="<!-- TMPL_VAR NAME=RM -->"
+	     value="<!-- TMPL_VAR NAME=CHOOSE -->"
+	     class="button" />
+      </div>
+
+    </form>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/insert.html b/lib/ArrayExpress/AutoSubmission/templates/insert.html
new file mode 100644
index 0000000..b9bdde3
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/insert.html
@@ -0,0 +1,8 @@
+<!-- TMPL_INCLUDE HEADER -->
+    <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+    <p class="text">Successfully created experiment "<!-- TMPL_VAR NAME=EXPT_NAME -->".</p>
+
+    <!-- TMPL_INCLUDE NAME="_display.html" -->
+
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/intro.html b/lib/ArrayExpress/AutoSubmission/templates/intro.html
new file mode 100644
index 0000000..57fd703
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/intro.html
@@ -0,0 +1,65 @@
+<!-- TMPL_INCLUDE HEADER -->
+    <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+    <p class="text">Welcome to the <!-- TMPL_VAR NAME=EXPT_TYPE -->
+    submissions system. This web page will allow you to submit your
+    data files with experimental annotation entered in a
+    spreadsheet. If you wish, you may have a template spreadsheet
+    automatically generated for you. Alternatively, if you are an
+    experienced user of Tab2MAGE, you may create your own spreadsheet
+    using the <a
+    href="http://tab2mage.sourceforge.net/docs/index.html">Tab2MAGE
+    documentation</a>.</p>
+
+    <p class="text">Note that if you wish to submit your data using
+    the more flexible <a
+    href="http://tab2mage.sourceforge.net/docs/magetab_docs.html">MAGE-TAB
+    format</a>, you should use our <a
+    href="http://www.ebi.ac.uk/cgi-bin/microarray/magetab.cgi">MAGE-TAB
+    submissions web page</a>. MAGE-TAB is particularly recommended
+    in the following circumstances:</p>
+
+    <ul>
+
+      <li>Submission of high-throughput sequencing data (e.g., Solexa,
+      454).</li>
+
+      <li>Submission of other non array-based data (e.g.,
+      metabolomics, proteomics).</li>
+
+      <li>Cases in which you wish to precisely specify annotation
+      terms and their source <br/> (e.g., disease state terms, and their
+      accession numbers, from a disease ontology).</li>
+
+    </ul>
+
+    <p class="text">Please see <a
+    href="http://tab2mage.sourceforge.net/docs/magetab_vs_tab2mage.html">this
+    comparison page</a> for more detail. ArrayExpress will continue to
+    support both Tab2MAGE and MAGE-TAB submissions; if, however, you
+    wish to convert your Tab2MAGE spreadsheet into a MAGE-TAB
+    document, a <a
+    href="http://tab2mage.sourceforge.net/index.html#tabconverter">Tab2MAGE
+    to MAGE-TAB converter tool</a> has been created and made <a
+    href="http://sourceforge.net/project/showfiles.php?group_id=120325&package_id=226041">available
+    for download here</a>.</p>
+
+    <p class="text">Login to start a submission and generate a
+    template. Please use the "New submitter" link on this page if you
+    need to create a new account.</p>
+
+    <p class="text">Full help documentation is available using the links to
+    the side of this page.</p>
+
+    <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+
+      <div class="navbar">
+      <input type="submit"
+	     name="<!-- TMPL_VAR NAME=RM -->"
+             value="<!-- TMPL_VAR NAME=LOGIN -->"/>
+      <input type="submit"
+	     name="<!-- TMPL_VAR NAME=RM -->"
+             value="<!-- TMPL_VAR NAME=SIGNUP -->"/>
+      </div>
+    </form>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/login.html b/lib/ArrayExpress/AutoSubmission/templates/login.html
new file mode 100644
index 0000000..bab6ba5
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/login.html
@@ -0,0 +1,65 @@
+<!-- TMPL_INCLUDE HEADER -->
+    <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+    <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+      <p class="text">Please enter your username and password in the fields below:</p>
+
+      <!-- TMPL_IF NAME=FAILED -->
+      <p class="error">Error: login failed. Please check your username and password.</p>
+      <!-- /TMPL_IF -->
+
+      <table summary="login form">
+	<tr>
+	  <td>Username</td>
+	  <td>
+	    <input id="authen_loginfield"
+	           tabindex="1"
+	           type="text"
+	           name="<!-- TMPL_VAR NAME=USERNAME -->"
+	           size="30"
+	           value="" />
+	  </td>
+	</tr>
+	<tr>
+	  <td>Password</td>
+	  <td>
+	    <input id="authen_passwordfield"
+	           tabindex="2"
+	           type="password"
+	           name="<!-- TMPL_VAR NAME=PASSWORD -->"
+	           size="30" />
+	  </td>
+	</tr>
+	<tr>
+	  <td> </td>
+	  <td>
+	    <div class="buttons">
+	      <input type="hidden"
+		     name="<!-- TMPL_VAR NAME=RM -->"
+		     value="<!-- TMPL_VAR NAME=LOGIN -->" />
+	      <input type="submit"
+		     tabindex="3"
+		     name="login"
+		     value="Log in"
+		     class="button" />
+	      <input type="reset"
+		     tabindex="4"
+		     name="resetlogin"
+		     value="Reset"
+		     class="button" />
+	    </div>
+	  </td>
+	</tr>
+      </table>
+    </form>
+    <p class="text">If you do not yet have a username and password, please sign up using this link:</p>
+    <div class="navbar" align="center">
+    <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+          <input type="submit"
+	         tabindex="5"
+	         name="<!-- TMPL_VAR NAME=RM -->"
+	         value="<!-- TMPL_VAR NAME=SIGNUP -->"
+	         class="button" />
+    </form>
+    </div>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/logout.html b/lib/ArrayExpress/AutoSubmission/templates/logout.html
new file mode 100644
index 0000000..f5014a8
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/logout.html
@@ -0,0 +1,13 @@
+<!-- TMPL_INCLUDE HEADER -->
+    <h2>You have successfully logged out.</h2>
+
+    <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+
+      <div class="navbar">
+      <input type="submit"
+	     name="<!-- TMPL_VAR NAME=RM -->"
+             value="<!-- TMPL_VAR NAME=LOGIN -->"/>
+      </div>
+
+    </form>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/magetab_intro.html b/lib/ArrayExpress/AutoSubmission/templates/magetab_intro.html
new file mode 100644
index 0000000..f5cf8f2
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/magetab_intro.html
@@ -0,0 +1,42 @@
+<!-- TMPL_INCLUDE HEADER -->
+    <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+    <p class="text">Welcome to the <!-- TMPL_VAR NAME=EXPT_TYPE -->
+    submissions system. This web page will allow you to submit your
+    data files with experimental annotation entered in a MAGE-TAB
+    document. Please see our <a
+    href="http://tab2mage.sourceforge.net/docs/magetab_subs.html">MAGE-TAB
+    submission help notes</a> to get you started. If you wish, you may
+    use this web page to generate a template document which you can
+    download and complete on your computer. Alternatively, if you are
+    an experienced user of MAGE-TAB, you may create your own document
+    using the <a
+    href="http://tab2mage.sourceforge.net/docs/magetab_docs.html">MAGE-TAB
+    documentation</a>.</p>
+
+    <p class="text">Note that if you wish to submit your data using
+    our simpler <a
+    href="http://tab2mage.sourceforge.net/docs/spreadsheet.html">Tab2MAGE
+    format</a>, you should use our <a
+    href="http://www.ebi.ac.uk/cgi-bin/microarray/tab2mage.cgi">Tab2MAGE
+    submissions web page</a>.</p>
+
+    <p class="text">Login to start a submission and generate a
+    template. Please use the "New submitter" link on this page if you
+    need to create a new account.</p>
+
+    <p class="text">Full help documentation is available using the links to
+    the side of this page.</p>
+
+    <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+
+      <div class="navbar">
+      <input type="submit"
+	     name="<!-- TMPL_VAR NAME=RM -->"
+             value="<!-- TMPL_VAR NAME=LOGIN -->"/>
+      <input type="submit"
+	     name="<!-- TMPL_VAR NAME=RM -->"
+             value="<!-- TMPL_VAR NAME=SIGNUP -->"/>
+      </div>
+    </form>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/magetab_template.html b/lib/ArrayExpress/AutoSubmission/templates/magetab_template.html
new file mode 100644
index 0000000..417b811
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/magetab_template.html
@@ -0,0 +1,37 @@
+<!-- TMPL_INCLUDE HEADER -->
+    <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+    <p class="text">Template successfully generated for experiment "<!-- TMPL_VAR NAME=EXPT_NAME -->".</p>
+
+    <p class="text">Please download your spreadsheet template file
+    here (right-click or control-click to save directly to disk):</p>
+
+    <h3 class="text">
+    <a href="<!-- TMPL_VAR NAME=SS_LINK -->" target="<!-- TMPL_VAR NAME=SS_LINK -->">Spreadsheet template</a>
+    </h3>
+
+    <p class="text">Note that template files are only stored on this server for 24 hours.</p>
+
+    <p class="text">See the online <a
+    href="http://tab2mage.sourceforge.net/docs/magetab_docs.html">MAGE-TAB
+    documentation</a> for help on filling out your template
+    spreadsheet. Please also see these <a
+    href="http://tab2mage.sourceforge.net/docs/magetab_subs.html#realexamples">real-world
+    example spreadsheets</a> for tips on specific
+    experiment types.</p>
+
+    <div class="navbar">
+    <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+      <input type="hidden"
+	     name="experiment"
+	     value="<!-- TMPL_VAR NAME=EXPT_ID -->"/>
+      <input type="submit"
+	     name="<!-- TMPL_VAR NAME=RM -->"
+             value="<!-- TMPL_VAR NAME=UPLOAD -->"/>
+      <input type="submit"
+	     name="<!-- TMPL_VAR NAME=RM -->"
+             value="<!-- TMPL_VAR NAME=CHOOSE -->"/>
+    </form>
+    </div>
+
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/magetab_upload.html b/lib/ArrayExpress/AutoSubmission/templates/magetab_upload.html
new file mode 100644
index 0000000..724da37
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/magetab_upload.html
@@ -0,0 +1,152 @@
+<!-- TMPL_INCLUDE HEADER -->
+    <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+    <p class="text">Uploading files for your experiment "<!-- TMPL_VAR NAME=EXPT_NAME -->"</p>
+
+    <p class="text">Please upload the following:</p>
+
+    <ul class="text">
+      <li>One completed plain-text spreadsheet file containing your
+      experiment annotation (IDF and SDRF)</li>
+
+      <li>One or more zipped (.zip) or tar-gzipped (.tar.gz, .tgz)
+      archives containing all your data files.</li>
+    </ul>
+
+    <p class="text">Note: if you wish to upload your IDF and SDRF
+    files as separate documents, please upload your SDRF in the same
+    package as your data files.</p>
+
+    <p class="text">See the online <a
+    href="http://tab2mage.sourceforge.net/docs/magetab_docs.html">MAGE-TAB
+    documentation</a> for help on filling out your spreadsheet. Please
+    also see these <a
+    href="http://tab2mage.sourceforge.net/examples/magetab/real/">real-world
+    example spreadsheets</a> for tips on specific
+    experiment types.</p>
+
+    <p class="text">Supported data file formats are described in the <a
+    href="http://tab2mage.sourceforge.net/docs/datafiles.html">Tab2MAGE
+    help notes</a>.</p>
+
+    <p class="text">Please note that uploading a file with the same
+    name as a previously uploaded file will overwrite the old file
+    with the new one.</p>
+
+    <p class="text">To replace a previously uploaded spreadsheet,
+    simply upload a new one using the form below. This will substitute
+    your new spreadsheet for the old one.</p>
+
+    <p class="text">You may upload your data files in as many separate
+    compressed archives as are needed; the form below adds new data
+    file uploads without replacing the old files (unless they are
+    named the same).</p>
+
+    <p class="text">Once you have uploaded these files you will be
+    able to finalize your submission.</p>
+
+      <!-- TMPL_IF NAME=SOME_ERRORS -->
+      <p>There were some errors:</p>
+      <!-- /TMPL_IF -->
+
+      <!-- TMPL_IF NAME=ERR_EXPERIMENT -->
+      <!-- TMPL_VAR NAME=ERR_EXPERIMENT --><br/>
+      <!-- /TMPL_IF -->
+
+      <!-- TMPL_IF NAME=ERR_UPLOAD_FILES -->
+      <!-- TMPL_VAR NAME=ERR_UPLOAD_FILES --><br/>
+      <!-- /TMPL_IF -->
+
+      <!-- TMPL_IF NAME=ERR_SPREADSHEET -->
+      <!-- TMPL_VAR NAME=ERR_SPREADSHEET --><br/>
+      <!-- /TMPL_IF -->
+
+      <!-- TMPL_IF NAME=ERR_DATA_FILE -->
+      <!-- TMPL_VAR NAME=ERR_DATA_FILE --><br/>
+      <!-- /TMPL_IF -->
+
+    <form method="post"
+	  name="upload"
+	  action="<!-- TMPL_VAR NAME=CGI_BASE -->"
+	  enctype="multipart/form-data">
+      <table summary="file upload form">
+	<tr>
+	  <th>
+	    Upload type
+	  </th>
+	  <th>
+	    Select file
+	  </th>
+	  <th>
+	    Files already uploaded
+	  </th>
+	</tr>
+        <tr>
+	  <td>
+	    Upload/replace spreadsheet or IDF:
+	  </td>
+	  <td>
+            <input type="file"
+		   name="<!-- TMPL_VAR NAME=SPREADSHEET -->" />
+	  </td>
+	  <td>
+	    <!-- TMPL_LOOP NAME=SHEETS_LOADED -->
+	      <!-- TMPL_VAR NAME=NAME --><br/>
+            <!-- /TMPL_LOOP -->
+	  </td>
+	</tr>
+        <tr>
+	  <td>
+	    Upload/add Data file archive (with SDRF if used):
+	  </td>
+	  <td>
+	    <input type="file"
+		   name="<!-- TMPL_VAR NAME=DATA_FILE -->" />
+	  </td>
+	  <td>
+	    <!-- TMPL_LOOP NAME=FILES_LOADED -->
+	      <!-- TMPL_VAR NAME=NAME --><br/>
+            <!-- /TMPL_LOOP -->
+	  </td>
+        </tr>
+        <tr>
+	  <td>
+	     
+	  </td>
+	  <td>Click here to upload the selected files:
+            <input type="submit"
+		   name="<!-- TMPL_VAR NAME=RM -->"
+		   value="<!-- TMPL_VAR NAME=FILE_UPLOAD -->"
+		   class="button"
+		   <!-- TMPL_IF NAME=SHEETS_LOADED -->
+  		     onclick="if (document.upload.<!-- TMPL_VAR NAME=SPREADSHEET -->.value) {return confirm('Really overwrite previously uploaded spreadsheet?');}"
+                   <!-- /TMPL_IF -->
+	    />
+	  </td>
+	  <td>
+	     
+	  </td>
+	</tr>
+      </table>
+
+	<p class="text">If you are not yet ready to upload files,
+	please use the link below to return to the experiment
+	listing.</p>
+	
+      <div class="navbar">
+      <input type="hidden"
+	     name="experiment"
+	     value="<!-- TMPL_VAR NAME=EXPT_ID -->"/>
+      <input type="submit"
+	     name="<!-- TMPL_VAR NAME=RM -->"
+	     value="<!-- TMPL_VAR NAME=CHOOSE -->"
+	     class="button" />
+      <input type="submit"
+	     name="<!-- TMPL_VAR NAME=RM -->"
+	     value="<!-- TMPL_VAR NAME=SUBMIT -->"
+	     onclick="return confirm('Are you sure? You will be unable to make further changes.');"
+	     class="button" />
+      </div>
+
+    </form>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/signup.html b/lib/ArrayExpress/AutoSubmission/templates/signup.html
new file mode 100644
index 0000000..1846599
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/signup.html
@@ -0,0 +1,82 @@
+<!-- TMPL_INCLUDE HEADER -->
+    <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+    <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+      <p class="text">Please complete all the following fields:</p>
+
+      <!-- TMPL_IF NAME=SOME_ERRORS -->
+      <p>There were some errors:</p>
+      <!-- /TMPL_IF -->
+
+      <!-- TMPL_IF NAME=ERR_USERNAME -->
+      <!-- TMPL_VAR NAME=ERR_USERNAME --><br/>
+      <!-- /TMPL_IF -->
+
+      <!-- TMPL_IF NAME=ERR_PASSWORD -->
+      <!-- TMPL_VAR NAME=ERR_PASSWORD --><br/>
+      <!-- /TMPL_IF -->
+
+      <!-- TMPL_IF NAME=ERR_REALNAME -->
+      <!-- TMPL_VAR NAME=ERR_REALNAME --><br/>
+      <!-- /TMPL_IF -->
+
+      <!-- TMPL_IF NAME=ERR_EMAIL -->
+      <!-- TMPL_VAR NAME=ERR_EMAIL --><br/>
+      <!-- /TMPL_IF -->
+
+      <p class="text">N.B. both username and password must be 6 to 10
+      characters long.</p>
+
+      <table summary="user details entry">
+	<tr>
+	  <td>Name</td>
+	  <td<!-- TMPL_IF NAME=ERR_REALNAME --> class="fieldWithErrors"<!-- /TMPL_IF -->>
+	    <input id="authen_namefield"
+	           type="text"
+	           name="<!-- TMPL_VAR NAME=REALNAME -->"
+	           size="30"
+	           value=""/>
+	  </td>
+	</tr>
+	<tr>
+	  <td>Desired username</td>
+	  <td<!-- TMPL_IF NAME=ERR_USERNAME --> class="fieldWithErrors"<!-- /TMPL_IF -->>
+	    <input id="authen_loginfield"
+	           type="text"
+	           name="<!-- TMPL_VAR NAME=USERNAME -->"
+	           size="30"
+	           value=""/></td>
+	</tr>
+	<tr>
+	  <td>Password</td>
+	  <td<!-- TMPL_IF NAME=ERR_PASSWORD --> class="fieldWithErrors"<!-- /TMPL_IF -->>
+	    <input id="authen_passwordfield"
+	           type="password"
+	           name="<!-- TMPL_VAR NAME=PASSWORD -->"
+	           size="30"/></td>
+	</tr>
+	<tr>
+	  <td>Confirm password</td>
+	  <td<!-- TMPL_IF NAME=ERR_PASSWORD --> class="fieldWithErrors"<!-- /TMPL_IF -->>
+	    <input id="authen_passwordfield2"
+	           type="password"
+	           name="<!-- TMPL_VAR NAME=PASSWORD2 -->"
+	           size="30"/></td>
+	</tr>
+	<tr>
+	  <td>Email address</td>
+	  <td<!-- TMPL_IF NAME=ERR_EMAIL --> class="fieldWithErrors"<!-- /TMPL_IF -->>
+	    <input id="authen_email"
+	           type="text"
+	           name="<!-- TMPL_VAR NAME=EMAIL -->"
+	           size="30"/></td>
+	</tr>
+      </table>
+      <div class="navbar">
+      <input type="submit"
+	     name="<!-- TMPL_VAR NAME=RM -->"
+	     value="<!-- TMPL_VAR NAME=CREATEUSER -->"
+	     class="button"/>
+      </div>
+    </form>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/submitted.html b/lib/ArrayExpress/AutoSubmission/templates/submitted.html
new file mode 100644
index 0000000..0d3bcc4
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/submitted.html
@@ -0,0 +1,32 @@
+<!-- TMPL_INCLUDE HEADER -->
+    <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+      <!-- TMPL_IF NAME=SOME_ERRORS -->
+      <p>There were some errors:</p>
+
+      <!-- TMPL_ELSE -->
+
+    <p class="text">The experiment "<!-- TMPL_VAR NAME=EXPT_NAME -->"
+    was successfully submitted. The ArrayExpress curators will contact
+    you shortly regarding your submission. In the meantime, please
+    contact <a href="mailto:<!-- TMPL_VAR NAME=CURATOR_EMAIL -->">
+      <!-- TMPL_VAR NAME=CURATOR_EMAIL --></a> with any questions.</p>
+
+      <!-- /TMPL_IF -->
+
+    <form method="post"
+	  action="<!-- TMPL_VAR NAME=CGI_BASE -->"
+	  enctype="multipart/form-data">
+
+      <div class="navbar">
+      <input type="hidden"
+	     name="experiment"
+	     value="<!-- TMPL_VAR NAME=EXPT_ID -->"/>
+      <input type="submit"
+	     name="<!-- TMPL_VAR NAME=RM -->"
+	     value="<!-- TMPL_VAR NAME=CHOOSE -->"
+	     class="button" />
+      </div>
+
+    </form>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/subs_notify_curator.txt b/lib/ArrayExpress/AutoSubmission/templates/subs_notify_curator.txt
new file mode 100644
index 0000000..03e55dd
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/subs_notify_curator.txt
@@ -0,0 +1,15 @@
+Dear Curators,
+
+A new <TMPL_VAR NAME=TYPE> experiment has just been submitted:
+
+Name:       <TMPL_VAR NAME=EXPTNAME>
+Directory:  <TMPL_VAR NAME=EXPTDIR>
+User:       <TMPL_VAR NAME=REALNAME>
+Login:      <TMPL_VAR NAME=LOGIN>
+Email:      <TMPL_VAR NAME=SUBMTR_EMAIL>
+
+Submission date: <TMPL_VAR NAME=SUBSDATE>
+
+Best regards,
+
+your friendly neighborhood submissions system.
diff --git a/lib/ArrayExpress/AutoSubmission/templates/template.html b/lib/ArrayExpress/AutoSubmission/templates/template.html
new file mode 100644
index 0000000..e0d021b
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/template.html
@@ -0,0 +1,37 @@
+<!-- TMPL_INCLUDE HEADER -->
+    <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+    <p class="text">Template successfully generated for experiment "<!-- TMPL_VAR NAME=EXPT_NAME -->".</p>
+
+    <p class="text">Please download your spreadsheet template file
+    here (right-click or control-click to save directly to disk):</p>
+
+    <h3 class="text">
+    <a href="<!-- TMPL_VAR NAME=SS_LINK -->" target="<!-- TMPL_VAR NAME=SS_LINK -->">Spreadsheet template</a>
+    </h3>
+
+    <p class="text">Note that template files are only stored on this server for 24 hours.</p>
+
+    <p class="text">See the online <a
+    href="http://tab2mage.sourceforge.net/docs/spreadsheet.html">Tab2MAGE
+    documentation</a> for help on filling out your template
+    spreadsheet. Please also see these <a
+    href="http://tab2mage.sourceforge.net/docs/spreadsheet.html#realexamples">real-world
+    example spreadsheets</a> for tips on specific
+    experiment types.</p>
+
+    <div class="navbar">
+    <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+      <input type="hidden"
+	     name="experiment"
+	     value="<!-- TMPL_VAR NAME=EXPT_ID -->"/>
+      <input type="submit"
+	     name="<!-- TMPL_VAR NAME=RM -->"
+             value="<!-- TMPL_VAR NAME=UPLOAD -->"/>
+      <input type="submit"
+	     name="<!-- TMPL_VAR NAME=RM -->"
+             value="<!-- TMPL_VAR NAME=CHOOSE -->"/>
+    </form>
+    </div>
+
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/update.html b/lib/ArrayExpress/AutoSubmission/templates/update.html
new file mode 100644
index 0000000..f6cce62
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/update.html
@@ -0,0 +1,8 @@
+<!-- TMPL_INCLUDE HEADER -->
+    <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+    <p class="text">Successfully updated experiment "<!-- TMPL_VAR NAME=EXPT_NAME -->".</p>
+
+    <!-- TMPL_INCLUDE NAME="_display.html" -->
+
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/upload.html b/lib/ArrayExpress/AutoSubmission/templates/upload.html
new file mode 100644
index 0000000..7306b3d
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/upload.html
@@ -0,0 +1,148 @@
+<!-- TMPL_INCLUDE HEADER -->
+    <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+    <p class="text">Uploading files for your experiment "<!-- TMPL_VAR NAME=EXPT_NAME -->"</p>
+
+    <p class="text">Please upload the following:</p>
+
+    <ul class="text">
+      <li>One completed plain-text spreadsheet file containing your
+      experiment annotation</li>
+
+      <li>One or more zipped (.zip) or tar-gzipped (.tar.gz, .tgz)
+      archives containing all your data files.</li>
+    </ul>
+
+    <p class="text">See the online <a
+    href="http://tab2mage.sourceforge.net/docs/spreadsheet.html">Tab2MAGE
+    documentation</a> for help on filling out your spreadsheet. Please
+    also see these <a
+    href="http://tab2mage.sourceforge.net/docs/spreadsheet.html#realexamples">real-world
+    example spreadsheets</a> for tips on specific
+    experiment types.</p>
+
+    <p class="text">Supported data file formats are described in the <a
+    href="http://tab2mage.sourceforge.net/docs/datafiles.html">Tab2MAGE
+    help notes</a>.</p>
+
+    <p class="text">Please note that uploading a file with the same
+    name as a previously uploaded file will overwrite the old file
+    with the new one.</p>
+
+    <p class="text">To replace a previously uploaded spreadsheet,
+    simply upload a new one using the form below. This will substitute
+    your new spreadsheet for the old one.</p>
+
+    <p class="text">You may upload your data files in as many separate
+    compressed archives as are needed; the form below adds new data
+    file uploads without replacing the old files (unless they are
+    named the same).</p>
+
+    <p class="text">Once you have uploaded these files you will be
+    able to finalize your submission.</p>
+
+      <!-- TMPL_IF NAME=SOME_ERRORS -->
+      <p>There were some errors:</p>
+      <!-- /TMPL_IF -->
+
+      <!-- TMPL_IF NAME=ERR_EXPERIMENT -->
+      <!-- TMPL_VAR NAME=ERR_EXPERIMENT --><br/>
+      <!-- /TMPL_IF -->
+
+      <!-- TMPL_IF NAME=ERR_UPLOAD_FILES -->
+      <!-- TMPL_VAR NAME=ERR_UPLOAD_FILES --><br/>
+      <!-- /TMPL_IF -->
+
+      <!-- TMPL_IF NAME=ERR_SPREADSHEET -->
+      <!-- TMPL_VAR NAME=ERR_SPREADSHEET --><br/>
+      <!-- /TMPL_IF -->
+
+      <!-- TMPL_IF NAME=ERR_DATA_FILE -->
+      <!-- TMPL_VAR NAME=ERR_DATA_FILE --><br/>
+      <!-- /TMPL_IF -->
+
+    <form method="post"
+	  name="upload"
+	  action="<!-- TMPL_VAR NAME=CGI_BASE -->"
+	  enctype="multipart/form-data">
+      <table summary="file upload form">
+	<tr>
+	  <th>
+	    Upload type
+	  </th>
+	  <th>
+	    Select file
+	  </th>
+	  <th>
+	    Files already uploaded
+	  </th>
+	</tr>
+        <tr>
+	  <td>
+	    Upload/replace Spreadsheet:
+	  </td>
+	  <td>
+            <input type="file"
+		   name="<!-- TMPL_VAR NAME=SPREADSHEET -->" />
+	  </td>
+	  <td>
+	    <!-- TMPL_LOOP NAME=SHEETS_LOADED -->
+	      <!-- TMPL_VAR NAME=NAME --><br/>
+            <!-- /TMPL_LOOP -->
+	  </td>
+	</tr>
+        <tr>
+	  <td>
+	    Upload/add Data file archive:
+	  </td>
+	  <td>
+	    <input type="file"
+		   name="<!-- TMPL_VAR NAME=DATA_FILE -->" />
+	  </td>
+	  <td>
+	    <!-- TMPL_LOOP NAME=FILES_LOADED -->
+	      <!-- TMPL_VAR NAME=NAME --><br/>
+            <!-- /TMPL_LOOP -->
+	  </td>
+        </tr>
+        <tr>
+	  <td>
+	     
+	  </td>
+	  <td>Click here to upload the selected files:
+            <input type="submit"
+		   name="<!-- TMPL_VAR NAME=RM -->"
+		   value="<!-- TMPL_VAR NAME=FILE_UPLOAD -->"
+		   class="button"
+		   <!-- TMPL_IF NAME=SHEETS_LOADED -->
+  		     onclick="if (document.upload.<!-- TMPL_VAR NAME=SPREADSHEET -->.value) {return confirm('Really overwrite previously uploaded spreadsheet?');}"
+                   <!-- /TMPL_IF -->
+	    />
+	  </td>
+	  <td>
+	     
+	  </td>
+	</tr>
+      </table>
+
+	<p class="text">If you are not yet ready to upload files,
+	please use the link below to return to the experiment
+	listing.</p>
+	
+      <div class="navbar">
+      <input type="hidden"
+	     name="experiment"
+	     value="<!-- TMPL_VAR NAME=EXPT_ID -->"/>
+      <input type="submit"
+	     name="<!-- TMPL_VAR NAME=RM -->"
+	     value="<!-- TMPL_VAR NAME=CHOOSE -->"
+	     class="button" />
+      <input type="submit"
+	     name="<!-- TMPL_VAR NAME=RM -->"
+	     value="<!-- TMPL_VAR NAME=SUBMIT -->"
+	     onclick="return confirm('Are you sure? You will be unable to make further changes.');"
+	     class="button" />
+      </div>
+
+    </form>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/Curator/Common.pm b/lib/ArrayExpress/Curator/Common.pm
new file mode 100644
index 0000000..3a70c1a
--- /dev/null
+++ b/lib/ArrayExpress/Curator/Common.pm
@@ -0,0 +1,620 @@
+#!/usr/bin/env perl
+#
+# Module to provide basic methods used by the other ArrayExpress::Curator
+# modules.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Common.pm 2013 2008-03-31 17:23:14Z tfrayner $
+#
+
+package ArrayExpress::Curator::Common;
+
+use strict;
+use warnings;
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../index.html">
+                <img src="../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: Common.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Curator::Common.pm - a module providing some simple
+utility functions and regexps.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Curator::Common qw(date_now $RE_EMPTY_STRING);
+ 
+ if ( $test =~ $RE_EMPTY_STRING ) {
+    print date_now();
+ }
+
+=head1 DESCRIPTION
+
+This is a simple module providing utility functions and regexp
+patterns used elsewhere in the Tab2MAGE package.
+
+=head1 FUNCTIONS
+
+=over 2
+
+=item C<strip_discards( $indexcols, $line_array )>
+
+Sub to strip values out of an arrayref ($line_array) based on an
+arrayref of unwanted array indices ($indexcols). Does not modify the
+input lists, but instead returns a suitably stripped arrayref.
+
+=item C<round( $number, $precision )>
+
+A simple rounding function that returns $number rounded to $precision
+decimal places.
+
+=item C<get_indexcol( $list, $name )>
+
+Function to return the first index within @$list matching the string
+or regexp passed as $name. Returns -1 on failure.
+
+=item C<check_linebreaks( $path )>
+
+Takes a filename as an argument, checks for Mac, Unix or DOS line
+endings by reading the whole file in chunks, and regexp matching the
+various linebreak types.  Returns the appropriate linebreak for
+acceptable line breaks (line breaks must be unanimous), undef if a
+consensus is unreachable.
+
+=item C<clean_hash( $hashref )>
+
+Strip out undef or empty string values from $hashref.
+
+=item C<date_now()>
+
+Return current date and time as a MAGE best practice date-time string
+(uses mage_date(), below).
+
+=item C<mage_date( $time )>
+
+When passed the return value from e.g. perl's time() built-in, returns
+a MAGE best practice date-time string. This string will always be from
+the UTC/GMT time zone.
+
+=item C<untaint( $string )>
+
+A convenient data untainting function which replaces any run of
+non-whitelisted characters with a single underscore.
+
+=item C<find_cdf( $name, $dir )>
+
+Given a filename and an optional directory, returns an actual filename
+found in a case-insensitive fashion. Preferentially checks the main
+Affy library directory if set in $CONFIG (see
+L<ArrayExpress::Curator::Config>).
+
+=item C<decamelize( $string )>
+
+Function to convert CamelCase strings to underscore_delimited.
+
+=item C<get_filepath_from_uri( $string, $dir )>
+
+Given a string and an optional directory argument, this function
+determines which URI scheme is in use (default is "file://"), and
+downloads "http://" and "ftp://" URIs to either the indicated
+filesystem directory or the current working directory. Returns the
+filesystem path to the file.
+
+=back
+
+=head1 REGEXPS
+
+=over 2
+
+=item C<$RE_EMPTY_STRING>
+
+Matches an empty string (whitespace ignored).
+
+=item C<$RE_COMMENTED_STRING>
+
+Matches a string beginning with #.
+
+=item C<$RE_SURROUNDED_BY_WHITESPACE>
+
+Matches a string with whitespace on either side; $1 contains the
+string minus the whitespace.
+
+=item C<$RE_WITHIN_PARENTHESES>
+
+Matches a string with parentheses on either side; $1 contains the
+string minus the parentheses.
+
+=item C<$RE_WITHIN_BRACKETS>
+
+Matches a string with brackets on either side; $1 contains the
+string minus the brackets.
+
+=item C<$RE_SQUARE_BRACKETS>
+
+Matches either [ or ]; $1 contains the matched character.
+
+=item C<$RE_LINE_BREAK>
+
+Matches a linebreak (DOS, Unix or MacOS).
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2008.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments. 
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+use Carp;
+use charnames qw( :full );
+
+use Scalar::Util qw( openhandle );
+use File::Spec;
+use IO::File;
+use IO::Handle;
+use Readonly;
+use English qw( -no_match_vars );
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+use base 'Exporter';
+our @EXPORT_OK = qw(
+    strip_discards
+    round
+    get_indexcol
+    check_linebreaks
+    clean_hash
+    date_now
+    mage_date
+    untaint
+    find_cdf
+    decamelize
+    get_filepath_from_uri
+    mx2tab
+
+    $RE_EMPTY_STRING
+    $RE_COMMENTED_STRING
+    $RE_SURROUNDED_BY_WHITESPACE
+    $RE_WITHIN_PARENTHESES
+    $RE_WITHIN_BRACKETS
+    $RE_SQUARE_BRACKETS
+    $RE_LINE_BREAK
+);
+
+# Define some standard regexps:
+Readonly our $RE_EMPTY_STRING             => qr{\A \s* \z}xms;
+Readonly our $RE_COMMENTED_STRING         => qr{\A [\"\s]* \#}xms;
+Readonly our $RE_SURROUNDED_BY_WHITESPACE => qr{\A [\"\s]* (.*?) [\"\s]* \z}xms;
+Readonly our $RE_WITHIN_PARENTHESES       => qr{\( \s* (.*?) \s* \)}xms;
+Readonly our $RE_WITHIN_BRACKETS          => qr{\[ \s* (.*?) \s* \]}xms;
+Readonly our $RE_SQUARE_BRACKETS          => qr{( [\[\]] )}xms;
+Readonly our $RE_LINE_BREAK               => qr{[\r\n]* \z}xms;
+
+##################################################################
+# Given a list and a value in that list, returns the value index #
+##################################################################
+
+sub get_indexcol {
+    my ( $columnlist, $columnname ) = @_;
+
+    ref $columnlist eq 'ARRAY'
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    defined($columnname) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    # For efficiency; N.B. $columnname may contain whitepace, so no /x
+    # modifier here. Similarly we rely on the caller to specify /i or not.
+    my $re;
+
+    # Empty string is a special case (won't match \b)
+    if ( $columnname eq q{} ) {
+	$re = qr/\A\s*$columnname\s*\z/ms;
+    }
+    else{
+	$re = qr/\A\s*\b$columnname\b\s*\z/ms;
+    }
+    my $num_cols = scalar @{$columnlist};
+
+    for ( my $i = 0; $i < $num_cols; $i++ ) {
+        return $i if $columnlist->[$i] =~ $re;
+    }
+    return -1;
+}
+
+sub strip_discards {
+
+    # Sub to strip values out of an array (@line_array) based on a list
+    # of unwanted array indices (@indexcols). Does not modify the input
+    # lists.
+
+    my ( $indexcols, $line_array ) = @_;
+
+    ref $indexcols eq 'ARRAY'
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    ref $line_array eq 'ARRAY'
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my %to_be_discarded = map { $_ => 1 } @$indexcols;
+
+    my @new_line;
+    my $last_index = $#$line_array;    # Calculate this just once
+    foreach my $i ( 0 .. $last_index ) {
+        push( @new_line, $line_array->[$i] )
+            unless ( $to_be_discarded{$i} );
+    }
+
+    return \@new_line;
+
+}
+
+##############################################################
+# Round function to deal with floating-point rounding errors #
+##############################################################
+
+# stolen shamelessly from www.perlmonks.org
+
+sub round {
+    my ( $number, $precision ) = @_;
+    my $sign = ( $number > 0 ) ? 1 : -1;    # store the sign for later
+
+    $precision ||= 0;    # $precision should not be undefined
+
+    $number *= 10**$precision;    # move the decimal place
+                                  # $precision places to the
+                                  # right
+
+    $number = int( $number + .5 * $sign );    # add 0.5, correct the sign and
+                                              # truncate the number after the
+                                              # decimal point, thereby
+                                              # rounding it
+
+    return ( $number / 10**$precision );   # move the decimal place back again
+}
+
+sub clean_hash {
+
+    # Strip out undef or empty string values
+
+    my ($hashref) = @_;
+
+    ref $hashref eq 'HASH'
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my %cleaned;
+    while ( my ( $key, $value ) = each %$hashref ) {
+        $cleaned{$key} = $value if ( defined($value) && $value ne q{} );
+    }
+
+    return \%cleaned;
+
+}
+
+sub check_linebreaks {
+
+    # Takes a filename as an argument, checks for Mac, Unix or Dos line
+    # endings by reading the whole file in chunks, and regexp
+    # matching the various linebreak types.  Returns the appropriate
+    # linebreak for acceptable line breaks (line breaks must be
+    # unanimous FIXME), undef for not.
+
+    my $path = shift;
+    my $fh = IO::File->new( $path, '<' )
+        or croak(
+        "Error: Failed to open file $path for linebreak checking: $!\n");
+
+    my $bytelength = -s $path;
+
+    # Count all the line endings. This can get memory intensive
+    # (implicit list generation, can be over 1,000,000 entries for
+    # Affy CEL). We read the file in defined chunks to address this.
+    my ( $unix_count, $mac_count, $dos_count );
+    my $chunk_size          = 3_000_000;    # ~10 chunks to a big CEL file.
+    my $previous_final_char = q{};
+    for ( my $offset = 0; $offset < $bytelength; $offset += $chunk_size ) {
+
+        my $chunk;
+
+	my $bytes_read = read( $fh, $chunk, $chunk_size );
+
+	unless ( defined($bytes_read) ) {
+	    croak("Error reading file chunk at offset $offset ($path): $!\n");
+	}
+
+	# Lists generated implicitly here.
+        $unix_count += () = ( $chunk =~ m{\N{LINE FEED}}g );
+        $mac_count  += () = ( $chunk =~ m{\N{CARRIAGE RETURN}}g );
+        $dos_count  += () = ( $chunk =~ m{\N{CARRIAGE RETURN}\N{LINE FEED}}g );
+
+        # DOS line endings could conceivably be split between chunks.
+	if ( $bytes_read ) {    # Skip if at end of file.
+	    if (   ( substr( $chunk, 0, 1 ) eq "\N{LINE FEED}" )
+		&& ( $previous_final_char eq "\N{CARRIAGE RETURN}" ) ) {
+		$dos_count++;
+	    }
+	    $previous_final_char = substr( $chunk, -1, 1 );
+	}
+    }
+
+    close($fh)
+        or croak("Error closing file $path in sub check_linebreaks: $!\n");
+
+    my $dos  = $dos_count;
+    my $mac  = $mac_count - $dos_count;
+    my $unix = $unix_count - $dos_count;
+
+    # Set to undef on failure.
+    my $line_ending = undef;
+
+    # Determine the file line endings format, return the "standard" line
+    # ending to use
+    if ( $unix && !$mac && !$dos ) {    # Unix
+        $line_ending = "\N{LINE FEED}";
+    }
+    elsif ( $mac && !$unix && !$dos ) {    # Mac
+        $line_ending = "\N{CARRIAGE RETURN}";
+    }
+    elsif ( $dos && !$mac && !$unix ) {    # DOS
+        $line_ending = "\N{CARRIAGE RETURN}\N{LINE FEED}";
+    }
+
+    # Calling in scalar context just gives $line_ending.
+    my $counts = {
+        unix => $unix,
+        dos  => $dos,
+        mac  => $mac,
+    };
+    return wantarray ? ( $counts, $line_ending ) : $line_ending;
+
+}
+
+sub date_now {
+
+    # Return current date and time as a MAGE best practice date-time
+    # string.
+
+    return mage_date(time);
+}
+
+sub mage_date {
+
+    my $datenum = shift;
+
+    # Return a MAGE best practice date-time string. Note that the
+    # following "Z" implies UTC, hence we use gmtime here.
+
+    my ( $sec, $min, $hour, $mday, $mon, $year, $wday ) = gmtime($datenum);
+    $mon++;    # localtime starts months from 0
+    $year += 1900;    # localtime starts years from 100
+
+    return sprintf( "%04d-%02d-%02dT%02d:%02d:%02dZ",
+        $year, $mon, $mday, $hour, $min, $sec );
+
+}
+
+sub untaint {
+
+    # Untaint the input (runs with perl -T switch)
+
+    my $string = shift;
+    my @nameparts = $string =~ m/[-a-zA-Z0-9\.@\-\+\_]+/g;
+    $string = join( '_', @nameparts );
+    return $string;
+}
+
+sub find_cdf {
+
+    # Given a filename and an optional directory, returns an actual
+    # filename found in a case-insensitive fashion. Preferentially
+    # checks the main Affy library directory if set in $CONFIG.
+    my ( $name, $dir ) = @_;
+
+    # Check Affy library dir or source dir or working dir.
+    my $querydir  = $CONFIG->get_AFFYMETRIX_LIBRARYPATH()
+	      	 || $dir
+		 || q{.};
+
+    opendir (my $dirhandle, $querydir)
+	or croak("Error: Can't open directory $querydir");
+
+    my $actualname;
+    while ( my $candidate = readdir($dirhandle) ) {
+	if ( $name =~ /\A $candidate \z/ixms ) {
+	    $actualname = $candidate;
+	    last;
+	}
+    }
+
+    my $path = File::Spec->catfile( $querydir, $actualname );
+
+    # Beware calling in list context.
+    return $actualname ? ($actualname, $path)
+	   : wantarray ? ()
+	   : undef;
+}		    
+
+sub decamelize {
+
+    # Function to convert CamelCase strings to underscore_delimited.
+    my ( $camel ) = @_;
+
+    # Underscore separates internal capitals
+    $camel =~ s/([a-z])([A-Z])/$1\_$2/g;
+
+    # substitute spaces
+    $camel =~ s/\s+/\_/g;
+
+    # and then lowercase
+    $camel = lc($camel);
+
+    return $camel;
+}
+
+sub get_filepath_from_uri {
+
+    my ( $uri_string, $dir ) = @_;
+
+    require URI;
+
+    # N.B. URI module doesn't seem to like the file://my_filename.txt
+    # URI form, and confuses the path with the authority. URI module
+    # behaviour is probably correct, but the MAGE-TAB spec asks for
+    # this (invalid?) URI form. We fix that here:
+    $uri_string =~ s/\A file:\/\/ //ixms;
+
+    my $uri = URI->new( $uri_string );
+
+    # Assume file as default URI scheme.
+    my $path;
+    if ( ! $uri->scheme() || $uri->scheme() eq 'file' ) {
+
+	$uri->scheme('file');
+
+	# URI::File specific, this avoids quoting e.g. spaces in filenames.
+	my $uri_path = $uri->file();
+
+	if ( $dir ) {
+	    $path = File::Spec->file_name_is_absolute( $uri_path )
+		  ? $uri_path
+		  : File::Spec->catfile( $dir, $uri_path );
+	}
+	else {
+	    $path = File::Spec->rel2abs( $uri_path );
+	}
+    }
+    # Add the common network URI schemes.
+    elsif ( $uri->scheme() eq 'http' || $uri->scheme() eq 'ftp' ) {
+	$path = cache_network_file( $uri, $dir );
+    }
+    else {
+	croak(sprintf(
+	    "ERROR: Unsupported URI scheme: %s\n", $uri->scheme(),
+	));
+    }
+
+    return $path;
+}
+
+sub cache_network_file {
+
+    my ( $uri, $dir ) = @_;
+
+    require LWP::UserAgent;
+
+    # N.B. we don't handle URI fragments, just the path.
+    my ( $basename ) = ( $uri->path() =~ m!/([^/]+) \z!xms );
+
+    my $target;
+    if ( $dir ) {
+	$target = File::Spec->catfile( $dir, $basename );
+    }
+    else {
+	$target = $basename;
+    }
+
+    # Only download the file once.
+    unless ( -f $target ) {
+
+	printf STDOUT (
+	    qq{Downloading network file "%s"...\n},
+	    $uri->as_string(),
+	);
+
+	# Download the $uri->as_string()
+	my $ua = LWP::UserAgent->new();
+
+	my $response = $ua->get(
+	    $uri->as_string(),
+	    ':content_file' => $target,
+	);
+
+	unless ( $response->is_success() ) {
+	    croak(sprintf(
+		qq{Error downloading network file "%s" : %s\n},
+		$uri->as_string(),
+		$response->status_line(),
+	    ));
+	}
+    }
+
+    return $target;
+}
+
+sub mx2tab {
+
+    # Temporary method to wrap calls to mx_to_mtab until better error
+    # reporting is included in that call.
+
+    my ( $submission ) = @_;
+
+    eval {
+	require ExperimentConvert;
+	ExperimentConvert->import('mx_to_mtab');
+    };
+    if ($EVAL_ERROR) {
+	# FIXME not really an error; module may just not be
+	# available. This can probably be safely removed once
+	# ExperimentConvert is included in tab2mage core.
+    }
+    else {
+
+	eval {
+	    mx_to_mtab($submission);
+	};
+
+	if ($EVAL_ERROR) {
+	    # FIXME do something here (note that status has already
+	    # been set). This will be more valuable once the module
+	    # dies correctly on failure. N.B. may be better handled in
+	    # the caller.
+	}
+	else {
+
+	    # This is the best we can do at the moment to detect
+	    # failure.
+	    if ( $submission->status() =~ /\b fail(?:ed|ure) \b/ixms ) {
+		die("Error: mx_to_mtab process failed.");
+	    }
+
+	    # Submission should have been set to status='Waiting',
+	    # experiment_type='MAGE-TAB' at this point.  We also have
+	    # to set the in_curation flag here:
+	    $submission->set( in_curation => 1 );
+	    $submission->update();
+	}
+    }
+
+}
+
+1;
diff --git a/lib/ArrayExpress/Curator/Config.pm b/lib/ArrayExpress/Curator/Config.pm
new file mode 100644
index 0000000..4bc9189
--- /dev/null
+++ b/lib/ArrayExpress/Curator/Config.pm
@@ -0,0 +1,589 @@
+#!/usr/bin/env perl
+#
+# Module to provide constant values for a local installation of the
+# ArrayExpress::Curator modules.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Config.pm 2092 2008-06-26 15:42:22Z tfrayner $
+#
+
+package ArrayExpress::Curator::Config;
+
+use strict;
+use warnings;
+
+use Config::YAML;
+use Tie::IxHash;
+use Readonly;
+use File::Spec;
+
+use base 'Exporter';
+our @EXPORT_OK = qw($CONFIG);
+
+my $module_path      = File::Spec->rel2abs(__FILE__);
+my @module_dir_array = File::Spec->splitpath($module_path);
+
+# Always at least set these defaults.
+my $moduleconf = $ENV{PAR_TEMP}    # running under PAR
+               ? File::Spec->catfile( $ENV{PAR_TEMP},
+				      qw( inc lib ArrayExpress Curator Config.yml ) )
+               : File::Spec->catpath( @module_dir_array[ 0, 1 ], 'Config.yml' );
+
+# Add your site config path in the next line. Alternatively, create a
+# config file ~/.tab2mage.conf in your home directory.
+my $siteconf = q{};
+
+my $userconf = File::Spec->catpath( undef, $ENV{HOME}, '.tab2mage.conf' );
+
+our $CONFIG = Config::YAML->new(
+    config => $moduleconf,
+    output => $userconf,
+);
+
+if ($siteconf) {
+    $CONFIG->read($siteconf)
+	or croak("Error: Site config file $siteconf not found: $!\n");
+}
+
+# NOTE that order is critical in the arrays referenced below:
+# Acceptable columns for T2M raw and normalized data. These can also
+# be specified as qr// quoted strings, e.g. if case-insensitive
+# matching is desirable.
+tie my %datafile_indices, 'Tie::IxHash', (
+
+    # Preferential treatment given to Generic file format; this is
+    # because if we miss these and fixate on e.g. GenePix headers in
+    # the same file, we will then create duplicate MetaRow/MetaColumn
+    # headings, which is messy.
+    Generic     => [qr/MetaColumn/i, qr/MetaRow/i, qr/Column/i, qr/Row/i],
+
+    GenePix     => [qw(Block Column Row X Y)],
+    ArrayVision => [qw(Primary Secondary)],
+    Agilent     => [qw(Row Col PositionX PositionY)],
+    Scanalyze   => [qw(GRID COL ROW LEFT TOP RIGHT BOT)],
+    ScanArray   => [
+	'Array Column',
+	'Array Row',
+	'Spot Column',
+	'Spot Row',
+	'X',
+	'Y'
+    ],
+    QuantArray => [ 'Array Column', 'Array Row', 'Column', 'Row' ],
+    Spotfinder => [qw(MC MR SC SR C R)],
+    MEV        => [qw(MC MR C R UID)],
+    CodeLink   => [qw(Logical_row Logical_col Center_X Center_Y)],
+    BlueFuse          => [qw(COL ROW SUBGRIDCOL SUBGRIDROW)],
+    UCSFSpot          => [qw(Arr-colx Arr-rowy Spot-colx Spot-rowy)],
+    NimbleScanFeature => [qw(X Y PROBE_ID X_PIXEL Y_PIXEL)],
+    NimblegenNASA     => [qw(X_BC Y_BC Feature_ID ProbID_BC)],
+    ImaGene           => ['Meta Column', 'Meta Row', 'Column', 'Row', 'Field', 'Gene ID', q{}],
+    ImaGene3          => [qw(Meta_col Meta_row Sub_col Sub_row Name Selected)],
+    ImaGene7          => [qw(Block Column Row), 'Ch1 XCoord', 'Ch1 YCoord', 'Ch2 XCoord', 'Ch2 YCoord'],
+    CSIRO_Spot        => [qw(grid_c grid_r spot_c spot_r indexs)],
+
+    # N.B. the FGEM and FGEM_CS indices are assumed to reside here
+    # by other classes. Also AffyNorm and GEO.
+    FGEM              => [qr/Reporter ?Identifier/i],
+    FGEM_CS           => [qr/Composite ?Sequence ?Identifier/i],
+    GEO               => [qw(ID_REF)],
+    AffyNorm          => ['Probe ?Set ?(Name|ID)'],
+
+    # Lower specificity column headings.
+    NimbleScanNorm    => [qw(X Y PROBE_ID)],
+    AppliedBiosystems => [qw(Probe_ID Gene_ID)],
+    ArrayVision_lg2   => ['Spot labels'],
+
+    # Very non-specific column headings are left till the end:
+    Illumina          => [qw(PROBE_ID)],
+);
+
+# Some content is not appropriate for the config file, we handle it
+# internally here. This overwrites any siteconf changes, but can still
+# be overridden by userconf settings.
+my %default = (
+    MX_DBPARAMS => { PrintError => 0, RaiseError => 1 },
+    AE_DBPARAMS => { PrintError => 0, RaiseError => 1 },
+    AUTOSUBS_DBPARAMS => { PrintError => 0, RaiseError => 1 },
+
+    # Acceptable columns form tab2mage data. See above - this is a
+    # tied hash to retain order.
+    T2M_INDICES => \%datafile_indices,
+
+    # Ignored QTs for general QT checks on non-data matrix files.
+    IGNORED_QTS => [
+	qr/MetaColumn/i,
+	qr/MetaRow/i,
+	qr/Column/i,
+	qr/Row/i,
+	qr/Reporter ?(Name|Identifier)/i,
+	qr/Composite ?Sequence ?(Name|Identifier)/i,
+	qr/ID_REF/,
+        qr/X/,
+        qr/Y/,
+	qr/CellHeader=X/,
+	qr/Block/,
+	qr/Name/,
+	qr/ID/,
+    ],
+
+    # Order here is important - see Tab2MAGE.pm
+    T2M_FILE_TYPES   => [qw(raw normalized)],
+    FGEM_FILE_TYPE   => 'transformed',
+    RAW_DM_FILE_TYPE => 'measured_data_matrix',
+
+    # N.B. at least one value (1,2,4,8...) should be kept back so that
+    # we can always determine if the process has simply died (error
+    # code 255).  Note also that the use of these codes will probably
+    # need to be modulated depending on whether we've got a tab2mage
+    # or MX submission.
+
+    # Innocent errors which may be ignored.
+    ERROR_INNOCENT => 2,
+
+    # Missing MIAME information.
+    ERROR_MIAME => 8,
+
+    # Parsing may fail or give erroneous results.
+    ERROR_PARSEBAD => 32,
+
+    # Parsing _will_ fail.
+    ERROR_PARSEFAIL => 128,
+
+    # Checking crashed (in a recoverable way).
+    ERROR_CHECKERCRASH => 512,
+
+    # Values used in tracking MIAME checklist compliance. This list
+    # will probably grow.
+    MIAME_RAWDATA      => 1,
+    MIAME_NORMDATA     => 2,
+    MIAME_FACTORVALUES => 4,
+    MIAME_NORMPROTOCOL => 8,
+    MIAME_ARRAYSEQ     => 16,
+
+    # Values used in tracking AEDW suitability. Again, will probably grow.
+    AEDW_DESIGNTYPE     => 1,
+    AEDW_HYBNUMBER      => 2,
+    AEDW_ARRAYLOADED    => 4,
+    AEDW_FACTORVALUES   => 8,
+    AEDW_GOODDATA       => 16,
+
+    ERROR_MESSAGE_ARGS    => 'Bad parameters passed to method',
+    ERROR_MESSAGE_PRIVATE => 'Attempt to access a private method',
+
+    STATUS_PENDING          => 'Waiting',
+    STATUS_DB_RETRIEVAL     => 'Retrieving info from MX',
+    STATUS_CHECKING         => 'Checking in progress',
+    STATUS_CRASHED          => '** CHECKER CRASH **',
+    STATUS_PASSED           => 'Checking passed',
+    STATUS_FAILED           => 'Checking failed',
+    STATUS_EXPORT           => 'MAGE-ML export',
+    STATUS_EXPORT_POSTPONED => 'Export postponed',
+    STATUS_EXPORT_ERROR     => 'Export failed',
+    STATUS_COMPLETE         => 'Complete',
+
+    EXPTCHECK_PROGNAME => 'expt_check.pl',
+    EXPTCHECK_VERSION  => '3.2.2',
+
+    TAB2MAGE_PROGNAME  => 'Tab2MAGE',
+    TAB2MAGE_VERSION   => '2.2.2',
+
+    # This is now set in QT_list.pm
+    DEFAULT_QT_FILENAME => q{},
+    DEFAULT_ENTREZ_FILENAME => $ENV{PAR_TEMP}    # running under PAR
+               ? File::Spec->catfile( $ENV{PAR_TEMP},
+				      qw( inc lib ArrayExpress Curator Entrez_list.txt ) )
+               : File::Spec->catpath(
+		   @module_dir_array[ 0, 1 ], 'Entrez_list.txt' ),
+
+    # N.B. these are processed using oct()
+    FILE_PERMISSIONS    => '0666',
+    DIR_PERMISSIONS     => '0777',
+);
+
+$CONFIG->fold( \%default );
+
+# Incorporate user config, if present.
+if ( $ENV{HOME} ) {
+    if ( -f $userconf ) {
+        warn("Reading user config file $userconf\n");
+        $CONFIG->read($userconf);
+    }
+}
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../index.html">
+                <img src="../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: ExperimentChecker.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Curator::Config.pm - a module defining general config options.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Curator::Config qw($CONFIG);
+
+=head1 DESCRIPTION
+
+This module provides definition of some configuration options used
+throughout the code. These options may be changed by editing the
+ArrayExpress/Curator/Config.yml file. If you wish to point your
+Tab2MAGE installation to an alternate site config YAML file, please
+edit the $siteconf variable in the ArrayExpress/Curator/Config.pm
+file. For user-specific configuration, this module will also read any
+.tab2mage.conf file located in your home directory (i.e.,
+$HOME/.tab2mage.conf).
+
+See L<ArrayExpress::Curator::MAGE::Definitions> for other
+MAGE-specific constants.
+
+=head2 Configuration options
+
+=over 2
+
+=item MX_DSN
+
+MIAMExpress data source name or DSN, e.g., "DBI:mysql:db_name:host:port".
+
+=item MX_USERNAME
+
+The username used to connect to the MIAMExpress database.
+
+=item MX_PASSWORD
+
+The password used to connect to the MIAMExpress database.
+
+=item AE_DSN
+
+For local ArrayExpress installations only. This is the ArrayExpress
+data source name or DSN, e.g., "DBI:Oracle:db_name:host:port" used in
+direct connection to the database. Note that the appropriate
+DBD::Oracle module must be installed for this to work.
+
+=item AE_USERNAME
+
+The username used to connect to the ArrayExpress database.
+
+=item AE_PASSWORD
+
+The password used to connect to the ArrayExpress database.
+
+=item AEDW_DSN
+
+For checking the data warehouse readiness of an experiment
+submission. This database connection is used in checking that any
+required array designs have been loaded into the data warehouse. This
+config value should be the ArrayExpress DW data source name or DSN,
+e.g., "DBI:Oracle:db_name:host:port" used in direct connection to the
+data warehouse. Note that the appropriate DBD::Oracle module must be
+installed for this to work.
+
+=item AEDW_USERNAME
+
+The username used to connect to the AEDW database.
+
+=item AEDW_PASSWORD
+
+The password used to connect to the AEDW database.
+
+=item AE_ARRAYDESIGN_LIST
+
+Remote ArrayExpress array design list web page. Used by Tab2MAGE,
+MIAMExpress and the experiment checker. This is currently accessible
+from outside EBI, and so the setting can be left as it is.
+
+=item AE_RETRIEVE_FEATURELIST
+
+The ArrayExpress web page root for retrieving array feature lists. In
+use, the accession number for the PhysicalArrayDesign is appended to
+AE_RETRIEVE_FEATURELIST before it is used in an HTTP GET.
+
+=item AE_RETRIEVE_ADF
+
+The ArrayExpress web page root for retrieving ADFs. In use, the
+database identifier for the PhysicalArrayDesign is appended to
+AE_RETRIEVE_ADF before it is used in an HTTP GET.
+
+=item AFFYMETRIX_LIBRARYPATH
+
+The path to a directory containing CDF files needed for parsing
+Affymetrix CHP files. The default value is an empty string, which
+indicates to the scripts that the CDF files are in the current working
+directory.
+
+=item T2M_PROTOCOL_PREFIX
+
+The prefix used to autogenerate reassigned protocol
+accessions. Default value is "P-TABM-". Please note that you should
+change this to prevent conflicts if you intend to use protocol
+accession reassignment and submit the resulting MAGE-ML to
+ArrayExpress.
+
+=item T2M_EXPERIMENT_PREFIX
+
+The prefix used in creating experiment accessions. This is used to
+check that a valid experiment accession number has been used in
+conjunction with the protocol reassignment mechanism.
+
+=item MAX_LWP_DOWNLOAD
+
+The maximum size of LWP::UserAgent downloads. This applies to
+ArrayExpress ADF and feature list downloads. Currently set to 40MB.
+
+=item MAX_DATAFILE_SIZE
+
+The maximum size of data file which these scripts will attempt to
+parse. Currently set to 100MB.
+
+=item VISUALIZE_FONT
+
+The name of the default font to use in visualization graph
+creation. This gets passed to the "dot" program. Currently set to
+"Courier".
+
+=item AUTOSUBS_PIDFILE
+
+Full path to the file used by autosubs_checkd.pl to track running
+instances of itself.
+
+=item AUTOSUBS_ADMIN
+
+Email address of the administrator responsible for managing the
+checker and exporter daemon processes. Emails will be sent to this
+address on abnormal termination of the process (e.g. on crashes).
+
+=item AUTOSUBS_ADMIN_USERNAME
+
+Login for the administrator responsible for managing the checker and
+exporter daemon processes. Other users are restricted from launching
+these daemons to aid in process management.
+
+=item AUTOSUBS_DOMAIN
+
+The default domain used in creating MAGE-ML identifiers when exporting
+experiments using the autosubmissions system. This is set by default
+to 'ebi.ac.uk'. Note that this does not affect submissions exported
+from a MIAMExpress database.
+
+=item AUTOSUBS_CURATOR_EMAIL
+
+The email to which enquiries will be directed from the autosubmissions
+web form.
+
+=item AUTOSUBS_SMTP_SERVER
+
+The SMTP server used to send notification email from the
+autosubmissions system.
+
+=item AUTOSUBS_DSN
+
+The DSN to use when connecting to the autosubmissions database
+system. Typically this will be of the form
+"DBI:mysql:dbname:host:port".
+
+=item AUTOSUBS_USERNAME
+
+The username to use when connecting to the autosubmissions database.
+
+=item AUTOSUBS_PASSWORD
+
+The password to use when connecting to the autosubmissions database.
+
+=item AUTOSUBMISSIONS_FILEBASE
+
+The filesystem path to the top-level directory where the
+autosubmissions system should store uploaded spreadsheets and data
+files.
+
+=item AUTOSUBMISSIONS_TARGET
+
+The filesystem directory into which new submissions are exported as
+MAGE-ML. A new directory, named using the automatically assigned
+experiment accession, will be created and populated.
+
+=item WEBFORM_TEMPLATE_DIR
+
+The filesystem directory where the web submissions form may write
+temporary files. This is principally used for temporary storage of
+spreadsheet template files. It is recommended that you run a regular
+script from e.g. your crontab to delete old files.
+
+=item WEBFORM_TEMPLATE_URL
+
+The URL pointing to the WEBFORM_TEMPLATE_DIR, used to create links to
+the temporary template files from the web submissions form.
+
+=item MX_AUTOSUBS_PIDFILE
+
+Full path to the file used by mx_autocheck_daemon.pl to track running
+instances of itself.
+
+=item MX_AUTOSUBS_ADMIN
+
+Email address of the administrator responsible for managing the
+mx_autocheck_daemon.pl process. See notes for AUTOSUBS_ADMIN.
+
+=item MX_MAGEML_EXPORT_COMMAND
+
+Full command (minus arguments) used to export MAGE-ML from a local
+MIAMExpress database.
+
+=item AEDW_DESIGN_TYPES
+
+A list of MO ExperimentDesignTypes that indicate an experiment is
+suitable for the ArrayExpress Data Warehouse.
+
+=item AEDW_UNWANTED_DESIGN_TYPES
+
+A list of MO ExperimentDesignTypes that indicate an experiment is not
+suitable for the ArrayExpress Data Warehouse. This is used to
+differentiate between experiments known to be of the wrong type from
+those which are merely under-annotated.
+
+=item AEDW_MINIMUM_HYBS
+
+The minimum number of hybridizations required in an experiment for it
+to be considered for the AE Data Warehouse.
+
+=item MIAME_COMPLIANT_ARRAY_PIPELINES
+
+A list of array design accession prefixes (e.g. "A-AFFY-") where every
+design can be assumed to be MIAME compliant, for the purposes of
+experiment MIAME checking.
+
+=back
+
+=head1 Private options
+
+These are options we recommend you don't change, unless you know what
+you're doing.
+
+=over 2
+
+=item T2M_INDICES
+
+A hashref, with keys representing data file format type and values as
+arrayrefs listing the coordinate index columns to be used for parsing
+those formats. See also L<ArrayExpress::Datafile>. Current format
+types are: Generic, GenePix, Affymetrix, ArrayVision, Agilent,
+Scanalyze, ScanArray, QuantArray, Spotfinder, BlueFuse, UCSF Spot,
+Illumina, CodeLink, Applied Biosystems, NimbleScan.
+
+=item T2M_FILE_TYPES
+
+Supported data file types for per-hyb parsing and MAGE-ML
+creation. These tags can appear in the Tab2MAGE spreadsheet as part of
+a File[] column heading. Currently supported: "raw" and "normalized".
+
+=item FGEM_FILE_TYPE
+
+Supported data file type for FGEM parsing and MAGE-ML creation. These
+tags can appear in the Tab2MAGE spreadsheet as part of a File[] column
+heading. Currently supported: "transformed".
+
+=item IGNORED_QTS
+
+A list of regular expression which match QTs which are omitted
+from analyses or MAGE-ML output from non-data matrix files.
+
+=item ERROR_INNOCENT, ERROR_MIAME, ERROR_PARSEBAD, ERROR_PARSEFAIL
+
+Errors returned to the shell by each of the scripts are represented by
+8-bit integers; here we map them to the constants used.
+
+=item ERROR_CHECKERCRASH
+
+Similar to the errors above, this error indicates that the checking
+process crashed for some reason.
+
+=item ERROR_MESSAGE_ARGS, ERROR_MESSAGE_PRIVATE
+
+A selection of internal error message texts.
+
+=item MX_EXTENDED_REPORT
+
+Controls whether extended reporting of sample annotation and factor
+values is available (only supported for generic MIAMExpress
+installations).
+
+=item DEFAULT_QT_FILENAME
+
+Name of the file to use as the default source of QT information. This
+is used to point to a file installed alongside the perl modules, and
+should not be changed unless you know what you're doing.
+
+=item DEFAULT_ENTREZ_FILENAME
+
+Name of the file containing a list of Entrez-approved publication
+abbreviations. Again, this value should not be changed unless strictly
+necessary.
+
+=item FILE_PERMISSIONS
+
+Octal number indicating the default permissions to use when creating
+files using the autosubmission system. This is useful if, for example,
+your webserver process is in a different group from that of your
+users. The default is 0555.
+
+=item DIR_PERMISSIONS
+
+Octal number indicating the default directory permissions for the
+autosubmission system. The default is 0777.
+
+=item MX_DBPARAMS
+
+A hashref of parameters used in the MIAMExpress database connection.
+
+=item AE_DBPARAMS
+
+A hashref of parameters used in the ArrayExpress database connection.
+
+=item AEDW_DBPARAMS
+
+A hashref of parameters used in the AEDW database connection.
+
+=item AUTOSUBS_DBPARAMS
+
+A hashref of parameters used in the autosubmissions database connection.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2004.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments. 
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+1;
diff --git a/lib/ArrayExpress/Curator/Config.yml b/lib/ArrayExpress/Curator/Config.yml
new file mode 100644
index 0000000..9c1deab
--- /dev/null
+++ b/lib/ArrayExpress/Curator/Config.yml
@@ -0,0 +1,89 @@
+---
+
+# See 'perldoc ArrayExpress::Curator::Config' for a full description
+# of these configuration options.
+
+# MIAMExpress connection settings.
+MX_DSN: ''
+MX_PASSWORD: ''
+MX_USERNAME: ''
+MX_EXTENDED_REPORT: 1
+
+# ArrayExpress database connection settings.
+AE_DSN: ''
+AE_PASSWORD: ''
+AE_USERNAME: ''
+
+# ArrayExpress warehouse connection settings.
+AEDW_DSN: ''
+AEDW_PASSWORD: ''
+AEDW_USERNAME: ''
+AEDW_DESIGN_TYPES:
+    - co-expression_design
+AEDW_UNWANTED_DESIGN_TYPES:
+    - comparative_genome_hybridization_design
+    - binding_site_identification_design
+    - genotyping_design
+    - tiling_path_design
+    - platform_comparison_design
+    - array_platform_variation_design
+    - hardware_variation_design
+    - software_variation_design
+    - operator_variation_design
+    - quality_control_testing_design
+    - optimization_design
+    - normalization_testing_design
+    - self_vs_self_design
+AEDW_MINIMUM_HYBS: 6
+
+# A list of array pipelines assumed to be MIAME compliant.
+MIAME_COMPLIANT_ARRAY_PIPELINES:
+    - A-AFFY-
+    - A-AGIL-
+    - A-BUGS-
+    - A-WMIT-
+    - A-GEHB-
+
+# ArrayExpress repository webpages.
+AE_RETRIEVE_ADF: http://www.ebi.ac.uk/aerep/lob?name=adss&id=
+AE_RETRIEVE_FEATURELIST: http://www.ebi.ac.uk/aerep/report?cmd=featurelist&array=
+AE_ARRAYDESIGN_LIST: http://www.ebi.ac.uk/aerep/report?cmd=arraydesignlist
+
+# Path to directory containing CDF files.
+AFFYMETRIX_LIBRARYPATH: ''
+
+# Autosubmission system settings.
+AUTOSUBS_ADMIN: ''
+AUTOSUBS_ADMIN_USERNAME: ''
+AUTOSUBS_PIDFILE: ''
+AUTOSUBS_DOMAIN: ebi.ac.uk
+
+AUTOSUBS_DSN: ''
+AUTOSUBS_USERNAME: ''
+AUTOSUBS_PASSWORD: ''
+
+AUTOSUBMISSIONS_FILEBASE: ''
+AUTOSUBMISSIONS_TARGET: ''
+
+AUTOSUBS_CURATOR_EMAIL: ''
+AUTOSUBS_SMTP_SERVER: ''
+
+WEBFORM_TEMPLATE_DIR: ''
+WEBFORM_TEMPLATE_URL: ''
+
+MX_AUTOSUBS_ADMIN: ''
+MX_AUTOSUBS_PIDFILE: ''
+MX_MAGEML_EXPORT_COMMAND: ''
+
+# Download size constraints. Note that $MAX_LWP_DOWNLOAD needs to be
+# at least as big as the downloaded arraydesignlist, otherwise the
+# script won't be able to find all the array design database ids.
+MAX_DATAFILE_SIZE: 104857600
+MAX_LWP_DOWNLOAD:  62914560
+
+# Settings for protocol accession reassignment.
+T2M_EXPERIMENT_PREFIX: 'E-[A-Z]{4}-'
+T2M_PROTOCOL_PREFIX: P-TABM-
+
+# The font used in GraphViz graph PNG generation.
+VISUALIZE_FONT: Courier
diff --git a/lib/ArrayExpress/Curator/Database.pm b/lib/ArrayExpress/Curator/Database.pm
new file mode 100644
index 0000000..602831b
--- /dev/null
+++ b/lib/ArrayExpress/Curator/Database.pm
@@ -0,0 +1,672 @@
+#!/usr/bin/env perl
+#
+# Database.pm - a module derived from and used in the experiment
+# checker script. Contains routines which might be useful elsewhere.
+#
+# Tim Rayner 2004 ArrayExpress team, EBI
+#
+# $Id: Database.pm 2027 2008-04-16 10:25:26Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../index.html">
+                <img src="../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: Database.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Curator::Database - a module used by expt_check.pl
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Curator::Database qw(retrieve_AE_adf parse_adf);
+
+ my $db_id = 123456789;
+ my ( $fh, $accession ) = retrieve_AE_adf( $db_id, undef, \*STDERR, 0 );
+ my $array_design = parse_adf( $fh, $accession );
+
+=head1 DESCRIPTION
+
+This module provides a set of subroutines used by the experiment
+checker script to interact with the ArrayExpress databases. Queries may
+be redirected to local ArrayExpress instances by editing values in
+the L<ArrayExpress::Curator::Config> package.
+
+=head1 FUNCTIONS
+
+=over 2
+
+=item C<get_ae_dbh()>
+
+Returns a singleton database handle for the currently-configured
+ArrayExpress repository database instance.
+
+=item C<get_aedw_dbh()>
+
+Returns a singleton database handle for the currently-configured
+ArrayExpress warehouse database instance.
+
+=item C<retrieve_AE_adf( $db_id, $accession, $error_fh, $skip_adf_download )>
+
+Given either AE database table row identifier or AE accession, an open
+filehandle for error reporting and a flag indicating whether the ADF
+should be downloaded, check for the existence of said ADF (by database
+id or accession), download it and return an opened filehandle and the
+accession number (the latter is useful when querying by database id).
+
+=item C<retrieve_AE_featurelist( $accession )>
+
+Given an array accession number, return a hashref where the keys are
+q{.}-delimited lists of MetaColumn.MetaRow.Column.Row feature
+coordinates, and the values are Reporter identifiers.
+
+=item C<parse_adf( $adf_fh, $accno, $reporter_prefix, $compseq_prefix )>
+
+Passed an ADF filehandle and an accession number (see
+retrieve_AE_adf()), and optional Reporter or Composite Sequence
+identifier prefixes, parse the ADF and return an ArrayDesign object
+(see L<ArrayExpress::Datafile::ArrayDesign>).
+
+=item C<arrayaccession_in_aedw( $accession )>
+
+Given an array design accession number, return true if the design is
+loaded into the currently-configured AE data warehouse, false
+otherwise.
+
+=item C<map_affy_accno_to_name( $accession )>
+
+Given an array design accession number, attempt to match it to an
+Affymetrix design name. This relies on the names of loaded Affy array
+designs including the design name (e.g. HG-U133A) in square brackets
+(e.g. "Affymetrix GeneChip Human Genome HG-U133A [HG-U133A]").
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2008.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments. 
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Curator::Database;
+
+use strict;
+use warnings;
+
+use LWP::UserAgent;
+use List::Util qw( min );
+use Scalar::Util qw( openhandle );
+
+use Carp;
+use IO::File;
+
+use ArrayExpress::Curator::Common qw(
+    get_indexcol
+);
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+require ArrayExpress::Datafile::ArrayDesign;
+
+use base 'Exporter';
+our @EXPORT_OK = qw(
+    get_ae_dbh
+    get_aedw_dbh
+    retrieve_AE_adf
+    retrieve_AE_featurelist
+    parse_adf
+    arrayaccession_in_aedw
+    map_affy_accno_to_name
+    get_ae_array_details
+);
+
+# Globals to hold AE and AEDW database handles, if needed.
+my ($AE_DBH, $AEDW_DBH);
+
+# Global to store retrieved AE array accession number table; we only
+# want to retrieve it once.
+my @AE_ARRAY_ACCNO_TABLE;
+
+sub _connect_to_oracle {
+
+    my ($dsn, $username, $password, $params) = @_;
+
+    eval {
+	require DBI;
+	require DBD::Oracle;
+    };
+    if ($@) {
+	croak(<<END_ERROR);
+
+Error: Connection to a local ArrayExpress database instance requires
+both the DBI and the DBD::Oracle modules to be installed.
+The following error was encountered:
+
+$@
+
+END_ERROR
+    }
+
+    my $dbh = DBI->connect( $dsn, $username, $password, $params )
+	or croak( "Error: Cannot connect to local ArrayExpress"
+		      . " database instance: $DBI::errstr\n" );
+
+    return $dbh;
+}
+
+sub get_ae_dbh {
+
+    # If we're not yet connected, and we know how to, do so.
+    if ( ! $AE_DBH ) {
+	if ( $CONFIG->get_AE_DSN() ) {
+	    print STDOUT ("Connecting to ArrayExpress database...\n");
+	    $AE_DBH = _connect_to_oracle(
+		$CONFIG->get_AE_DSN(),      $CONFIG->get_AE_USERNAME(),
+		$CONFIG->get_AE_PASSWORD(), $CONFIG->get_AE_DBPARAMS()
+	    );
+	}
+    }
+
+    # Check that we're still connected, and reconnect if
+    # necessary. N.B. This still doesn't fix the problem of sharing
+    # DBH between forked processes, which is generally frowned upon
+    # anyway.
+    elsif ( ! $AE_DBH->ping() ) {
+	print STDOUT ("Reconnecting to ArrayExpress database...\n");
+	$AE_DBH = _connect_to_oracle(
+	    $CONFIG->get_AE_DSN(),      $CONFIG->get_AE_USERNAME(),
+	    $CONFIG->get_AE_PASSWORD(), $CONFIG->get_AE_DBPARAMS()
+	);
+    }
+
+    return $AE_DBH;
+}
+
+sub get_aedw_dbh {
+
+    # If we're not yet connected, and we know how to, do so.
+    if ( !$AEDW_DBH && $CONFIG->get_AEDW_DSN() ) {
+	print STDOUT ("Connecting to ArrayExpress Data Warehouse...\n");
+	$AEDW_DBH = _connect_to_oracle(
+	    $CONFIG->get_AEDW_DSN(),      $CONFIG->get_AEDW_USERNAME(),
+            $CONFIG->get_AEDW_PASSWORD(), $CONFIG->get_AEDW_DBPARAMS()
+	);
+    }
+
+    return $AEDW_DBH;
+}
+
+sub map_affy_accno_to_name {
+
+    my ( $accno ) = @_;
+
+    unless ( scalar @AE_ARRAY_ACCNO_TABLE ) {
+	retrieve_accno_table();
+    }
+
+    my $affyname = q{};
+
+    AE_ACCNO_LINE:
+    foreach my $line ( @AE_ARRAY_ACCNO_TABLE ) {
+	next AE_ACCNO_LINE if ( $line =~ m/^\s*$/ );       # skip empty lines
+
+	my @line_array = split /\t/, $line;
+	if ( $line_array[1] eq $accno ) {
+	    ($affyname) = ($line_array[2] =~ m/\[ ([^\]]+) \]/xms);
+	    last AE_ACCNO_LINE;
+	}
+    }
+
+    return $affyname;
+}
+
+sub parse_adf {    # Accepts filehandles, not filenames. This is to allow
+                   # the use of temporary files via IO::File when getting
+                   # the ADF from ArrayExpress.
+
+    my ( $adf_filehandle, $accno, $reporter_prefix, $compseq_prefix ) = @_;
+
+    openhandle($adf_filehandle)
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my $feature_indices  = [];
+    my $reporter_indices = [];
+    my $compseq_indices  = [];
+
+    # Special case of Affymetrix identifiers. This is tied to the
+    # ArrayExpress accession number format.
+    if ( $accno && $accno =~ m/\A A-AFFY-\w+ \z/xms ) {
+
+	# Use the AE-derived name (in brackets) where available,
+	# otherwise fall back to a simple default.
+	my $affyname = map_affy_accno_to_name($accno) || '[^:]*';
+
+	# Don't override user-supplied prefixes.
+	unless (defined $reporter_prefix) {
+	    $reporter_prefix = "Affymetrix:Reporter:$affyname:";
+	}
+	unless (defined $compseq_prefix) {
+	    $compseq_prefix = "Affymetrix:CompositeSequence:$affyname:";
+	}
+    }
+
+    # First, scan down through whatever header there is.
+    ADF_HEADER_LINE:
+    while ( my $line = <$adf_filehandle> ) {
+
+        chomp $line;
+
+	# Start parsing the ADF on the first line containing both
+	# Identifier and Name columns (either Reporter or
+	# CompositeSequence). This replaces a parsing start based on
+	# the number of columns in the ADF, which doesn't work for too
+	# many crappy ADFs in AE.
+	my $identifier_re = qr/(Reporter|Composite [ ]* Sequence) [ ]* Identifier/ixms;
+	my $name_re       = qr/(Reporter|Composite [ ]* Sequence) [ ]* Name/ixms;
+	if (   $line =~ m/(\A|\t) [ ]* $identifier_re [ ]* (\t|\z)/xms
+	    && $line =~ m/(\A|\t) [ ]* $name_re       [ ]* (\t|\z)/xms ) {
+
+            # Scan through the column headings for identifiers and coord headings.
+            my @line_array = split /\t/, $line;
+
+	    # Feature coordinates.
+            foreach my $indexname qw(MetaColumn MetaRow Column Row) {
+                my $colnum = get_indexcol( \@line_array, qr/$indexname/i );
+                push( @$feature_indices, $colnum ) unless ( $colnum < 0 );
+            }
+
+            # 'Reporter ?Identifier'
+            foreach my $indexname (
+                @{ $CONFIG->get_T2M_INDICES()->{FGEM} } ) {
+                my $colnum = get_indexcol( \@line_array, qr/$indexname/i );
+                push( @$reporter_indices, $colnum ) unless ( $colnum < 0 );
+            }
+
+            # 'Composite ?Sequence ?Identifier'
+            foreach my $indexname (
+                @{ $CONFIG->get_T2M_INDICES()->{FGEM_CS} } ) {
+                my $colnum = get_indexcol( \@line_array, qr/$indexname/i );
+                push( @$compseq_indices, $colnum ) unless ( $colnum < 0 );
+            }
+
+            last ADF_HEADER_LINE
+                if ( ( $#$feature_indices == 3 )
+                || @$reporter_indices
+                || @$compseq_indices );    # Require one of these
+        }
+    }
+
+    unless ( @$reporter_indices || @$compseq_indices ) {
+        print STDOUT (
+            "Error: ADF does not contain reporter or composite sequence identifiers! ",
+            "Combined data matrix file checking will not work properly.\n"
+        );
+    }
+
+    # Set up our identifier prefix regexps. Either use a supplied
+    # prefix, or default to using MIAMExpress-style prefixes.
+    my $reporter_prefix_regexp
+	= defined($reporter_prefix) ? qr/\A $reporter_prefix/xms
+	: defined($accno)           ? qr/\A (ebi.ac.uk:MIAMExpress:Reporter:$accno\.|R:$accno:)/xms
+	: undef;
+    my $compseq_prefix_regexp
+	= defined($compseq_prefix)  ? qr/\A $compseq_prefix/xms
+        : defined($accno)           ? qr/\A (ebi.ac.uk:MIAMExpress:CompositeSequence:$accno\.|CS:$accno:)/xms
+	: undef;
+
+    # Then process the remainder of the adf lines.  %design_elements
+    # is a hash of (potentially) three sub-hashes, themselves with
+    # keys composed of feature coords, reporter ids or compseq
+    # ids.
+    my (%design_elements);
+    while ( my $line = <$adf_filehandle> ) {
+        chomp $line;
+
+	# split is faster than a regexp.
+        my @line_array = split /\t/, $line, -1;
+
+        # Features
+        if (@$feature_indices) {
+
+            my @coords;
+            foreach my $index (@$feature_indices) {
+                push( @coords, $line_array[$index] );
+            }
+            my $key = join( ".", @coords );
+            $design_elements{adf_features}{$key}++;
+        }
+
+        # Reporters (one column only)
+        if (   @$reporter_indices == 1
+            && $line_array[ $reporter_indices->[0] ] ) {
+
+            my $key = $line_array[ $reporter_indices->[0] ];
+
+            if ( defined($reporter_prefix_regexp) ) {
+                $key =~ s/$reporter_prefix_regexp//;
+            }
+
+            $design_elements{adf_reporters}{$key}++;
+        }
+
+        # Composite Sequences (one column only)
+        if (   @$compseq_indices == 1
+	    && $line_array[ $compseq_indices->[0] ] ) {
+
+            my $key = $line_array[ $compseq_indices->[0] ];
+
+            if ( defined($compseq_prefix_regexp) ) {
+                $key =~ s/$compseq_prefix_regexp//;
+            }
+
+            $design_elements{adf_compseqs}{$key}++;
+        }
+    }
+
+    my $array = ArrayExpress::Datafile::ArrayDesign->new({
+	accession => $accno,
+    });
+
+    $array->set_features(  $design_elements{adf_features}  || {} );
+    $array->set_reporters( $design_elements{adf_reporters} || {} );
+    $array->set_compseqs(  $design_elements{adf_compseqs}  || {} );
+
+    return ( $array );
+}
+
+sub retrieve_accno_table {
+
+    if ( my $dbh = get_ae_dbh() ) {
+
+	# If possible, connect via DBI to avoid public/private array
+	# design issues.
+	print STDOUT (
+	    "\nConnecting to ArrayExpress database...retrieving accession number table.\n"
+	);
+
+	# Select just the fully-loaded array designs (having an entry
+	# in PL_LABEL).
+	my $sth = $dbh->prepare(<<'QUERY');
+select i.id, i.identifier, i.name
+from tt_identifiable i,
+ tt_physicalarraydesign a,
+ pl_label l
+where i.id=a.id
+and i.id=l.mainobj_id
+QUERY
+
+	$sth->execute()
+	    or croak("Error retrieving array list from database: " . $sth->errstr);
+
+	while ( my $result = $sth->fetchrow_arrayref() ) {
+	    push @AE_ARRAY_ACCNO_TABLE, join("\t", map { $_ || q{} } @{ $result } );
+	}
+    }
+    else {
+
+	# Fall back to web access. Note that private arrays are
+	# unavailable here.
+	print STDOUT (
+	    "\nConnecting to ArrayExpress web site...retrieving accession number table.\n"
+	);
+
+	my $ua = LWP::UserAgent->new();
+	$ua->max_size( $CONFIG->get_MAX_LWP_DOWNLOAD() );
+
+	my $response = $ua->get( $CONFIG->get_AE_ARRAYDESIGN_LIST() );
+
+	unless ( $response->is_success ) {
+	    croak(    "Error connecting to ArrayExpress: "
+		    . $response->status_line
+                    . "\n" );
+	}
+
+	# If the database goes down, ArrayExpress can return an error
+	# page. We trap this as HTML.
+	if ( $response->content =~ m/^\s*<html>/i ) {
+	    croak(
+		"Error connecting to ArrayExpress (web query returned HTML, not text).\n"
+	    );
+	}
+
+	@AE_ARRAY_ACCNO_TABLE = split /\n/, $response->content;    # split into rows
+    }
+
+    return;
+}
+
+sub get_ae_array_details {
+
+    my ( $args ) = @_;
+
+    my $accession = $args->{'accession'};
+    my $db_id     = $args->{'database_id'};
+    my $array_name;
+
+    unless ( $accession || $db_id ) {
+        croak("Error: Neither accession nor database ID given for array design.");
+    }
+
+    # Only get this once; we use the @AE_ARRAY_ACCNO_TABLE global to
+    # store the array design id -> accno -> name mapping.
+    unless ( scalar @AE_ARRAY_ACCNO_TABLE ) {    # Should contain at least one row!
+	retrieve_accno_table();
+    }
+
+    AE_ACCNO_LINE:
+    foreach my $line ( @AE_ARRAY_ACCNO_TABLE ) {
+        next AE_ACCNO_LINE if ( $line =~ m/^\s*$/ );       # skip empty lines
+
+        my @line_array = split /\t/, $line;
+        if ( $db_id && $line_array[0] == $db_id ) {
+            $accession  = $line_array[1];
+            $array_name = $line_array[2];
+            last AE_ACCNO_LINE;
+        }
+        if ( $accession && $line_array[1] eq $accession ) {
+            $db_id      = $line_array[0];
+            $array_name = $line_array[2];
+            last AE_ACCNO_LINE;
+        }
+    }
+
+    {
+	# Hide the exceptions from our sig handler (but set $@).
+	my $sighandler = $SIG{__DIE__};
+	delete $SIG{__DIE__};
+	unless ( $accession && $db_id ) {
+	    if ( $db_id ) {
+		die(
+		     q{Error: Could not find array accession number }
+		  . qq{for database identifier "$db_id" in ArrayExpress.\n}
+	        );
+	    }
+	    if ( $accession ) {
+		die(
+		     q{Error: Could not find array database identifier }
+		  . qq{for accession number "$accession" in ArrayExpress.\n}
+	        );
+	    }
+	    die(
+		"Error: Neither accession number nor database identifier "
+	      . "for array found in ArrayExpress.\n"
+	    );
+	}
+	$SIG{__DIE__} = $sighandler;
+    }
+
+    return wantarray ?
+      ($accession, $db_id, $array_name) : $accession;
+}
+
+sub retrieve_AE_adf {
+
+    my ( $db_id, $accession, $error_fh, $skip_adf_download ) = @_;
+
+    unless ( $accession || $db_id ) {
+        carp(
+            "ERROR: Neither array accession nor AE database identifier available "
+                . "for at least one array.\n" );
+        return;
+    }
+
+    openhandle($error_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my $array_name;
+
+    ( $accession, $db_id, $array_name )
+        = get_ae_array_details({ accession   => $accession,
+				 database_id => $db_id, });
+
+    if ( $array_name && $array_name =~ m/Agilent/i ) {
+        print $error_fh (
+            "Warning: Agilent array design used: $array_name. "
+	  . "The orientation of the array may be incorrect.\n"
+        );
+    }
+
+    # Get a tab-delimited ADF from ArrayExpress, unless we've been
+    # told not to (e.g. we're skipping the data file checks):
+    my $temporary_adf_filehandle;
+    if ( $skip_adf_download ) {
+	print STDOUT (
+	    "Skipping ArrayExpress ADF download.\n"
+	);
+    }
+    else {
+	print STDOUT (
+	    "Connecting to ArrayExpress..."
+	  . "retrieving ADF for Accession Number $accession.\n"
+	);
+
+	my $ua = LWP::UserAgent->new();
+	$ua->max_size( $CONFIG->get_MAX_LWP_DOWNLOAD() );
+
+	my $response = $ua->get( $CONFIG->get_AE_RETRIEVE_ADF() . $db_id );
+
+	unless ( $response->is_success ) {
+	    croak(    "Error retrieving ADF from ArrayExpress: "
+			  . $response->status_line
+			      . "\n" );
+	}
+
+	# put it into a temporary filehandle, unless it was too big to download
+	$temporary_adf_filehandle = IO::File->new_tmpfile;
+	if ( $response->headers()->header('Client-Aborted') ) {
+	    warn(
+		"Warning: ADF size exceeds download limit. Feature parsing will be skipped.\n"
+	    );
+	}
+	else {
+	    print $temporary_adf_filehandle ( $response->content );
+	    seek $temporary_adf_filehandle, 0,
+		0;    # reset the filehandle for reading
+	}
+    }
+    
+    return wantarray
+	? ( $temporary_adf_filehandle, $accession )
+	    : $temporary_adf_filehandle;
+
+}
+
+sub retrieve_AE_featurelist {
+
+    my ($accession) = @_;
+
+    $accession or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    # Get feature IDs from ArrayExpress:
+    print STDOUT (
+        "Attempting to connect to ArrayExpress...retrieving feature identifiers for Array accession $accession.\n"
+    );
+
+    my $ua = LWP::UserAgent->new();
+    $ua->max_size( $CONFIG->get_MAX_LWP_DOWNLOAD() );
+
+    my $response
+        = $ua->get( $CONFIG->get_AE_RETRIEVE_FEATURELIST() . $accession );
+
+    unless ( $response->is_success ) {
+        croak(    "Error retrieving feature list from ArrayExpress: "
+                . $response->status_line
+                . "\n" );
+    }
+
+    my $coords_to_identifiers;    # undef has a meaning here
+    if ( $response->headers()->header('Client-Aborted') ) {
+        warn("Warning: Feature table size exceeds download limit.\n");
+    }
+
+    # HTML or empty string signifies an error.
+    elsif ( $response->content && ( $response->content !~ m/<html>/i ) ) {
+        %$coords_to_identifiers = map {
+            my ( @coords, $identifier );
+            ( @coords[ 0 .. 3 ], $identifier ) = ( split /\t/ );
+            join( '.', @coords ) => $identifier;
+        } split /[\r\n]+/, $response->content;
+    }
+
+    return $coords_to_identifiers;
+
+}
+
+sub arrayaccession_in_aedw {
+
+    # Given an array accession, query the DW and return the number of
+    # rows that match (i.e. zero if not loaded).
+
+    my ($accession) = @_;
+
+    my $dbh = get_aedw_dbh();
+
+    unless ($dbh) {
+	warn("AEDW database handle not available; skipping array check.\n");
+	return;
+    }
+
+    my $sth = $dbh->prepare(<<'QUERY');
+select distinct * from AE2__ARRAYDESIGN
+where ARRAYDESIGN_ACCESSION=?
+QUERY
+
+    $sth->execute($accession);
+
+    my $count = scalar @{ $sth->fetchall_arrayref() };
+
+    $sth->finish();
+
+    return $count;
+}
+
+1;
diff --git a/lib/ArrayExpress/Curator/Entrez_list.pm b/lib/ArrayExpress/Curator/Entrez_list.pm
new file mode 100644
index 0000000..e034bd9
--- /dev/null
+++ b/lib/ArrayExpress/Curator/Entrez_list.pm
@@ -0,0 +1,121 @@
+#!/usr/bin/env perl
+#
+# Entrez_list.pm
+#
+# $Id: Entrez_list.pm 1941 2008-02-11 12:27:15Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../index.html">
+                <img src="../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: Entrez_list.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Curator::Entrez_list - a module used by expt_check.pl
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Curator::Entrez_list qw(parse_entrez_names);
+ 
+ my $is_recognized = parse_entrez_names();
+ die("Error: Unknown journal: $pub") unless $is_recognized->{ $pub };
+
+=head1 DESCRIPTION
+
+This module provides a very basic interface to the listing of approved
+Entrez publication names included with the Tab2MAGE package.
+
+=head1 FUNCTIONS
+
+=over 2
+
+=item C<parse_entrez_names( $fh )>
+
+Parses the contents of either the optional passed filehandle, or the
+internally-defined Entrez_list.txt filehandle, to generate a hashref
+of publication name keys and true values.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2008.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments. 
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Curator::Entrez_list;
+
+use strict;
+use warnings;
+
+use Carp;
+use IO::File;
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw($RE_LINE_BREAK);
+
+use base 'Exporter';
+our @EXPORT_OK = qw(parse_entrez_names);
+
+sub parse_entrez_names {
+
+    # Optional filehandle argument, default is to read the standard
+    # list ($CONFIG->get_DEFAULT_ENTREZ_FILENAME()).
+    my $passed_fh = shift;
+
+    # Get the Entrez list into memory
+    my %entrez_approved;
+    my $entrez_fh = $passed_fh || _entrez_list_fh();
+
+    while ( my $line = <$entrez_fh> ) {
+        $line =~ s/$RE_LINE_BREAK//xms;
+        $entrez_approved{$line}++;
+    }
+
+    return \%entrez_approved;
+}
+
+sub _entrez_list_fh {
+
+    my $filehandle
+        = IO::File->new( $CONFIG->get_DEFAULT_ENTREZ_FILENAME(), '<' )
+        or croak( "Error opening Entrez publications list file "
+            . $CONFIG->get_DEFAULT_ENTREZ_FILENAME()
+            . ": $!\n" );
+
+    return ($filehandle);
+
+}
+
+1;
diff --git a/lib/ArrayExpress/Curator/Entrez_list.txt b/lib/ArrayExpress/Curator/Entrez_list.txt
new file mode 100644
index 0000000..e7701b9
--- /dev/null
+++ b/lib/ArrayExpress/Curator/Entrez_list.txt
@@ -0,0 +1,19188 @@
+1199 News
+20 Century Br Hist
+21st Century Sci Technol
+A & M
+A A G Bijdr
+A M A Arch Ind Hyg Occup Med
+AACN Clin Issues
+AACN Clin Issues Crit Care Nurs
+AADE Ed J
+AAHS J
+AANA J
+AANNT J
+AAOHN J
+AAPPO J
+AAPS J
+AAPS PharmSci
+AAPS PharmSciTech
+AARC Times
+AARN News Lett
+AB Bookm Wkly
+ABA J
+ABC Decor
+ABNF J
+ACE Q
+ACES Bull
+ACM Queue
+ACOG Comm Opin
+ACOG Educ Bull
+ACOG Tech Bull
+ACP J Club
+ACRH Rep
+AD Nurse
+ADA News
+ADM
+AFL CIO Am Fed
+AFOSR TN United States Air Force Off Sci Res
+AFOSR TR United States Air Force Off Sci Res
+AGSO J Aust Geol Geophys
+AHA Hosp Technol Ser
+AHIP Cover
+AHME J
+AHP J
+AIA J
+AIAA Stud J
+AICC Econ Rev
+AIChE J
+AID Eval News
+AIDS
+AIDS Action
+AIDS Action Policy Brief
+AIDS Action Update
+AIDS Alert
+AIDS Anal Afr
+AIDS Anal Asia
+AIDS Asia
+AIDS Behav
+AIDS Care
+AIDS Clin Care
+AIDS Clin Rev
+AIDS Educ Prev
+AIDS Forsch
+AIDS Health Promot Exch
+AIDS Illus
+AIDS Inf Exch
+AIDS Inst Newsl
+AIDS Patient Care
+AIDS Patient Care STDS
+AIDS Policy Law
+AIDS Public Policy J
+AIDS Read
+AIDS Res
+AIDS Res Hum Retroviruses
+AIDS Res Ther
+AIDS Rev
+AIDS STD Health Promot Exch
+AIDS Soc
+AIDS Treat News
+AIDS Updates
+AIDS Watch
+AIDS Wkly
+AIDS Wkly Plus
+AIDSED Newsl
+AIDSlink
+AIHA J (Fairfax, Va)
+AIHAJ
+AIPLA Q J
+AJNR Am J Neuroradiol
+AJR Am J Roentgenol
+AJS
+AJS Rev
+ALAFO
+ALTEX
+AM Rep
+AMA Am J Dis Child
+AMA Arch Derm
+AMA Arch Derm Syphilol
+AMA Arch Ind Health
+AMA Arch Intern Med
+AMA Arch Neurol
+AMA Arch Neurol Psychiatry
+AMA Arch Ophthalmol
+AMA Arch Otolaryngol
+AMA Arch Pathol
+AMA Arch Surg
+AMA J Dis Child
+AMB Rev Assoc Med Bras
+AMD TR Rep
+AMDI Boll
+AMFAR Rep
+AMHC Forum
+AMIA Annu Symp Proc
+AMRL TR
+AMRO
+AN Ark Nurse
+ANA Clin Conf
+ANA Clin Sess
+ANA Publ
+ANEC
+ANL Rep
+ANNA J
+ANPHI Pap
+ANQ
+ANS Adv Nurs Sci
+ANU Hist J
+ANZ J Surg
+AOHA
+AOHA Today
+AORN J
+APA Newsl Philos Med
+APDI Newsl
+APIC
+APMIS
+APMIS Suppl
+AQ
+ARAG Natl Newsl
+ARN J
+ARTnews
+ASA Newsl
+ASAIO J
+ASAIO Trans
+ASDA News
+ASDC J Dent Child
+ASEAN Econ Bull
+ASGSB Bull
+ASHA
+ASHA Monogr
+ASHA Suppl
+ASM News
+ASNA Rep
+ASTM Stand News
+ATLA Abstr
+AUAA J
+AVISO
+AVS News
+AVSC News
+AWHONN Lifelines
+AWHONN Voice
+AWHONNS Clin Issues Perinat Womens Health Nurs
+AWR Bull
+Aachen Kunstbl
+Aastaraam Eesti Tead Seltsi Rootsis
+Abbottempo
+Abdom Imaging
+Abdom Surg
+Aberdeen Univ Rev
+Abh Ber Dtsch Mus
+Abh Ber Staatl Mus Voelkerkd Dres
+Abh Gesch Med Naturwiss
+Aborig Isl Health Work J
+Abort Res Notes
+Abortion Rev
+Abr Nahain
+Abstr Gen Meet Am Soc Microbiol
+Abstr Soc Neurosci
+Acad Bookman
+Acad Emerg Med
+Acad Manage J
+Acad Manage Rev
+Acad Med
+Acad News
+Acad Nurse
+Acad Peru Cir
+Acad Psychiatry
+Acad Radiol
+Acad Rev Calif Acad Periodontol
+Academic Psychol Bull
+Acadiensis
+Acarologia
+Acc Chem Res
+Accad Bibl Ital
+Accad Medica
+Accad Naz Lincei Rome
+Accid Anal Prev
+Accid Emerg Nurs
+Account Res
+Acog Nurse Bull
+Acoust Res Lett Online
+Acquir Immunodefic Syndr (AIDS) Wkly Surveill Rep
+Acquis Med Recent
+Across Board (NY)
+Act Hepato
+Act Nerv Super (Praha)
+Acta Acad Med Wuhan
+Acta Acad Sci Pol
+Acta Agron
+Acta Allergol
+Acta Allergol Suppl (Copenh)
+Acta Anaesthesiol
+Acta Anaesthesiol Belg
+Acta Anaesthesiol Ital
+Acta Anaesthesiol Scand
+Acta Anaesthesiol Scand Suppl
+Acta Anaesthesiol Sin
+Acta Anaesthesiol Taiwan
+Acta Anat (Basel)
+Acta Anat Suppl (Basel)
+Acta Anthropogenet
+Acta Antiq Hung
+Acta Appl Math
+Acta Argent Fisiol Fisiopatol
+Acta Astronaut
+Acta Balt
+Acta Belg Arte Med Pharm Mil
+Acta Belg Hist Med
+Acta Belg Med Phys
+Acta Biochim Biophys Acad Sci Hung
+Acta Biochim Biophys Hung
+Acta Biochim Biophys Sin (Shanghai)
+Acta Biochim Pol
+Acta Biol Acad Sci Hung
+Acta Biol Exp (Warsz)
+Acta Biol Hung
+Acta Biol Med (Gdansk)
+Acta Biol Med Ger
+Acta Biomed Ateneo Parmense
+Acta Biotechnol
+Acta Biotheor
+Acta Bot Gallica
+Acta Brevia Neerl Physiol Pharmacol Microbiol E A
+Acta Cancerol (Lima)
+Acta Cardiol
+Acta Cardiol Suppl
+Acta Chem Scand
+Acta Chem Scand A
+Acta Chem Scand B
+Acta Chir Acad Sci Hung
+Acta Chir Belg
+Acta Chir Hung
+Acta Chir Ital
+Acta Chir Iugosl
+Acta Chir Orthop Traumatol Cech
+Acta Chir Patav
+Acta Chir Plast
+Acta Chir Scand
+Acta Chir Scand Suppl
+Acta Cient Venez
+Acta Cir Bras
+Acta Cl
+Acta Clin Belg
+Acta Clin Belg Suppl
+Acta Clin Odontol
+Acta Crystallogr
+Acta Crystallogr A
+Acta Crystallogr B
+Acta Crystallogr C
+Acta Crystallogr D Biol Crystallogr
+Acta Crystallograph Sect F Struct Biol Cryst Commun
+Acta Cytol
+Acta Demogr
+Acta Derm Venereol
+Acta Derm Venereol Suppl (Stockh)
+Acta Dermatol Kyoto Engl Ed
+Acta Dermatovenerol Alp Panonica Adriat
+Acta Dermatovenerol Croat
+Acta Diabetol
+Acta Diabetol Lat
+Acta Embryol Exp (Palermo)
+Acta Embryol Morphol Exp
+Acta Endocrinol (Copenh)
+Acta Endocrinol Iber
+Acta Endocrinol Panam
+Acta Endocrinol Suppl (Copenh)
+Acta Endoscopica
+Acta Ethnol Linguist
+Acta Eur Fertil
+Acta Gastroenterol Belg
+Acta Gastroenterol Latinoam
+Acta Genet Med Gemellol (Roma)
+Acta Genet Stat Med
+Acta Geogr
+Acta Geogr Croat
+Acta Geogr Sin
+Acta Gerontol (Milano)
+Acta Ginecol (Madr)
+Acta Gynaecol Obstet Hisp Lusit
+Acta Haematol
+Acta Haematol Pol
+Acta Hepatogastroenterol (Stuttg)
+Acta Hepatosplenol
+Acta Hist Leopoldina
+Acta Hist Med Pharm Vet (Beograd)
+Acta Hist Med Vallisoletana Monogr
+Acta Hist Rerum Nat Tech
+Acta Hist Sci Nat Med
+Acta Histochem
+Acta Histochem Suppl
+Acta Hortic
+Acta Hosp
+Acta Iber Radiol Cancerol
+Acta Isot (Padova)
+Acta Leiden
+Acta Leprol
+Acta Manila Ser A
+Acta Med Acad Sci Hung
+Acta Med Austriaca
+Acta Med Austriaca Suppl
+Acta Med Auxol (Milano)
+Acta Med Biol (Niigata)
+Acta Med Cathol
+Acta Med Colomb
+Acta Med Costarric
+Acta Med Croatica
+Acta Med Dom
+Acta Med Hidalguense
+Acta Med Hisp
+Acta Med Hist Patav
+Acta Med Hung
+Acta Med Indones
+Acta Med Iran
+Acta Med Ital Mal Infett Parassit
+Acta Med Ital Med Trop Subtrop Gastroenterol
+Acta Med Iugosl
+Acta Med Leg Soc (Liege)
+Acta Med Nagasaki
+Acta Med Okayama
+Acta Med Orient
+Acta Med Patav
+Acta Med Patavina Suppl
+Acta Med Philipp
+Acta Med Pol
+Acta Med Port
+Acta Med Rom
+Acta Med Scand
+Acta Med Scand Suppl
+Acta Med Tenerife
+Acta Med Valle
+Acta Med Vet (Napoli)
+Acta Med Vietnam
+Acta Medica (Hradec Kralove)
+Acta Medica (Hradec Kralove) Suppl
+Acta Medica Cordoba
+Acta Microbiol Acad Sci Hung
+Acta Microbiol Bulg
+Acta Microbiol Hung
+Acta Microbiol Immunol Hung
+Acta Microbiol Pol
+Acta Microbiol Pol A
+Acta Microbiol Pol B
+Acta Microbiol Sin
+Acta Microbiol Virol Immunol (Sofiia)
+Acta Morphol Acad Sci Hung
+Acta Morphol Hung
+Acta Morphol Neerl Scand
+Acta Mus Napocensis
+Acta Myol
+Acta Neurobiol Exp (Wars)
+Acta Neurochir (Wien)
+Acta Neurochir Suppl
+Acta Neurochir Suppl (Wien)
+Acta Neurol (Napoli)
+Acta Neurol Belg
+Acta Neurol Latinoam
+Acta Neurol Psychiatr Belg
+Acta Neurol Scand
+Acta Neurol Scand Suppl
+Acta Neurol Taiwan
+Acta Neurol [Quad] (Napoli)
+Acta Neuropathol (Berl)
+Acta Neuropathol Suppl (Berl)
+Acta Neuroveg (Wien)
+Acta Neuroveg Suppl
+Acta Numis
+Acta Obstet Ginecol Hisp Lusit
+Acta Obstet Ginecol Hisp Lusit Suppl
+Acta Obstet Gynaecol Jpn
+Acta Obstet Gynecol Scand
+Acta Obstet Gynecol Scand Suppl
+Acta Odontol Latinoam
+Acta Odontol Pediatr
+Acta Odontol Scand
+Acta Odontol Scand Suppl
+Acta Odontol Venez
+Acta Oncol
+Acta Oncol (Madr)
+Acta Ophthalmol (Copenh)
+Acta Ophthalmol Scand
+Acta Ophthalmol Scand Suppl
+Acta Ophthalmol Suppl
+Acta Orthop
+Acta Orthop Belg
+Acta Orthop Scand
+Acta Orthop Scand Suppl
+Acta Orthop Suppl
+Acta Orthop Traumatol Turc
+Acta Otolaryngol
+Acta Otolaryngol Suppl
+Acta Otorhinolaryngol Belg
+Acta Otorhinolaryngol Ital
+Acta Otorinolaryngol Iber Am
+Acta Otorrinolaringol Esp
+Acta Paediatr
+Acta Paediatr Acad Sci Hung
+Acta Paediatr Belg
+Acta Paediatr Hung
+Acta Paediatr Jpn
+Acta Paediatr Lat
+Acta Paediatr Scand
+Acta Paediatr Scand Suppl
+Acta Paediatr Suppl
+Acta Paediatr Taiwan
+Acta Paedolog
+Acta Paedopsychiatr
+Acta Palaeontol Pol
+Acta Pathol Jpn
+Acta Pathol Microbiol Immunol Scand Suppl
+Acta Pathol Microbiol Immunol Scand [A]
+Acta Pathol Microbiol Immunol Scand [B]
+Acta Pathol Microbiol Immunol Scand [C]
+Acta Pathol Microbiol Scand
+Acta Pathol Microbiol Scand Suppl
+Acta Pathol Microbiol Scand [A]
+Acta Pathol Microbiol Scand [B]
+Acta Pathol Microbiol Scand [B] Microbiol Immunol
+Acta Pathol Microbiol Scand [C]
+Acta Pediatr Esp
+Acta Pharm
+Acta Pharm Hung
+Acta Pharm Int
+Acta Pharm Jugosl
+Acta Pharm Nord
+Acta Pharm Suec
+Acta Pharmacol Sin
+Acta Pharmacol Toxicol (Copenh)
+Acta Physiol (Oxf)
+Acta Physiol Acad Sci Hung
+Acta Physiol Hung
+Acta Physiol Lat Am
+Acta Physiol Pharmacol Bulg
+Acta Physiol Pharmacol Latinoam
+Acta Physiol Pharmacol Neerl
+Acta Physiol Pharmacol Ther Latinoam
+Acta Physiol Plant
+Acta Physiol Pol
+Acta Physiol Scand
+Acta Physiol Scand Suppl
+Acta Physiother Rheumatol Belg
+Acta Phytophysiol Sinica
+Acta Pol Hist
+Acta Pol Pharm
+Acta Protozool
+Acta Psiquiatr Psicol Am Lat
+Acta Psychiatr Belg
+Acta Psychiatr Neurol Scand
+Acta Psychiatr Neurol Scand Suppl
+Acta Psychiatr Scand
+Acta Psychiatr Scand Suppl
+Acta Psychol (Amst)
+Acta Psychother Psychosom Orthopaedagog
+Acta Radiol
+Acta Radiol Cancerol
+Acta Radiol Diagn (Stockh)
+Acta Radiol Oncol
+Acta Radiol Oncol Radiat Phys Biol
+Acta Radiol Suppl
+Acta Radiol Ther Phys Biol
+Acta Reprod Turc
+Acta Rheumatol Scand
+Acta Rhumatol
+Acta Rhumatol Belg
+Acta Salmant Hist Med
+Acta Salmanticensia Ser Filos Letra
+Acta Sch Med Univ Kioto
+Acta Sci Sin
+Acta Soc Med Ups
+Acta Soc Med Ups Suppl
+Acta Sociol
+Acta Sociomed Scand
+Acta Sociomed Scand Suppl
+Acta Stomatol Belg
+Acta Stomatol Croat
+Acta Stomatol Int
+Acta Ther
+Acta Theriol (Warsz)
+Acta Trop
+Acta Trop Suppl
+Acta Tuberc Belg
+Acta Tuberc Jpn
+Acta Tuberc Pneumol Belg
+Acta Tuberc Pneumol Scand
+Acta Tuberc Scand
+Acta Tuberc Scand Suppl
+Acta Unio Int Contra Cancrum
+Acta Univ Agric Fac Agron
+Acta Univ Carol Geogr
+Acta Univ Carol Med Monogr
+Acta Univ Carol Philos Hist
+Acta Univ Carol [Med] (Praha)
+Acta Univ Lund
+Acta Univ Palacki Olomuc Fac Med
+Acta Univ Szeged Attila Jozsef Nominatae
+Acta Univ Ups
+Acta Urol Belg
+Acta Vet (Beogr)
+Acta Vet Acad Sci Hung
+Acta Vet Brno
+Acta Vet Hung
+Acta Vet Scand
+Acta Vet Scand Suppl
+Acta Virol
+Acta Vitaminol
+Acta Vitaminol Enzymol
+Acta Zool
+Acta Zool Pathol Antverp
+Actas Dermatol Dermatopatol
+Actas Dermosifiliogr
+Actas Esp Psiquiatr
+Actas Luso Esp Neurol Psiquiatr
+Actas Luso Esp Neurol Psiquiatr Cienc Afines
+Actas Reun Cient Cuerfo Fac Inst Policlin
+Actas Urol Esp
+Actes Rech Sci Soc
+Action Child
+Action Contre SIDA
+Action Natl
+Actionaid Disabil News
+Acts Legis Prov Manit Manit
+Acts Parliam Commonw Aust Pass Sess Aust
+Actual Anatpathol
+Actual Biol
+Actual Cardiol Angeiol Int (Paris)
+Actual Chim
+Actual Econ
+Actual Endocrinol (Paris)
+Actual Hepatogastroenterol (Paris)
+Actual Jurid Aranzadi
+Actual Medica
+Actual Neurophysiol (Paris)
+Actual Odontostomatol (Paris)
+Actual Pediatr (Granada)
+Actual Pharm
+Actual Pharmacol (Paris)
+Actual Physiol Pathol (Paris)
+Actual Psychiatr
+Actuel Urol
+Acupunct Electrother Res
+Acupunct Med
+Acute Care
+Ad Astra
+Addict Behav
+Addict Biol
+Addict Dis
+Addiction
+Additamenta Folia Med Neerl
+Adiyat Halab
+Adler
+Adler Mus Bull
+Adm Change
+Adm Dev
+Adm Law J
+Adm Law Rev
+Adm Manage
+Adm Ment Health
+Adm Policy Ment Health
+Adm Radiol
+Adm Radiol J
+Adm Sci Q
+Adm Soc
+Adm Soc Work
+Admit Manage J
+Adolesc Educ Newsl
+Adolesc Med
+Adolesc Med Clin
+Adolesc Pediatr Gynecol
+Adolesc Psychiatry
+Adolescence
+Adult Educ
+Adv Abort Care
+Adv Adolesc Mental Health
+Adv Alcohol Subst Abuse
+Adv Anat Embryol Cell Biol
+Adv Anat Pathol
+Adv Appl Microbiol
+Adv Behav Biol
+Adv Biochem Eng Biotechnol
+Adv Biochem Psychopharmacol
+Adv Bioeth
+Adv Biol Med Phys
+Adv Biol Skin
+Adv Biomed Eng Med Phys
+Adv Biophys
+Adv Biosci
+Adv Biotechnol Processes
+Adv Bot Res
+Adv Cancer Res
+Adv Carbohydr Chem
+Adv Carbohydr Chem Biochem
+Adv Card Surg
+Adv Cardiol
+Adv Cardiopulm Dis
+Adv Cell Biol
+Adv Chem Ser
+Adv Chemother
+Adv Child Dev Behav
+Adv Chromatogr
+Adv Chronic Kidney Dis
+Adv Clin Care
+Adv Clin Chem
+Adv Clin Path
+Adv Clin Pharmacol
+Adv Clin Rehabil
+Adv Colloid Interface Sci
+Adv Comp Physiol Biochem
+Adv Consum Res
+Adv Contracept
+Adv Contracept Deliv Syst
+Adv Cryog Eng
+Adv Cyclic Nucleotide Protein Phosphorylation Res
+Adv Cyclic Nucleotide Res
+Adv Cytopharmacol
+Adv Data
+Adv Dent Res
+Adv Dermatol
+Adv Drug Deliv Rev
+Adv Drug Res
+Adv Ecol Rs
+Adv Endocrinol Metab
+Adv Enzyme Regul
+Adv Enzymol Relat Areas Mol Biol
+Adv Enzymol Relat Subj Biochem
+Adv Exp Med Biol
+Adv Fertil Control
+Adv Fluorine Res
+Adv Food Nutr Res
+Adv Food Res
+Adv Food Res Suppl
+Adv Genet
+Adv Gerontol
+Adv Gerontol Res
+Adv Health Econ Health Serv Res
+Adv Health Econ Health Serv Res Suppl
+Adv Health Sci Educ Theory Pract
+Adv Heterocycl Chem
+Adv Hum Genet
+Adv Imaging
+Adv Immun Cancer Ther
+Adv Immunol
+Adv Inorg Biochem
+Adv Intern Med
+Adv Lipid Res
+Adv Mar Biol
+Adv Mass Spectrom
+Adv Med Sociol
+Adv Metab Disord
+Adv Microb Ecol
+Adv Microb Physiol
+Adv Mind Body Med
+Adv Morphog
+Adv Myocardiol
+Adv Myochem
+Adv Neonatal Care
+Adv Nephrol Necker Hosp
+Adv Neural Inf Process Syst
+Adv Neuroimmunol
+Adv Neurol
+Adv Nurse Pract
+Adv Nutr Res
+Adv Ophthalmic Plast Reconstr Surg
+Adv Ophthalmol
+Adv Oral Biol
+Adv Otorhinolaryngol
+Adv Pain Res Ther
+Adv Parasitol
+Adv Pathobiol
+Adv Pediatr
+Adv Pediatr Infect Dis
+Adv Perit Dial
+Adv Pest Control Res
+Adv Pharm Sci
+Adv Pharmacol
+Adv Pharmacol Chemother
+Adv Physiol Educ
+Adv Plan Parent
+Adv Popul
+Adv Pract Nurs Q
+Adv Prostaglandin Thromboxane Leukot Res
+Adv Prostaglandin Thromboxane Res
+Adv Protein Chem
+Adv Psychobiol
+Adv Psychol Res
+Adv Psychosom Med
+Adv Radiat Biol
+Adv Ren Replace Ther
+Adv Reprod Physiol
+Adv Robot
+Adv Sci
+Adv Second Messenger Phosphoprotein Res
+Adv Sex Horm Res
+Adv Shock Res
+Adv Skin Wound Care
+Adv Sociodent Res
+Adv Space Biol Med
+Adv Space Res
+Adv Steroid Biochem Pharmacol
+Adv Surg
+Adv Tech Stand Neurosurg
+Adv Ther
+Adv Tracer Methodol
+Adv Tuberc Res
+Adv Vet Med
+Adv Vet Sci
+Adv Vet Sci Comp Med
+Adv Virus Res
+Adv Wound Care
+Advent Herit
+Adverse Drug React Acute Poisoning Rev
+Adverse Drug React Bull
+Adverse Drug React Toxicol Rev
+Advert Age
+Advocacy Now
+Advocate
+Aegyptus
+Aere Perennius
+Aeromed Acta
+Aeromed Rev
+Aeronaut Astronaut
+Aerosol Sci Technol
+Aerosp Am
+Aerosp Eng
+Aerosp Hist
+Aerosp Med
+Aesthetic Plast Surg
+Aevum
+Aff Soc Int
+Affilia
+Afghan Med J
+Afghanistan
+Afr Aff (Lond)
+Afr Alternat
+Afr Anal
+Afr Asie Mod
+Afr Concord
+Afr Contemp
+Afr Demogr
+Afr Dent J
+Afr Dev
+Afr Dev Rev
+Afr Doc
+Afr Econ Hist
+Afr Environ
+Afr Farmer
+Afr Francaise Chir
+Afr Health
+Afr Health Sci
+Afr Hist Stud
+Afr Insight
+Afr J Fertil Sexual Reprod Heal
+Afr J Health Sci
+Afr J Med Med Sci
+Afr J Med Pract
+Afr J Med Sci
+Afr J Psychiatry
+Afr J Psychol Study Soc Issues
+Afr J Reprod Health
+Afr J Sex Transmi Dis
+Afr J Sociol
+Afr Link
+Afr Litt Artist
+Afr Marburgen
+Afr Med
+Afr Media Rev
+Afr Notes
+Afr Notes News
+Afr Popul Dev Bull
+Afr Popul Newsl
+Afr Q
+Afr Recovery
+Afr Rep
+Afr Res Bull
+Afr Rev
+Afr Soc Res
+Afr South
+Afr Stud
+Afr Stud Rev
+Afr Study Monogr
+Afr Today
+Afr Uebersee
+Afr Urban Notes B
+Afr Urban Q
+Afr Urban Stud
+Afr Woman
+Afr Women Health
+Africa
+Africa (Lond)
+Africasia
+Africom
+Afrique 2000
+Afro Am N Y Life Hist
+Afro Tech Pap
+Afya
+Agapit
+Age
+Age (Omaha)
+Age Ageing
+Aged Care Serv Rev
+Ageing Int
+Ageing Res Rev
+Ageing Soc
+Agenda
+Agenda Salud
+Agents Actions
+Agents Actions Suppl
+Aggiorn Clinico Ter
+Aggiorn Fisiol
+Aggiorn Ostet Ginecol
+Aggiorn Pediatr
+Aggiorn Sulle Mal Infez
+Aggiorn Ter Oftalmol
+Aggress Behav
+Aggress Violent Behav
+Aging
+Aging (Milano)
+Aging Cell
+Aging Clin Exp Res
+Aging Leis Living
+Aging Male
+Aging Ment Health
+Aging Trends
+Aging Work
+Agnes Karll Schwest Krankenpfleger
+Agratort Szle
+Agressologie
+Agri
+Agric Biol Chem
+Agric Econ Res
+Agric Hist
+Agric Hist Rev
+Agric Human Values
+Agric Inf Dev Bull
+Agric Syst
+Agron J
+Aguiaine
+Ahfad J
+Ahot Beyisrael
+Ai Zheng
+Aichi Gakuin Daigaku Shigakkai Shi
+Aichi Gakuin Dent Sci
+Aidos News
+Aidscaptions
+Ain Shams Med J
+Air Force Law Rev
+Air Line Pilot
+Air Med J
+Air Space
+Air Univ Rev (US ed)
+Air Waste
+Air Water Pollut
+Aisa Found News
+Akad Dzive
+Akad Iatr
+Akron Bus Econ Rev
+Akron Law Rev
+Aktuel Endokrinol Stoffwechsel
+Aktuel Ernahrungsmed
+Aktuel Probl Phoniatr Logop
+Aktuelle Derm
+Aktuelle Gerontol
+Aktuelle Otorhinolaryngol
+Aktuelle Probl Chir
+Aktuelle Probl Chir Orthop
+Aktuelle Radiol
+Aktuelle Traumatol
+Aktuelle Urol
+Akush Ginekol (Mosk)
+Akush Ginekol (Sofiia)
+Akwesasne Notes
+Al Azhar Med J
+Al Darat
+Al Mihan Al Tibbiyah
+Al Qantara
+Al Raida
+Al Usrah Wa Al Umran Al Bashari
+Al-Arabi
+Ala Dent Rev
+Ala Herit
+Ala Hist Q
+Ala J Med Sci
+Ala Law Rev
+Ala Med
+Ala Nurse
+Ala Rev
+Alaska J
+Alaska Med
+Alaska Nurse
+Albany Law J Sci Technol
+Albany Law Rev
+Alberta Hist
+Alberta Med Bull
+Albertus Magnus Blaett
+Albion
+Albrecht Von Graefes Arch Klin Exp Ophthalmol
+Albrecht Von Graefes Arch Ophthalmol
+Albuq Tribune
+Alcohol
+Alcohol Alcohol
+Alcohol Alcohol Suppl
+Alcohol Clin Exp Res
+Alcohol Drug Res
+Alcohol Drugs Driving
+Alcohol Health Res World
+Alcohol Res Health
+Alcohol Treat Q
+Aldarah
+Alergia
+Alex Dent J
+Alex J Pharm Sci
+Alexandria Med J
+Alger Medicale
+Aliment Pharmacol Ther
+Aliment Vie
+Aliso
+Alkaloids Chem Biol
+All Engl Law Rep
+All Stat Arch
+Allem Aujourdhui
+Allerg Asthma (Leipz)
+Allerg Asthmaforsch
+Allerg Immunol (Leipz)
+Allerg Immunol (Paris)
+Allerg Pointe Claire
+Allergo J
+Allergol Immunopathol (Madr)
+Allergy
+Allergy Asthma Proc
+Allergy Proc
+Allg Homoopath Ztg
+Allied Health Behav Sci
+Alma Mater (Baltimore)
+Alma Mater Philipp
+Almanac
+Almustaqbal Alarabi
+Alpha Omega Fr
+Alpha Omegan
+Alsk Hist
+Alsk Statut 1962 Alsk
+Alt Thuring
+Alta Law Rev
+Alta RN
+Altamura
+Altern Entwickl Aging Dev
+Altern Lab Anim
+Altern Med Rev
+Altern Ther Health Med
+Alternative Lifestyles
+Altertum
+Altnurnb Landsch
+Altsprachi Unterr
+Alumnae Mag
+Alumnae Mag Columbia Univ Presbyt Hosp Sch Nurs Alumnae Assoc
+Alumni Bull Sch Dent Indiana Univ
+Alumni Bull Univ Mich Sch Dent
+Alumni Mag
+Alumni Mag Columbia Univ Presbyt Hosp Sch Nurs Alumni Assoc
+Alzheimer Dis Assoc Disord
+Am Ann Deaf
+Am Anthropol
+Am Antiq
+Am Arch
+Am Arch Rehabil Ther
+Am Art J
+Am Asian Rev
+Am Assoc Ind Nurses J
+Am Assoc Pet Geol Bull
+Am Baby
+Am Bar Assoc J
+Am Bar Found Res J
+Am Behav Sci
+Am Benedictine Rev
+Am Biol Teach
+Am Biotechnol Lab
+Am Clin Lab
+Am Coll Cardiol Extended Learn Suppl Tape
+Am Coll Physicians Obs
+Am Correct Ther J
+Am Crim Law Rev
+Am Demogr
+Am Eccles Rev
+Am Econ
+Am Econ Rev
+Am Educ
+Am Educ Res J
+Am Enterp
+Am Fam Physician
+Am Fam Physician GP
+Am Health
+Am Heart Hosp J
+Am Heart J
+Am Herit
+Am Herit Invent Technol
+Am Hist Illus
+Am Hist Rev
+Am Horseman
+Am Imago
+Am Ind Hyg Assoc J
+Am Ind Hyg Assoc Q
+Am Indian Alsk Native Ment Health Res
+Am Indian Alsk Native Ment Health Res Monogr Ser
+Am Indian Art Mag
+Am Indian Cult Res J
+Am Indian Q
+Am Indig
+Am J Acupunct
+Am J Addict
+Am J Agric Econ
+Am J Alzheimers Dis Other Demen
+Am J Anat
+Am J Anesthesiol
+Am J Art Ther
+Am J Audiol
+Am J Bioeth
+Am J Bot
+Am J Cancer
+Am J Card Imaging
+Am J Cardiol
+Am J Cardiovasc Drugs
+Am J Cardiovasc Pathol
+Am J Chin Med
+Am J Chin Med (Gard City N Y)
+Am J Clin Dermatol
+Am J Clin Hypn
+Am J Clin Nutr
+Am J Clin Oncol
+Am J Clin Pathol
+Am J Community Psychol
+Am J Comp Law
+Am J Contact Dermat
+Am J Correct
+Am J Crim Law
+Am J Crit Care
+Am J Dent
+Am J Dermatopathol
+Am J Dig Dis
+Am J Dis Child
+Am J Drug Alcohol Abuse
+Am J EEG Technol
+Am J Econ Sociol
+Am J Electroneurodiagnostic Technol
+Am J Emerg Med
+Am J Epidemiol
+Am J Ethics Med
+Am J Fam Law
+Am J Fam Ther
+Am J Forensic Med Pathol
+Am J Forensic Psychiatry
+Am J Forensic Psychol
+Am J Gastroenterol
+Am J Geriatr Cardiol
+Am J Geriatr Pharmacother
+Am J Geriatr Psychiatry
+Am J Gynecol Health
+Am J Health Behav
+Am J Health Plann
+Am J Health Promot
+Am J Health Syst Pharm
+Am J Hematol
+Am J Hosp Care
+Am J Hosp Palliat Care
+Am J Hosp Pharm
+Am J Hum Biol
+Am J Hum Genet
+Am J Hyg
+Am J Hypertens
+Am J Ind Med
+Am J Infect Control
+Am J Int Law
+Am J Jurisprud
+Am J Kidney Dis
+Am J Knee Surg
+Am J Law Med
+Am J Leg Hist
+Am J Manag Care
+Am J Med
+Am J Med Electron
+Am J Med Genet
+Am J Med Genet A
+Am J Med Genet B Neuropsychiatr Genet
+Am J Med Genet C Semin Med Genet
+Am J Med Genet Suppl
+Am J Med Qual
+Am J Med Sci
+Am J Med Technol
+Am J Ment Defic
+Am J Ment Retard
+Am J Nephrol
+Am J Nurs
+Am J Obstet Dis Women Child
+Am J Obstet Gynecol
+Am J Occup Ther
+Am J Ophthalmol
+Am J Optom Arch Am Acad Optom
+Am J Optom Physiol Opt
+Am J Orthod
+Am J Orthod Dentofacial Orthop
+Am J Orthop
+Am J Orthop Surg
+Am J Orthopsychiatry
+Am J Otol
+Am J Otolaryngol
+Am J Pathol
+Am J Pediatr Hematol Oncol
+Am J Perinatol
+Am J Pharm
+Am J Pharm Educ
+Am J Pharm Sci Support Public Health
+Am J Pharmacogenomics
+Am J Philol
+Am J Phys
+Am J Phys Anthropol
+Am J Phys Med
+Am J Phys Med Rehabil
+Am J Physiol
+Am J Physiol Cell Physiol
+Am J Physiol Endocrinol Metab
+Am J Physiol Gastrointest Liver Physiol
+Am J Physiol Heart Circ Physiol
+Am J Physiol Imaging
+Am J Physiol Lung Cell Mol Physiol
+Am J Physiol Regul Integr Comp Physiol
+Am J Physiol Renal Physiol
+Am J Pol Sci
+Am J Pract Nurs
+Am J Prev Med
+Am J Primatol
+Am J Proctol
+Am J Proctol Gastroenterol Colon Rectal Surg
+Am J Psychiatry
+Am J Psychoanal
+Am J Psychol
+Am J Psychother
+Am J Public Health
+Am J Public Health Nations Health
+Am J Reprod Immunol
+Am J Reprod Immunol Microbiol
+Am J Respir Cell Mol Biol
+Am J Respir Crit Care Med
+Am J Respir Med
+Am J Rhinol
+Am J Roentgenol Radium Ther Nucl Med
+Am J Sci
+Am J Soc Psychiatry
+Am J Sociol
+Am J Speech Lang Pathol
+Am J Sports Med
+Am J Surg
+Am J Surg Pathol
+Am J Syph Gonorrhea Vener Dis
+Am J Ther
+Am J Transplant
+Am J Trial Advocacy
+Am J Trop Med Hyg
+Am J Vet Med
+Am J Vet Res
+Am Jew
+Am Jew Arch
+Am Jew Hist
+Am Jew Hist Q
+Am Lab
+Am Lat
+Am Laund Dig
+Am Law Rep ALR 3rd Cases Annot
+Am Libr
+Am Lit
+Am Lung Assoc Bull
+Am Math Mon
+Am Med News
+Am Mus Novit
+Am Nat
+Am Neptune
+Am Nurse
+Am Orthopt J
+Am Pharm
+Am Philat
+Am Philos Q
+Am Polit Q
+Am Polit Sci Rev
+Am Potato J
+Am Pract Dig Treat
+Am Presbyt
+Am Prospect
+Am Psychol
+Am Q
+Am Rehabil
+Am Rev Can Stud
+Am Rev Respir Dis
+Am Rev Tuberc
+Am Sch Board J
+Am Scholar
+Am Sci
+Am Soc Legion Honor Mag
+Am Sociol
+Am Sociol Rev
+Am Spect
+Am Speech
+Am Stat
+Am Stud (Lawrence)
+Am Surg
+Am Univ Field Staff Rep Afr
+Am Univ Field Staff Rep Asia
+Am Univ Field Staff Rep North Am
+Am Univ Field Staff Rep South Am
+Am Univ J Gend Soc Policy Law
+Am Univ J Int Law Policy
+Am Univ Law Rev
+Am Zool
+Amatus Lusit
+Ambio
+Ambix
+Ambul Care
+Ambul Outreach
+Ambul Pediatr
+Amerasia J
+America (NY)
+Americas (Engl ed)
+Amerikastudien
+Amic J
+Amicus
+Amif
+Amino Acids
+Amphib Reptile Conserv
+Amstelodamum
+Amyloid
+Amyotroph Lateral Scler Other Motor Neuron Disord
+An Acad Bras Cienc
+An Acad Geogr Hist Guatem
+An Antropol
+An Bras Derm Sifilogr
+An Bras Dermatol
+An Bras Ginecol
+An Casa Salud Valdecilla
+An Chil Hist Med
+An Cir (Rosario)
+An Esc Nacl Saude Publica Med Trop (Lisb)
+An Esp Odontoestomatol
+An Esp Pediatr
+An Fac Farm Porto
+An Fac Med Lima
+An Fac Med Montev
+An Fac Med Porto Alegre
+An Fac Med Recife
+An Fac Med Univ Repub Montev Urug
+An Fac Med Univ Sao Paulo
+An Fac Odontol
+An Fac Odontol Univ Fed Pernambuco
+An Fac Quim Farm (Santiago)
+An Farm Hosp (Madr)
+An Gazi Husrev Begove Bibl
+An Hist
+An Hist Antigua Mediev
+An Hist Contemp
+An Hosp St Cruz San Pablo
+An Ilustre Col Of Med
+An Inst Barraquer
+An Inst Estud Madr
+An Inst Hig Med Trop (Lisb)
+An Inst Invest Odontol (Maracaibo)
+An Inst Invest Vet
+An Inst Med Trop (Lisb)
+An Inst Nac Antropol Hist
+An Klin Bol Dr M Stojanovic
+An Matern Sao Paulo
+An Med (Barc)
+An Med (Lima)
+An Med Cir
+An Med Concepc
+An Med Espec
+An Med Interna
+An Med Med
+An Med Publica
+An Microbiol (Rio J)
+An Nestle (B Aires)
+An Nestle (Rio De Janeiro)
+An Otorrinolaringol Ibero Am
+An Paul Med Cir
+An Pediatr (Barc)
+An Programa Acad Med (Lima)
+An R Acad Nac Med (Madr)
+An Real Acad Farm
+An Sist Sanit Navar
+An Soc Geogr Hist
+An Soc Mex Hist Cienc Tec
+An Stiint Univ AI I Cuza Iasi Sect III C Stiint Econ
+An Univ Bucur Istor
+Anadolu Kardiyol Derg
+Anaerobe
+Anaesth Intensive Care
+Anaesth Resusc Intensive Ther
+Anaesthesia
+Anaesthesiol Reanim
+Anaesthesist
+Anal Bioanal Chem
+Anal Biochem
+Anal Cell Pathol
+Anal Chem
+Anal Chim Acta
+Anal Lett
+Anal Polit
+Anal Psychol
+Anal Quant Cytol
+Anal Quant Cytol Histol
+Anal Sci
+Anal Soc
+Analecta Sacra Tarracon
+Analog Sci Fict Sci Fact
+Analysis
+Analyst
+Analyst (Lond)
+Analytica
+Anasth Intensivther Notfallmed
+Anasthesiol Intensivmed Notfallmed Schmerzther
+Anasthesiol Intensivmed Prax
+Anat Anz
+Anat Chir
+Anat Clin
+Anat Embryol (Berl)
+Anat Histol Embryol
+Anat Pathol
+Anat Rec
+Anat Rec A Discov Mol Cell Evol Biol
+Anat Rec B New Anat
+Anat Rec Suppl
+Anat Sci Int
+Anc Philos
+Andover Newton Q
+Andrologia
+Andrologie
+Anest Rianim
+Anesteziol Reanimatol
+Anesth Anal
+Anesth Analg
+Anesth Analg (Paris)
+Anesth Pain Control Dent
+Anesth Prog
+Anesthesiol Clin North America
+Anesthesiol Rev
+Anesthesiology
+Angeiol Ann Soc Fr Angeiol Histopathol
+Angeiologie
+Angelicum
+Angew Chem Int Ed Engl
+Angew Chem Weinheim Bergstr Ger
+Angew Parasitol
+Angiogenesis
+Angiol Sosud Khir
+Angiologia
+Angiologica
+Angiology
+Angle Orthod
+Anglia
+Anglican Theol Rev
+Anglo Am Law Rev
+Anglo Ger Med Rev
+Anglo Sax Engl
+Anim Agenda
+Anim Behav
+Anim Biol Leiden Neth
+Anim Biotechnol
+Anim Blood Groups Biochem Genet
+Anim Blood Groups Biochem Genet Suppl
+Anim Cogn
+Anim Genet
+Anim Health Res Rev
+Anim Learn Behav
+Anim Prod
+Anim Reprod Sci
+Anim Technol
+Anim Welf
+Ankara Univ Hekim Fak Derg
+Ann Acad Med Singapore
+Ann Acad Med Stetin
+Ann Acad Med Stetin Supl
+Ann Acad Sci Fenn A
+Ann Acad Sci Fenn A 3 Geol Geogr
+Ann Acad Sci Fenn [Biol]
+Ann Agric Environ Med
+Ann Allergy
+Ann Allergy Asthma Immunol
+Ann Am Acad Pol Soc Sci
+Ann Anat
+Ann Anat Pathol (Paris)
+Ann Anesthesiol Fr
+Ann Appl Biol
+Ann Archeol Syrie
+Ann Arid Zone
+Ann Assoc Am Geogr
+Ann Aust Coll Dent Surg
+Ann Behav Med
+Ann Belg Med Mil
+Ann Biochem Exp Med
+Ann Biol
+Ann Biol Anim Biochim Biophys
+Ann Biol Clin (Paris)
+Ann Biomed Eng
+Ann Bot (Lond)
+Ann Bourgogne
+Ann Bretagne Payes Ouest
+Ann Cardiol Angeiol (Paris)
+Ann Cerc Roy Hist Archeol Ath
+Ann Chim
+Ann Chin Hist Soc Pac Northwest
+Ann Chir
+Ann Chir Gynaecol
+Ann Chir Gynaecol Fenn
+Ann Chir Gynaecol Fenn Suppl
+Ann Chir Gynaecol Suppl
+Ann Chir Infant
+Ann Chir Main
+Ann Chir Main Memb Super
+Ann Chir Plast
+Ann Chir Plast Esthet
+Ann Chir Thorac Cardiovasc
+Ann Cisalp Hist Soc
+Ann Clin Biochem
+Ann Clin Lab Sci
+Ann Clin Microbiol Antimicrob
+Ann Clin Psychiatry
+Ann Clin Res
+Ann Demogr Hist (Paris)
+Ann Dent
+Ann Dermatol Syphiligr (Paris)
+Ann Dermatol Venereol
+Ann Diagn Pathol
+Ann Droit Int Med
+Ann Dyslexia
+Ann Econ Soc Civilis
+Ann Econ Stat
+Ann Emerg Med
+Ann Endocrinol (Paris)
+Ann Entomol Soc Am
+Ann Epidemiol
+Ann Est
+Ann Eugen
+Ann Fac Econ Commer
+Ann Fac Lett Sci Hum
+Ann Fac Lett Sci Hum Nice
+Ann Fac Med Chir Univ Studi Perugia
+Ann Fam Med
+Ann Fond Luigi Einaudi
+Ann Fr Anesth Reanim
+Ann Gastroenterol Hepatol (Paris)
+Ann Gen Hosp Psychiatry
+Ann Gen Psychiatry
+Ann Genet
+Ann Genet Sel Anim
+Ann Georgr
+Ann Health Law
+Ann Hematol
+Ann Hepatol
+Ann Hist Comput
+Ann Hist Mantois
+Ann Hist Revolut Fr
+Ann Histochim
+Ann Homeopath Fr
+Ann Hum Biol
+Ann Hum Genet
+Ann Hyg
+Ann Hyg Med Colon
+Ann ICRP
+Ann IFORD
+Ann Ig
+Ann Ig (Roma)
+Ann Immunol
+Ann Immunol (Paris)
+Ann Immunol Hung
+Ann Insee
+Ann Inst Archeol Luxemb
+Ann Inst Pasteur (Paris)
+Ann Inst Pasteur Immunol
+Ann Inst Pasteur Lille
+Ann Inst Pasteur Microbiol
+Ann Inst Pasteur Virol
+Ann Intern Med
+Ann Iowa
+Ann Isnardi Auxol Norm Patol
+Ann Ist Carlo Forlanini
+Ann Ist Giangiacomo Feltrinelli
+Ann Ist Mus Stor Sci Fir
+Ann Ist Stor Italo Ger Trento
+Ann Ist Super Sanita
+Ann Ital Chir
+Ann Ital Dermatol Sifilogr
+Ann Ital Med Int
+Ann Ital Pediatr
+Ann Laringol Otol Rinol Faringol
+Ann Limnol
+Ann Med
+Ann Med Exp Biol Fenn
+Ann Med Hist
+Ann Med Intern Fenn
+Ann Med Interne (Paris)
+Ann Med Leg Criminol Police Sci Toxicol
+Ann Med Nancy
+Ann Med Nav (Roma)
+Ann Med Nav Trop
+Ann Med Phys (Lille)
+Ann Med Psychol (Paris)
+Ann Med Sect Pol Acad Sci
+Ann Med Sondalo
+Ann Med Univ Bialyst Pol
+Ann Microbiol
+Ann Microbiol (Paris)
+Ann Microbiol Enzimol
+Ann Midi
+Ann N Y Acad Sci
+Ann Natl Acad Med Sci
+Ann Natl Assoc Geogr
+Ann Nestle [Eng]
+Ann Nestle [Fr]
+Ann Nestle [Ger]
+Ann Neurol
+Ann Neuropsichiatr Psicoanal
+Ann Noninvasive Electrocardiol
+Ann Normandie
+Ann Nucl Med
+Ann Nutr Aliment
+Ann Nutr Metab
+Ann Occup Hyg
+Ann Ocul (Paris)
+Ann Odontostomatol (Lyon)
+Ann Oncol
+Ann Ophthalmol
+Ann Osp Maria Vittoria Torino
+Ann Ostet Ginecol
+Ann Ostet Ginecol Med Perinat
+Ann Otol Rhinol Laryngol
+Ann Otol Rhinol Laryngol Suppl
+Ann Otolaryngol
+Ann Otolaryngol Chir Cervicofac
+Ann Ottalmol Clin Ocul
+Ann Paediatr
+Ann Paediatr Fenn
+Ann Parasitol Hum Comp
+Ann Pathol
+Ann Pediatr (Paris)
+Ann Periodontol
+Ann Pharm Fr
+Ann Pharmacother
+Ann Phys (Paris)
+Ann Phys Med
+Ann Physiol Anthropol
+Ann Plast Surg
+Ann R Australas Coll Dent Surg
+Ann R Coll Physicians Surg Can
+Ann R Coll Surg Engl
+Ann Radiol (Paris)
+Ann Radiol Diagn (Bologna)
+Ann Readapt Med Phys
+Ann Rech Vet
+Ann Reg Sci
+Ann Rheum Dis
+Ann Sanita Pubblica
+Ann Saudi Med
+Ann Sc Norm Super Pisa
+Ann Sci
+Ann Sci Univ Besancon Med
+Ann Sclavo
+Ann Sclavo Collana Monogr
+Ann Serv Antiq Egypte
+Ann Soc Angeiol Histopathologie
+Ann Soc Belg Hist Hop
+Ann Soc Belg Med Trop
+Ann Soc Entomol Fr
+Ann Soc R Archeol Brux
+Ann Soc R Sci Med Nat Brux
+Ann Soc Sci Counc Niger
+Ann Stomatol (Roma)
+Ann Surg
+Ann Surg Oncol
+Ann Thorac Cardiovasc Surg
+Ann Thorac Surg
+Ann Transplant
+Ann Trop Med Parasitol
+Ann Trop Paediatr
+Ann Tuberc
+Ann Univ Abidjan Ser G
+Ann Univ Ankara
+Ann Univ Mariae Curie Sklodowska [Med]
+Ann Univ Sarav [Med]
+Ann Univ Sci Budap Rolando Eotvos Nom Sect Geogr
+Ann Univ Sci Budap Rolando Eotvos Nominatae [Biol]
+Ann Urol (Paris)
+Ann Vasc Surg
+Ann West Med Surg
+Ann Wyo
+Ann soc Emul Bruges
+Annal Droit
+Annee Biol
+Annee Endocrinol
+Annee Psychol
+Annee Soc
+Annee Sociol
+Annee Ther
+Annee Ther Clin Ophtalmol
+Annot Code Public Gen Laws Md Md
+Annot Laws Mass Mass
+Annot Zool Jpn
+Annotation
+Annu Afr Nord
+Annu Conf Res Med Educ
+Annu Mediaev
+Annu Meet Am Inst Oral Biol
+Annu Pays Ocean Indien
+Annu Proc Assoc Adv Automot Med
+Annu Prog Reprod Med
+Annu Rep Div Biol Med Res Argonne Natl Lab
+Annu Rep Med Chem
+Annu Rep R Soc Promot Health
+Annu Rep Res Inst Environ Med Nagoya Univ
+Annu Rev Anthropol
+Annu Rev Biochem
+Annu Rev Biomed Eng
+Annu Rev Biophys Bioeng
+Annu Rev Biophys Biomol Struct
+Annu Rev Biophys Biophys Chem
+Annu Rev Cell Biol
+Annu Rev Cell Dev Biol
+Annu Rev Earth Planet Sci
+Annu Rev Ecol Syst
+Annu Rev Entomol
+Annu Rev Fluid Mech
+Annu Rev Genet
+Annu Rev Genomics Hum Genet
+Annu Rev Gerontol Geriatr
+Annu Rev Immunol
+Annu Rev Med
+Annu Rev Microbiol
+Annu Rev Neurosci
+Annu Rev Nucl Sci
+Annu Rev Nurs Res
+Annu Rev Nutr
+Annu Rev Pharmacol
+Annu Rev Pharmacol Toxicol
+Annu Rev Phys Chem
+Annu Rev Physiol
+Annu Rev Phytopathol
+Annu Rev Plant Biol
+Annu Rev Plant Physiol
+Annu Rev Plant Physiol Plant Mol Biol
+Annu Rev Popul Law
+Annu Rev Psychol
+Annu Rev Public Health
+Annu Rev Rehabil
+Annu Rev Sex Res
+Annu Rev Sociol
+Annu Soc Christ Ethics
+Annu Stat Assist Previd Soc
+Annu Stat Suppl Soc Secur Bull
+Annu Surv Am Law
+Annu Symp Nurs Fac Pract
+Anot Pediatr
+Antaios
+Antarct J US
+Antarct Sci
+Anthropol Anz
+Anthropol Archeol Eurasia
+Anthropol Educ Q
+Anthropol Kozl
+Anthropol Pap Am Mus Nat Hist
+Anthropol Q
+Anthropol Soc
+Anthropol Today
+Anthropol UCLA
+Anthropol Work Rev
+Anthropologia
+Anthropologica
+Anthropotes
+Anthroquest
+Anthrozoos
+Antibiot Annu
+Antibiot Chemother
+Antibiot Khimioter
+Antibiot Med Biotekhnol
+Antibiotic Med Clin Ther
+Antibiotica
+Antibiotiki
+Anticancer Agents Med Chem
+Anticancer Drug Des
+Anticancer Drugs
+Anticancer Res
+Antichthon
+Anticipation
+Antiek
+Antike Welt
+Antimicrob Agents Chemother
+Antimicrobial Agents Chemother (Bethesda)
+Antioch Rev
+Antioquia Med
+Antioxid Redox Signal
+Antipode
+Antiq Class
+Antiqueweek
+Antiquity
+Antisense Nucleic Acid Drug Dev
+Antisense Res Dev
+Antiseptic
+Antivir Chem Chemother
+Antivir Ther
+Antiviral Res
+Antonie Van Leeuwenhoek
+Anu Bras Odontol
+Anu Estud Am
+Anu Estud Atl
+Anu Estud Centroam
+Anu Estud Mediev
+Anu Indig
+Anu Isnt Istor Arheol Xenopol
+Anxiety
+Anz Altertwiss
+Anz Philol Hist Kl Osterr Akad Wiss
+Anzhes J
+Apeiron
+Apex
+Aphasiology
+Apollonia (Malmo)
+Apollonia (Sydney)
+Apoptosis
+Apoth Kunst
+Appalach J
+Appetite
+Appl Anal
+Appl Anim Behav Sci
+Appl Behav Sci Rev
+Appl Biochem Biotechnol
+Appl Biochem Microbiol
+Appl Bioinformatics
+Appl Cardiol
+Appl Cardiopulm Pathophysiol
+Appl Catal
+Appl Cogn Psychol
+Appl Demor
+Appl Econ
+Appl Econ Discuss Pap Ser
+Appl Econ Lett
+Appl Eng Agric
+Appl Entomol Zool (Jpn)
+Appl Environ Microbiol
+Appl Ergon
+Appl Geogr
+Appl Geogr Dev
+Appl Health Econ Health Policy
+Appl Human Sci
+Appl Immunohistochem Mol Morphol
+Appl Magn Reson
+Appl Math Model
+Appl Microbiol
+Appl Microbiol Biotechnol
+Appl Micrograv Technol
+Appl Neurophysiol
+Appl Neuropsychol
+Appl Nurs Res
+Appl Occup Environ Hyg
+Appl Opt
+Appl Parasitol
+Appl Pathol
+Appl Philos
+Appl Phys B
+Appl Physiol Nutr Metab
+Appl Polym Symp
+Appl Prev Psychol
+Appl Psychol
+Appl Psychophysiol Biofeedback
+Appl Radiat Isot
+Appl Radiol
+Appl Res Ment Retard
+Appl Spectrosc
+Appl Stat
+Appl Theor Electrophor
+Appl Ther
+Appraisal J
+Apres Demain
+Aptechn Delo
+Aquaculture
+Aquat Toxicol
+Arab Aff
+Arab Econ
+Arab J Soc Sci
+Arab Stud Q
+Arabia
+Arabica
+Aramco World Mag
+Arb Gesch Med Giessen
+Arb Paul Ehrlich Inst Bundesamt Sera Impfstoffe Frankf A M
+Arb Paul Ehrlich Inst Georg Speyer Haus Ferdinand Blum Inst Frankf A M
+Arb Soz
+Arbeitsmed Sozialmed Praventivmed
+Arbeitsphysiologie
+Arbetarrorel Arsb
+Arbor
+Arc Cercle
+Arch AIDS Res
+Arch Anat Cytol Pathol
+Arch Anat Histol Embryol
+Arch Anat Microsc Morphol Exp
+Arch Anat Pathol (Paris)
+Arch Androl
+Arch Anim Nutr
+Arch Argent Dermatol
+Arch Argent Pediatr
+Arch Argent Tisiol
+Arch Argent Tisiol Neumonol
+Arch Belg
+Arch Belg Dermatol
+Arch Belg Dermatol Syphiligr
+Arch Belg Med Soc
+Arch Biochem
+Arch Biochem Biophys
+Arch Biol (Liege)
+Arch Biol Andina
+Arch Biol Med Exp (Santiago)
+Arch Bronconeumol
+Arch Cardiol Mex
+Arch Cas
+Arch Child Health
+Arch Chir Neerl
+Arch Chir Torac Cardiovasc
+Arch Clin Neuropsychol
+Arch Col Med El Salv
+Arch Cuba Cancerol
+Arch De Vecchi Anat Patol
+Arch Derm Syphilol
+Arch Dermatol
+Arch Dermatol Forsch
+Arch Dermatol Res
+Arch Dis Child
+Arch Dis Child Fetal Neonatal Ed
+Arch Domin Pediatr
+Arch Emerg Med
+Arch Enferm Coraz Vasos
+Arch Environ Contam Toxicol
+Arch Environ Health
+Arch Esp Morfol
+Arch Esp Urol
+Arch Eur Sociol
+Arch Exp Veterinarmed
+Arch Facial Plast Surg
+Arch Fam Med
+Arch Farmacol Sper Sci Affin
+Arch Farmacol Toxicol
+Arch Fisiol
+Arch Fr Mal App Dig
+Arch Fr Pediatr
+Arch Franciscanum Hist
+Arch Fund Roux Ocefa
+Arch Gen Psychiatry
+Arch Genet (Zur)
+Arch Gerontol Geriatr
+Arch Gerontol Geriatr Suppl
+Arch Gesamte Psychol
+Arch Gesamte Virusforsch
+Arch Gesch Buchwes
+Arch Gesch Naturwiss
+Arch Gesch Oberfranken
+Arch Geschwulstforsch
+Arch Gewerbepathol Gewerbehyg
+Arch Gynakol
+Arch Gynecol
+Arch Gynecol Obstet
+Arch Hisp
+Arch Hist Exact Sci
+Arch Hist Filoz Med
+Arch Hist Med (Warsz)
+Arch Hist Med Port
+Arch Histol Cytol
+Arch Histol Jpn
+Arch Histol Norm Patol
+Arch Hosp (Paris)
+Arch Hosp Univ
+Arch Hydrobiol Suppl Algol Stud
+Arch Hyg Bakteriol
+Arch Iatr Epistem
+Arch Iatr Hetaireon
+Arch Ibero Am
+Arch Immunol Ther Exp (Warsz)
+Arch Ind Hyg Occup Med
+Arch Insect Biochem Physiol
+Arch Inst Biol Andina
+Arch Inst Cardiol Mex
+Arch Inst Farmacol Exp (Madr)
+Arch Inst Pasteur Alger
+Arch Inst Pasteur Hell
+Arch Inst Pasteur Madagascar
+Arch Inst Pasteur Martinique
+Arch Inst Pasteur Tunis
+Arch Int Claude Bernard
+Arch Int Hist Idees
+Arch Int Hist Sci (Paris)
+Arch Int Neurol
+Arch Int Pharmacodyn Ther
+Arch Int Physiol
+Arch Int Physiol Biochim
+Arch Int Physiol Biochim Biophys
+Arch Interam Rheumatol
+Arch Intern Med
+Arch Internazionale Studi Neurol
+Arch Invest Med (Mex)
+Arch Iran Med
+Arch Ital Anat Embriol
+Arch Ital Anat Istol Patol
+Arch Ital Biol
+Arch Ital Chir
+Arch Ital Dermatol Sifilogr Venereol
+Arch Ital Dermatol Venereol Sessuol
+Arch Ital Laringol
+Arch Ital Mal Appar Dig
+Arch Ital Otol Rinol Laringol
+Arch Ital Otol Rinol Laringol Patol Cervicofacc
+Arch Ital Otol Rinol Laringol Patol Cervicofacc Suppl
+Arch Ital Otol Rinol Laringol Suppl
+Arch Ital Patol Clin Tumori
+Arch Ital Pediatr Pueric
+Arch Ital Sci Farmacol
+Arch Ital Sci Med Trop Parassitol
+Arch Ital Urol
+Arch Ital Urol Androl
+Arch Ital Urol Nefrol
+Arch Ital Urol Nefrol Androl
+Arch Julius Klaus Stift Vererbungsforsch Sozialanthropol Rassenhyg
+Arch Kinderheilkd
+Arch Kinderheilkd Suppl
+Arch Klin Exp Dermatol
+Arch Klin Exp Ohren Nasen Kehlkopfheilkd
+Arch Klin Med
+Arch Kreislaufforsch
+Arch Kriminol
+Arch Kulturgesch
+Arch Latinoam Nutr
+Arch Lebensmittelhyg
+Arch Mal Appar Dig Mal Nutr
+Arch Mal Coeur Vaiss
+Arch Mal Prof
+Arch Maragliano Patol Clin
+Arch Maragliano Patolog Clin Collana Monogr
+Arch Med Cuba
+Arch Med Exp
+Arch Med Gen Trop
+Arch Med Infant
+Arch Med Intern
+Arch Med Interna
+Arch Med Mutual
+Arch Med Ouest
+Arch Med Panamenos
+Arch Med Res
+Arch Med Sadowej Kryminol
+Arch Med Soc
+Arch Medicos Mex
+Arch Mediterr Med
+Arch Meteorol Geophys Bioklimatol [B]
+Arch Microbiol
+Arch Middx Hosp
+Arch Mikrobiol
+Arch Monaldi
+Arch Monaldi Mal Torace
+Arch Nat Hist
+Arch Neurobiol (Madr)
+Arch Neurol
+Arch Neurol Psychiatry
+Arch Odonto Estomatol
+Arch Oftalmol B Aires
+Arch Ohren Nasen Kehlkopfheilkd
+Arch Ophtalmol (Paris)
+Arch Ophtalmol Rev Gen Ophtalmol
+Arch Ophthal
+Arch Ophthalmol
+Arch Oral Biol
+Arch Orient
+Arch Orientforsch
+Arch Orthop Trauma Surg
+Arch Orthop Unfallchir
+Arch Ortop
+Arch Osp Mare
+Arch Ostet Ginecol
+Arch Otolaryngol
+Arch Otolaryngol Head Neck Surg
+Arch Otorhinolaryngol
+Arch Otorhinolaryngol Suppl
+Arch Ottamol
+Arch Papyrusforsch
+Arch Pathol
+Arch Pathol (Chic)
+Arch Pathol Lab Med
+Arch Patol Clin Med
+Arch Pediatr
+Arch Pediatr (Barc)
+Arch Pediatr Adolesc Med
+Arch Pediatr Urug
+Arch Pharm
+Arch Pharm (Weinheim)
+Arch Pharm Ber Dtsch Pharm Ges
+Arch Pharm Chem (Kbh)
+Arch Pharm Res
+Arch Philos
+Arch Phys Med
+Arch Phys Med Rehabil
+Arch Phys Ther
+Arch Phys Ther (Leipz)
+Arch Physiol Biochem
+Arch Protistenkd
+Arch Psicol Neurol Psichiatr
+Arch Psychiatr Nervenkr
+Arch Psychiatr Nervenkr Z Gesamte Neurol Psychiatr
+Arch Psychiatr Nurs
+Arch Psychol (Frankf)
+Arch Putti Chir Organi Mov
+Arch Radiol
+Arch Reformation Hist
+Arch Report
+Arch Roum Pathol Exp Microbiol
+Arch STD HIV Res
+Arch Sci
+Arch Sci Biol (Bologna)
+Arch Sci Compte Rendu Seances Soc
+Arch Sci Med (Torino)
+Arch Sci Physiol (Paris)
+Arch Sci Soc Relig
+Arch Sex Behav
+Arch Soc Cir Chile
+Arch Soc Esp Oftalmol
+Arch Soc Rom Stor Patria
+Arch Sozialgesch
+Arch Stomatol
+Arch Stomatol (Napoli)
+Arch Stor Ital
+Arch Stor Lomb
+Arch Stor Prov Napol
+Arch Stor Sicil
+Arch Suicide Res
+Arch Surg
+Arch Tierernahr
+Arch Tisiol
+Arch Tisiol Mal Appar Respir
+Arch Toxicol
+Arch Toxicol Suppl
+Arch Toxikol
+Arch Urug Med Cir Espec
+Arch Veneto
+Arch Venez Med Trop Parasitol Med
+Arch Venez Pueric Pediatr
+Arch Vet Ital
+Arch Vet Pol
+Arch Virol
+Arch Virol Suppl
+Arch Womens Ment Health
+Arch Z
+Archaea
+Archaeol Aeliana
+Archaeol Anz
+Archaeol Cantiana
+Archaeology
+Archeologia
+Archief Meded k Zeeuwsch Genoot Wet
+Archifacts
+Archiginnasio
+Archit Aujourdhui
+Archit Rec
+Architecture
+Archivar
+Archivaria
+Archivio Chir Torace
+Archivist
+Archivmitteilungen
+Archivos Soc Oftalmol Hisp Am
+Arcisp S Anna Ferrara
+Arctic Anthropol
+Arctic Med Res
+Arerugi
+Arethusa
+Argent Repub Laws Statut
+Argos
+Argument
+Arh Farm (Belgr)
+Arh Hig Rada
+Arh Hig Rada Toksikol
+Arh Zast
+Ariel (Jerus)
+Ariz Dent J
+Ariz J Int Comp Law
+Ariz Law Rev
+Ariz Med
+Ariz Nurse
+Ariz Revis Statut Annot Ariz
+Ariz State Law J
+Ariz West
+Ark Archeol
+Ark Dent
+Ark Dent J
+Ark Hist Q
+Ark Law Rev
+Ark Light Newsl
+Ark Statut 1947 Ark
+Arkh Anat Gistol Embriol
+Arkh Patol
+Armed Forces Med J India
+Armed Forces Soc
+Armen Rev
+Army Lawyer
+Army Q Def J
+Arq Bras Cardiol
+Arq Bras Endocrinol Metabol
+Arq Bras Med
+Arq Bras Med Nav
+Arq Bras Oftalmol
+Arq Bras Psicol
+Arq Cent Cult Port
+Arq Cent Estud Curso Odontol
+Arq Cent Estud Fac Odontol UFMG (Belo Horiz)
+Arq Cir Clin Exp
+Arq Clin (Rio De J)
+Arq Fac Hig Saude Publica Univ Sao Paulo
+Arq Gastroenterol
+Arq Hig Saude Publica
+Arq Inst Biol (Sao Paulo)
+Arq Inst Bras Invest Tubers
+Arq Inst Farm
+Arq Med Cirur Pernamb
+Arq Min Leprol
+Arq Neuropsiquiatr
+Arq Oncol
+Arq Patol
+Arq Pediatr
+Arq Port Bioquim
+Arq Port Oftalmol
+Arquivo
+Arrows Change
+Ars Curandi Odontol
+Arsb Goteb Tandlak Sallsk
+Arsb Odontol Samf Finl
+Arsberet Kbh Univ Med Hist Inst Mus
+Arsberet Kobenhavns Univ Med Hist Mus
+Art Am
+Art Bull
+Art Hist
+Art Med
+Arteres Veines
+Arterioscler Thromb
+Arterioscler Thromb Vasc Biol
+Arteriosclerosis
+Artery
+Artes Mex
+Artha Vijnana
+Arthritis Care Res
+Arthritis Res
+Arthritis Res Ther
+Arthritis Rheum
+Arthritis Rheum (Munch)
+Arthroscopy
+Articulator (Columb)
+Articulator (Syd)
+Artif Cells Blood Substit Immobil Biotechnol
+Artif Intell Med
+Artif Life
+Artif Limbs
+Artif Organs
+Arukoru Kenkyuto Yakubutsu Ison
+Arv
+Arzneimittelforschung
+Arztebl Baden Wurtt
+Arztebl Rheinl Pfalz
+Arztl Dienst
+Arztl Forsch
+Arztl Fortbild
+Arztl Jugendkd
+Arztl Kosmetol
+Arztl Prax
+Arztl Sammelbl
+Arztl Wochensch
+Asbury Theol J
+Ascania
+Asclepio
+Asepsis
+Asia
+Asia Mag
+Asia Major
+Asia Oceania J Obstet Gynaecol
+Asia Pac Econ Lit
+Asia Pac J Clin Nutr
+Asia Pac J Public Health
+Asia Pac J Rural Dev
+Asia Pac J Soc Work
+Asia Pac Obs
+Asia Pac POPIN Bull
+Asia Pac Pop Policy
+Asia Pac Popul J
+Asia Pac Popul Res Abstr
+Asia Pac Viewp
+Asia Q
+Asia-Pac POPIN Newsl
+Asian Afr Stud
+Asian Am Pac Isl J Health
+Asian Cardiovasc Thorac Ann
+Asian Dev Rev
+Asian Econ
+Asian Econ Rev
+Asian Folkl Stud
+Asian Forum Newsl
+Asian Geogr
+Asian J Aesthet Dent
+Asian J Androl
+Asian J Commun
+Asian J Control
+Asian J Econ
+Asian J Econ Soc Stud
+Asian J Infect Dis
+Asian J Surg
+Asian Med J
+Asian Migr
+Asian Pac Cens Forum
+Asian Pac Census Newsl
+Asian Pac J Allergy Immunol
+Asian Pac J Cancer Prev
+Asian Pac Migr J
+Asian Pac Popul Forum
+Asian Pac Popul Programme News
+Asian Perspect
+Asian Popul Programme News
+Asian Popul Stud Ser
+Asian Profile
+Asian Stud
+Asian Surv
+Asian Thought Soc
+Asiat Forsch
+Asiaweek
+Asien Afr Lateinam
+Ask Sihhiye Derg
+Asklepii
+Aslib Proc
+Aspen Emphysema Conf
+Aspens Advis Nurse Exec
+Assay Drug Dev Technol
+Assertive Nurse
+Assessment
+Assia Jew Med Ethics
+Assignment Child
+Assist Inferm Ric
+Assist Reprod Rev
+Assist Technol
+Assoc Manage
+Assoc Soc Manager
+Asthet Med (Berl)
+Astragale
+Astrameddelande
+Astrobiology
+Astron Astrophys
+Astron Astrophys Suppl Ser
+Astron J
+Astronomy
+Astrophys J
+Astrophys J Suppl Ser
+Astrophys Lett Commun
+Astrophys Space Sci
+At Energy Law J
+At Energy Rev
+Aten Primaria
+Atene Roma
+Atenea
+Ateneo Parmense Acta Biomed
+Ateneo Parmense Acta Nat
+Ateneo Parmense [1]
+Ateneo Veneto
+Athena
+Atheroscler Suppl
+Atherosclerosis
+Atl Econ J
+Atl Mon
+Atl Report
+Atlanta
+Atlanta Hist
+Atlanta Hist J.
+Atlanta Med
+Atlanta Wkly
+Atlantic
+Atlantida
+Atlantis (Montr)
+Atlantis (Wolfv)
+Atlas Oral Maxillofac Surg Clin North Am
+Atlas Radiol Clin Presse Med
+Atlas World Press Rev
+Atmos Environ
+Atoll Res Bull
+Atompraxis
+Attach Hum Dev
+Atti Accad Fisiocrit Siena
+Atti Accad Fisiocrit Siena [Med Fis]
+Atti Accad Med Lomb
+Atti Accad Naz Lincei
+Atti Accad Sci Ist Bologna Classe Sci Fis Rend
+Atti Clin Otorinolaringoitr Univ Palermo
+Atti Congr Naz Soc Ital Med Leg
+Atti Mem Accad Ital Stor Farm
+Atti Mem Accad Naz Sci Lett Arti Modena
+Atti Mem Accad Stor Arte Sanit
+Atti Mem Accad Toscana Sci Lett Colombaria
+Atti Mem Deput Stor Patria Antiche Prov Modenesi
+Atti Mem R Accad Petrarca Lett Arti Sci
+Atti Soc Ital Cardiol
+Attual Dent
+Attual Diet
+Attual Ematol
+Attual Ostet Ginecol
+Auckl Univ Law Rev
+Audiol Neurootol
+Audiology
+Audit Unit News
+Audubon
+Auris Nasus Larynx
+Ausgrab Funde
+Aussenwirtschaft
+Aust Agder Arv
+Aust Bull Labour
+Aust Clin Rev
+Aust Coll Midwives Inc J
+Aust Crit Care
+Aust Cult Hist
+Aust Dent J
+Aust Dent Pract
+Aust Econ Hist Rev
+Aust Econ Pap
+Aust Econ Rev
+Aust Endod J
+Aust Fam Physician
+Aust Geogr
+Aust Geogr Stud
+Aust Health Rev
+Aust Hist Stud
+Aust Hosp
+Aust Intellect Prop J
+Aust J Adv Nurs
+Aust J Ageing
+Aust J Agric Res
+Aust J Agric Resour Econ
+Aust J Anthropol
+Aust J Biol Sci
+Aust J Biotechnol
+Aust J Chem
+Aust J Chin Aff
+Aust J Clin Hypnother
+Aust J Dermatol
+Aust J Early Child
+Aust J Exp Biol Med Sci
+Aust J Fam Law
+Aust J Forensic Sci
+Aust J Fr Stud
+Aust J Geod Photogramm Surv
+Aust J Holist Nurs
+Aust J Marriage Fam
+Aust J Ment Retard
+Aust J Midwifery
+Aust J Ophthalmol
+Aust J Optom
+Aust J Physiother
+Aust J Plant Physiol
+Aust J Polit Hist
+Aust J Polit Sci
+Aust J Prof Appl Ethics
+Aust J Psychol
+Aust J Public Health
+Aust J Rural Health
+Aust J Sci Med Sport
+Aust J Sci Res (B)
+Aust J Sex Marriage And Fam
+Aust J Soc Issues
+Aust J Stat
+Aust J Zool
+Aust Law J
+Aust Libr J
+Aust Med Rec J
+Aust N Z Gen Pract
+Aust N Z J Med
+Aust N Z J Ment Health Nurs
+Aust N Z J Obstet Gynaecol
+Aust N Z J Obstet Gynaecol Suppl
+Aust N Z J Ophthalmol
+Aust N Z J Psychiatry
+Aust N Z J Public Health
+Aust N Z J Sociol
+Aust N Z J Surg
+Aust New Zealand Health Policy
+Aust Nurs J
+Aust Nurses J
+Aust Orthod J
+Aust Outlook
+Aust Paediatr J
+Aust Prosthodont J
+Aust Prosthodont Soc Bull
+Aust Psychol
+Aust Q
+Aust Soc
+Aust Soc Prosthodontists Bull
+Aust Vet J
+Australas Ann Med
+Australas Biotechnol
+Australas Cathol Rec
+Australas J Ageing
+Australas J Dermatol
+Australas J Philos
+Australas Nurses J
+Australas Phys Eng Sci Med
+Australas Psychiatry
+Australas Radiol
+Austriaca
+Autism
+Autoimmun Rev
+Autoimmunity
+Auton Agent Multi Agent Syst
+Auton Autacoid Pharmacol
+Auton Neurosci
+Auton Robots
+Auxiliaire
+Av Commun Rev
+Av Odontoestomatol
+Av Periodoncia
+Av Piscol Clin Latinonot
+Avenir Med
+Aviakosm Ekolog Med
+Avian Dis
+Avian Pathol
+Aviat Space Environ Med
+Aviat Week Space Technol
+Axone
+Azerbaidzhanskii Meditsinskii Zhurnal
+Aziia Afr Segodnia
+Aztlan
+B C Med J
+B C Stud
+B-ENT
+BDD
+BERC Bull
+BETA
+BIRPERHT Publ
+BJOG
+BJR Suppl
+BJU Int
+BL Gesz
+BL Heimatkd
+BL Wueritemb Kirchengesch
+BMC Anesthesiol
+BMC Biochem
+BMC Bioinformatics
+BMC Biol
+BMC Biotechnol
+BMC Blood Disord
+BMC Cancer
+BMC Cardiovasc Disord
+BMC Cell Biol
+BMC Chem Biol
+BMC Clin Pathol
+BMC Clin Pharmacol
+BMC Complement Altern Med
+BMC Dermatol
+BMC Dev Biol
+BMC Ear Nose Throat Disord
+BMC Ecol
+BMC Emerg Med
+BMC Endocr Disord
+BMC Evol Biol
+BMC Fam Pract
+BMC Gastroenterol
+BMC Genet
+BMC Genomics
+BMC Geriatr
+BMC Health Serv Res
+BMC Immunol
+BMC Infect Dis
+BMC Int Health Hum Rights
+BMC Med
+BMC Med Educ
+BMC Med Ethics
+BMC Med Genet
+BMC Med Imaging
+BMC Med Inform Decis Mak
+BMC Med Res Methodol
+BMC Microbiol
+BMC Mol Biol
+BMC Musculoskelet Disord
+BMC Nephrol
+BMC Neurol
+BMC Neurosci
+BMC Nucl Med
+BMC Nurs
+BMC Ophthalmol
+BMC Oral Health
+BMC Palliat Care
+BMC Pediatr
+BMC Pharmacol
+BMC Physiol
+BMC Plant Biol
+BMC Pregnancy Childbirth
+BMC Psychiatry
+BMC Public Health
+BMC Pulm Med
+BMC Struct Biol
+BMC Surg
+BMC Urol
+BMC Vet Res
+BMC Womens Health
+BME
+BMJ
+BMQ
+BNI Q
+BNWL Rep
+BSCS Pam
+BTTA Rev
+BZB Bayer Zahnarztebl
+Backgr Notes Ser
+Backgrounder
+Bacteriol Rev
+Bacteriol Virusol Parazitol Epidemiol
+Bacteriol Virusol Parazitol Epidemiol (Bucur)
+Baillieres Best Pract Res Clin Endocrinol Metab
+Baillieres Best Pract Res Clin Gastroenterol
+Baillieres Best Pract Res Clin Haematol
+Baillieres Best Pract Res Clin Obstet Gynaecol
+Baillieres Best Pract Res Clin Rheumatol
+Baillieres Clin Anaesthesiol
+Baillieres Clin Endocrinol Metab
+Baillieres Clin Gastroenterol
+Baillieres Clin Haematol
+Baillieres Clin Immunol Allergy
+Baillieres Clin Neurol
+Baillieres Clin Obstet Gynaecol
+Baillieres Clin Oncol
+Baillieres Clin Rheumatol
+Baker Cederberg Noteb
+Balance
+Balcanica
+Baldwins Ky Revis Statut Annot Ky
+Baltim Mag
+Bangkok Post
+Banglad J Microbiol
+Bangladesh Dev Stud
+Bangladesh J Sci Res
+Bangladesh Med J
+Bangladesh Med Res Counc Bull
+Banque
+Baptist Hist Herit
+Baptist Q
+Barbados Nurs J
+Barc Quir
+Barrister
+Barrons
+Bartonia
+Basal Facts
+Basic Appl Histochem
+Basic Appl Myol
+Basic Appl Soc Psych
+Basic Clin Pharmacol Toxicol
+Basic Life Sci
+Basic Res Cardiol
+Basl Z Gesch Altertumskd
+Bassini
+Baxter Health Policy Rev
+Bayl Law Rev
+Baylor Dent J
+Baylor Nurs Educ
+Beaver
+Bedside Nurse
+Begg J Orthod Theory Treat
+Beginnings
+Behav Anal
+Behav Assess
+Behav Biol
+Behav Brain Funct
+Behav Brain Res
+Behav Brain Sci
+Behav Change
+Behav Cogn Neurosci Rev
+Behav Ecol
+Behav Ecol Sociobiol
+Behav Eng
+Behav Genet
+Behav Healthc
+Behav Healthc Tomorrow
+Behav Inf Technol
+Behav Med
+Behav Med Update
+Behav Modif
+Behav Neural Biol
+Behav Neurol
+Behav Neuropsychiatry
+Behav Neurosci
+Behav Pharmacol
+Behav Processes
+Behav Res Methods
+Behav Res Methods Instrum Comput
+Behav Res Ther
+Behav Sci
+Behav Sci Law
+Behav Sci Res
+Behav Sleep Med
+Behav Soc Action J
+Behav Soc Sci Librar
+Behav Ther
+Behaviorism
+Behaviormetrika
+Behaviour
+Behring Inst Mitt
+Beijing Da Xue Xue Bao
+Beilstein J Org Chem
+Beitr Dtsch Philol
+Beitr Gerichtl Med
+Beitr Gesch Arb
+Beitr Gesch Med Nebengeb
+Beitr Gesch Pharm Ihrer Nachbargeb
+Beitr Hochsch Wissenschaftsgesch Erfurts
+Beitr Hyg Epidemiol
+Beitr Infusionsther
+Beitr Infusionsther Transfusionsmed
+Beitr Infusionther Klin Ernahr
+Beitr Klin Chir
+Beitr Klin Erforsch Tuberk Lungenkr
+Beitr Klin Neurol Psychiatr
+Beitr Klin Tuberk Spezif Tuberkuloseforsch
+Beitr Krebsforsch
+Beitr Neurochir
+Beitr Orthop Traumatol
+Beitr Pathol
+Beitr Pathol Anat
+Beitr Rheumatol
+Beitr Sexualforsch
+Beitr Silikoseforsch
+Beitr Silikoseforsch Pneumokoniose
+Beitr Trop Landwirtsch Veterinarmed
+Beitr Wurttemb Apothekengesch
+Belcast J Belizean Aff
+Beleid Maatsch
+Belfagor
+Belg Tijdschr Geneesk
+Belg Tijdschr Nieuwste Geschied
+Belizean Stud
+Belleten
+Benders Health Care Law Mon
+Benefits Q
+Benelux
+Bengal Past Present
+Ber Bonn Univ Poliklin Mund Zahn Kieferkr
+Ber Dtsch Bot Ges
+Ber Dtsch Landeskd
+Ber Naturforsch Ges Freibg Br
+Ber Phys Med Ges Wurzbg
+Ber Raumforsch Raumplan
+Ber Wissenschaftsgesch
+Ber Zusammenkunft Dtsch Ophthalmol Ges
+Berita Jururawat
+Berkala Ilmu Kedokteran
+Berkeley J Sociol
+Berkeley Munic Code 1976 Berkeley Calif
+Berkeley Technol Law J
+Berkeley Womens Law J
+Berl Geogr Stud
+Berl Hist Stud
+Berl J Soziol
+Berl Med
+Berl Munch Tierarztl Wochenschr
+Berl Stat
+Berl Stat Mon Schr
+Berl Tierarztl Wochenschr
+Berufsdermatosen
+Berufsdermatosen (Monogr)
+Berytus
+Best Pract Benchmarking Healthc
+Best Pract Res Clin Anaesthesiol
+Best Pract Res Clin Endocrinol Metab
+Best Pract Res Clin Gastroenterol
+Best Pract Res Clin Haematol
+Best Pract Res Clin Obstet Gynaecol
+Best Pract Res Clin Rheumatol
+Bests Rev Life Health Insur Ed
+Better Homes Gard
+Between Species
+Bevolking Gezin
+Beyond Relief
+Bib Mitt
+Bibl Anat
+Bibl Cardiol
+Bibl Ec Chartes
+Bibl Gastroenterol
+Bibl Gynaecol
+Bibl Haematol
+Bibl Hig Instituta NR Srb
+Bibl Humanisme Renaiss
+Bibl Laeger
+Bibl Microbiol
+Bibl Nutr Dieta
+Bibl Ophthalmol
+Bibl Paediatr
+Bibl Psychiatr
+Bibl Psychiatr Neurol
+Bibl Radiol
+Bibl Stor Sci
+Bibl Tuberc
+Bibl Wiss
+Bibliofilia
+Bibliogr Int Demogr Hist
+Bibliogr Ital Stor Sci
+Bibliographr Reprod
+Bibliotheck
+Biblos
+Bien Naitre
+Bijdr Geschied
+Bijdr Meded
+Bijdr Meded Geschied Ned
+Bijdragen
+Biken J
+Bild Wiss
+Bildgebung
+Bilt Hematol Transfuz
+Bilt Mednar Fed Zob Teh
+Bilt Udruz Ortodonata Jugosl
+Binocul Vis Strabismus Q
+BioDrugs
+Biocell
+Biochem Arch
+Biochem Biophys Res Commun
+Biochem Cell Biol
+Biochem Clin
+Biochem Exp Biol
+Biochem Genet
+Biochem Int
+Biochem J
+Biochem Med
+Biochem Med Metab Biol
+Biochem Mol Biol Int
+Biochem Mol Med
+Biochem Pharmacol
+Biochem Physiol Pflanz
+Biochem Soc Symp
+Biochem Soc Trans
+Biochem Syst Ecol
+Biochem Z
+Biochemistry
+Biochemistry (Mosc)
+Biochim Biophys Acta
+Biochim Clin
+Biochimie
+Bioconjug Chem
+Biocontrol Sci
+Biocycle
+Biodegradation
+Biodynamica
+Bioelectrochem Bioenerg
+Bioelectrochemistry
+Bioelectromagnetics
+Bioessays
+Bioeth Bull
+Bioeth Exam
+Bioeth News
+Bioeth Res Notes
+Bioethics
+Bioethics Dig
+Bioethics Forum
+Bioethics Northwest
+Bioethics Q
+Biofactors
+Biofeedback Self Regul
+Biofilms
+Biofizika
+Biofouling
+Biog Amines
+Biogerontology
+Biogr Mem Fellows R Soc
+Biogr Mem Natl Acad Sci
+Biography
+Bioinformatics
+Bioinorg Chem
+Biokhimiia
+Biol Blood Marrow Transplant
+Biol Bull
+Biol Bull Acad Sci USSR
+Biol Cell
+Biol Chem
+Biol Chem Hoppe Seyler
+Biol Conserv
+Biol Cybern
+Biol Direct
+Biol Gastroenterol (Paris)
+Biol Hum Aff
+Biol J Linn Soc Lond
+Biol Lat
+Biol Listy
+Biol Mass Spectrom
+Biol Med (Paris)
+Biol Mem
+Biol Membr
+Biol Met
+Biol Neonat
+Biol Neonate
+Biol Pharm Bull
+Biol Plant
+Biol Proced Online
+Biol Psychiatry
+Biol Psychol
+Biol Psychol Bull
+Biol Reprod
+Biol Reprod Suppl
+Biol Res
+Biol Res Nurs
+Biol Res Pregnancy Perinatol
+Biol Rev Camb Philos Soc
+Biol Rhythm Res
+Biol Sci Space
+Biol Signals
+Biol Signals Recept
+Biol Soc
+Biol Sport
+Biol Struct Morphog
+Biol Trace Elem Res
+Biol Zent Bl
+Biologia
+Biologia (Bratisl)
+Biologica (Santiago)
+Biologicals
+Biologist
+Biologist (London)
+Biom Hum
+Biom J
+Biom Z
+Biomacromolecules
+Biomagn Res Technol
+Biomarkers
+Biomater Artif Cells Artif Organs
+Biomater Artif Cells Immobilization Biotechnol
+Biomater Med Devices Artif Organs
+Biomaterials
+Biomech Model Mechanobiol
+Biomed Biochim Acta
+Biomed Bull
+Biomed Chromatogr
+Biomed Commun
+Biomed Digit Libr
+Biomed Eng
+Biomed Eng (NY)
+Biomed Eng Online
+Biomed Environ Mass Spectrom
+Biomed Environ Sci
+Biomed Ethics
+Biomed Instrum
+Biomed Instrum Technol
+Biomed Khim
+Biomed Lib Bull
+Biomed Mass Spectrom
+Biomed Mater Eng
+Biomed Microdevices
+Biomed News
+Biomed Pap Med Fac Univ Palacky Olomouc Czech Repub
+Biomed Pept Proteins Nucleic Acids
+Biomed Pharmacother
+Biomed Purv
+Biomed Res
+Biomed Sci
+Biomed Sci Instrum
+Biomed Sci Technol
+Biomed Tech (Berl)
+Biomedica
+Biomedicine
+Biomembranes
+Biometals
+Biometrics
+Biometrika
+Biomimetics
+Biomol Eng
+Bioorg Chem
+Bioorg Khim
+Bioorg Med Chem
+Bioorg Med Chem Lett
+Biopharm Drug Dispos
+Biophys Chem
+Biophys J
+Biophys Struct Mech
+Biophysik
+Biopolim Kletka
+Biopolymers
+Bioprocess Biosyst Eng
+Bioprocess Technol
+Bioresour Technol
+Biorheology
+Biorheology Suppl
+Bios
+Biosci Biotechnol Biochem
+Biosci Rep
+Bioscience
+Biosecur Bioterror
+Biosens Bioelectron
+Biosensors
+Bioseparation
+Biospectroscopy
+Biostatistics
+Biosystems
+Biotech Histochem
+Biotechniques
+Biotechnol Adv
+Biotechnol Annu Rev
+Biotechnol Appl Biochem
+Biotechnol Bioeng
+Biotechnol Bioeng Symp
+Biotechnol Genet Eng Rev
+Biotechnol Lett
+Biotechnol Prog
+Biotechnol Ther
+Biotechnology
+Biotechnology (N Y)
+Biotelem Patient Monit
+Biotelemetry
+Biotherapy
+Biotronics
+Biotypologie
+Bipolar Disord
+Birth
+Birth Defects Orig Artic Ser
+Birth Defects Res A Clin Mol Teratol
+Birth Defects Res B Dev Reprod Toxicol
+Birth Defects Res C Embryo Today
+Birth Fam J
+Birth Gaz
+Birth Psychol Bull
+Birthright
+Biul Bibl Jagiellon
+Biul IGS
+Biul Inst Med Morsk Gdansk
+Biul Panstw Inst Med Morsk Trop J W Gdansku
+Biul Zydowskiego Inst Hist Pol
+Biull Eksp Biol Med
+Biull Mosk Ova Ispyt Prir (Biol)
+Biull Vsesoiuznogo Kardiol Nauchn Tsentra AMN SSSR
+Biulleten Russ SFSR Minist Zdravookhraneniia Uchenyi Meditsinskii sov
+Bl Dtsch Int Polit
+Bl Zahnheilkd
+Black Aging
+Black Law J
+Black Scholar
+Blake Stud
+Blood
+Blood Cells
+Blood Cells Mol Dis
+Blood Coagul Fibrinolysis
+Blood Press
+Blood Press Monit
+Blood Press Suppl
+Blood Purif
+Blood Rev
+Blood Vessels
+Bloomsbury Geogr
+Blue Cross Assoc Res Ser
+Blue Sheet
+Blut
+Blutalkohol
+Boardr Rep
+Bodleian Libr Rec
+Body Forum
+Body Posit
+Boei Eisei
+Boei Ika Daigakko Zasshi
+Bogens Verden
+Bogeon sahoe nonjib
+Bohemia Jahrb Coll Carolinum
+Bol Acad Nac Hist (Argent)
+Bol Acad Nac Hist (Caracas)
+Bol Acad Nac Med
+Bol Acad Peru Cir
+Bol Agrup Odontol Zona Cent Cap Fed B Aires
+Bol Anal Demogr
+Bol Antropol
+Bol Antropol Am
+Bol Asoc Argent Odontol Ninos
+Bol Asoc Chil Prot Fam
+Bol Asoc Demogr Hist
+Bol Asoc Med P R
+Bol Asoc Medica Nac Repub Panama
+Bol Asoc Venez Enferm Prof
+Bol B Aires Univ Nac Inst Clin Quir
+Bol Bibl Menendez Pelayo
+Bol Bibliogr Secr Hacienda Credito Publico
+Bol Cent Biol Reprod
+Bol Cent Estud Hosp Servidores Estado
+Bol Cent Estud Siglo XVIII
+Bol Chil Parasitol
+Bol Circ Argent Odontol
+Bol Clin Hosp Civis Lisb
+Bol Col Prof Enferm P R
+Bol Cons Nac Poblac
+Bol Cult Bibliogr
+Bol Cult Inf Cons Gen Col Med Esp
+Bol Demogr
+Bol Dent Oper
+Bol Dent Urug
+Bol Dir Gen Odontol (Santa Fe)
+Bol Div Nac Dermatol Sanit
+Bol Ed Col Mex
+Bol Epidemiol AIDS
+Bol Epidemiol Chile
+Bol Equipe Odontol Sanit
+Bol Estud Latinoam Caribe
+Bol Estud Med Biol
+Bol Fac Farm Odontol Ribeirao Preto
+Bol Fac Odontol Piracicaba
+Bol Geogr
+Bol Geogr Teor
+Bol Hist Antig
+Bol Hosp Mil Cuba Ejercito Hosp Mil Dr Carlos J Finlay
+Bol Hosp Oftalmol
+Bol Hosp San Juan Dios
+Bol Indic Coyunt
+Bol Inf Cent Doc Pesqui
+Bol Inf Col Odontol Estomatol (Barc)
+Bol Inf Dent (Madr)
+Bol Inf Parasit Chil
+Bol Inf Socio Econ
+Bol Inst Angola
+Bol Inst Estud Asturianos
+Bol Inst Estud Med Biol Univ Nac Auton Mex
+Bol Inst Hist Argent Am Dr Emilio Ravignani
+Bol Inst Interam Nino
+Bol Inst Invest Bibliogr (Mexico)
+Bol Inst Pueric
+Bol Inst Pueric Martagao Gesteira
+Bol Inst Sancho Sabio
+Bol Liga Contra Cancer Havana
+Bol Mat Dent
+Bol Med Hosp Infant Mex
+Bol Mens Estad DANE
+Bol Mex Derecho Comp
+Bol Odontol (B Aires)
+Bol Odontol (Bogota)
+Bol Odontol Mex
+Bol Of Estado Gac Madr Spain
+Bol Of Sanit Panam (Engl)
+Bol Oficina Sanit Panam
+Bol PROLAP
+Bol Poblac
+Bol Popul Emprego Renda Nordeste
+Bol Protes
+Bol Psicol
+Bol R Soc Esp Hist Nat Secc Biol
+Bol SIDEMA
+Bol Sanat Sao Lucas
+Bol Sanid Mil
+Bol Serv Odontol Sanit (Porto Alegre)
+Bol Soc Castell Cult
+Bol Soc Catalana Pediatr
+Bol Soc Chil Obstet Ginecol
+Bol Soc Cir Rosario
+Bol Soc Cir Urug
+Bol Soc Cubana Dermatol Sifilogr
+Bol Soc Dent Guatem
+Bol Soc Esp Hidrol Med
+Bol Soc Esp Hist Farm
+Bol Soc Esp Hist Med
+Bol Soc Estomatol Argent
+Bol Soc Geogr Lisb
+Bol Soc Mex Hist Filos Med
+Bol Soc Quim Peru
+Bol Soc Valencia Pediatr
+Bol Socioecon
+Bol Tr Soc Argent Cir
+Bol Trab Soc Cir B Aires
+Bol Univ Granada
+Bold
+Boletin
+Boll Cent Int Beltrame Stor Spazio Tempo
+Boll Chim Farm
+Boll Demogr Stor
+Boll Inf Consoc Naz (Rome)
+Boll Ist Sieroter Milan
+Boll Mal Orecch Gola Naso
+Boll Mem Soc Piemont Chir
+Boll Mem Soc Tosco Umbra Chir
+Boll Metallogr
+Boll Ocul
+Boll Osp Varese
+Boll Soc Ital Biol Sper
+Boll Soc Ital Cardiol
+Boll Soc Med Chir Bresciana
+Boll Soc Med Chir Cremona
+Boll Soc Med Chir Pisa
+Boll Stor Bibliogr Subalp
+Bologna Incontri
+Bologna Medica
+Bombay Hosp J
+Bone
+Bone Marrow Transplant
+Bone Miner
+Bonn Geschichtsbl
+Book Collect
+Book Suppl J Child Psychol Psychiatr
+Bookman
+Bord Chir
+Bord Med
+Bordens Rev Nutr Res
+Borgyogy Venerol Sz
+Borsuye
+Bosn J Basic Med Sci
+Boston
+Boston Bar J
+Boston Coll Environ Aff Law Rev
+Boston Coll Ind Commer Law Rev
+Boston Coll Law Rev
+Boston Coll Third World Law J
+Boston College Int Comp Law Rev
+Boston Globe
+Boston Univ Int Law J
+Boston Univ J
+Boston Univ Law Rev
+Boston Univ Public Interest Law J
+Bostonia
+Bot Acta
+Bot Bull Acad Sinica (Taiwan)
+Bot Gaz
+Bot J Linn Soc
+Bot Mus Lealf Harv Univ
+Bot Rev
+Botsw Natl Health Bull
+Botsw Notes Rec
+Br Book News
+Br Dent J
+Br Dent Nurs J
+Br Dent Surg Assist
+Br Heart J
+Br Herit
+Br Hist Illus
+Br Homeopath J
+Br J 18th Cent Stud
+Br J Addict
+Br J Addict Alcohol Other Drugs
+Br J Anaesth
+Br J Audiol
+Br J Audiol Suppl
+Br J Biomed Sci
+Br J Cancer
+Br J Cancer Suppl
+Br J Clin Pharmacol
+Br J Clin Pract
+Br J Clin Pract Suppl
+Br J Clin Psychol
+Br J Community Nurs
+Br J Criminol
+Br J Dermatol
+Br J Dis Chest
+Br J Disord Commun
+Br J Educ Psychol
+Br J Educ Technol
+Br J Exp Pathol
+Br J Fam Plann
+Br J Gen Pract
+Br J Guid Counc
+Br J Haematol
+Br J Health Psychol
+Br J Hist Sci
+Br J Hosp Med
+Br J Hosp Med (Lond)
+Br J Ind Med
+Br J Int Stud
+Br J Law Soc
+Br J Math Stat Psychol
+Br J Med Educ
+Br J Med Hypn
+Br J Med Psychol
+Br J Neurosurg
+Br J Nurs
+Br J Nutr
+Br J Obstet Gynaecol
+Br J Ophthalmol
+Br J Oral Maxillofac Surg
+Br J Oral Surg
+Br J Orthod
+Br J Perioper Nurs
+Br J Pharmacol
+Br J Pharmacol Chemother
+Br J Philos Sci
+Br J Phys Med
+Br J Physiol Opt
+Br J Plast Surg
+Br J Polit Sci
+Br J Prev Soc Med
+Br J Psychiatry
+Br J Psychiatry Suppl
+Br J Psychol
+Br J Radiol
+Br J Radiol Suppl
+Br J Rheumatol
+Br J Sex Med
+Br J Soc Clin Psychol
+Br J Soc Med
+Br J Soc Psychol
+Br J Soc Work
+Br J Sociol
+Br J Sociol Educ
+Br J Sports Hist
+Br J Sports Med
+Br J Surg
+Br J Theatre Nurs
+Br J Tuberc Dis Chest
+Br J Urol
+Br J Vener Dis
+Br Libr J
+Br Med Bull
+Br Med J
+Br Med J (Clin Res Ed)
+Br Poult Sci
+Br Rev Econ Issues
+Br Sci News
+Br Stud Monit
+Br Vet J
+Br Yearb Sci
+Brachytherapy
+Brain
+Brain Behav Evol
+Brain Behav Immun
+Brain Cogn
+Brain Dev
+Brain Inj
+Brain Lang
+Brain Pathol
+Brain Res
+Brain Res Brain Res Protoc
+Brain Res Brain Res Rev
+Brain Res Bull
+Brain Res Cogn Brain Res
+Brain Res Dev Brain Res
+Brain Res Gene Expr Patterns
+Brain Res Mol Brain Res
+Brain Topogr
+Brain Tumor Pathol
+Brandeis J Fam Law
+Brandeis Law J
+Brandeis Rev
+Bras Med
+Bratisl Lek Listy
+Braunschw Veroff Gesch Pharm Naturwiss
+Braz Dent J
+Braz Econ Stud
+Braz J Biol
+Braz J Infect Dis
+Braz J Med Biol Res
+Braz J Popul Stud
+Breast
+Breast Cancer
+Breast Cancer Res
+Breast Cancer Res Treat
+Breast Dis
+Breast J
+Breastfeed Rev
+Brethr Life Thought
+Brevia (Rome)
+Bridg Wash D C
+Brief Bioinform
+Brief Funct Genomic Proteomic
+Brief Med Ethics
+Briefs
+Brigh Young Univ Law Rev
+Brigham Young Univ Stud
+Bristol Med Chir J
+Britannia
+Bromley Local Hist
+Bronches
+Bronchopneumologie
+Bronte Soc Trans
+Brookhaven Symp Biol
+Brookings Bull
+Brookings Pap Econ Act
+Brookings Rev
+Brooklyn Hosp J
+Brooklyn J Int Law
+Brooklyn Law Rev
+Broward Rev
+Bruns Beitr Klin Chir
+Bruns Beitr Klinischen Chir
+Brux Med
+Bryologist
+Buch Augenarzt
+Buffalo Law Rev
+Build Oper Manage
+Build Syst Des
+Built Environ
+Bul Keluarga
+Bul Pol Acad Sci Chem
+Bul Stiint Sect Stiint Medicale Acad Republicii Pop Romane
+Bul Univ Shtet Tiranes Ser Shk Mjekesore
+Bull Acad Chir Dent (Paris)
+Bull Acad Dent (Paris)
+Bull Acad Dent Handicap
+Bull Acad Gen Dent
+Bull Acad Med N J
+Bull Acad Natl Chir Dent
+Bull Acad Natl Med
+Bull Acad Pol Sci Biol
+Bull Acad R Med Belg
+Bull Acad Serbe Sci Arts
+Bull Acad Soc Lorraines Sci
+Bull Acad Vet Fr
+Bull Akron City Hosp
+Bull Akron Dent Soc
+Bull Alameda Cty Dent Soc
+Bull Alexandria Fac
+Bull Am Acad Arts Sci
+Bull Am Acad Psychiatry Law
+Bull Am Assoc Dent Ed
+Bull Am Assoc Hist Nurs
+Bull Am Assoc Hosp Dent
+Bull Am Coll Nurse Midwifery
+Bull Am Coll Nurse Midwives
+Bull Am Coll Physicians
+Bull Am Coll Surg
+Bull Am Protestant Hosp Assoc
+Bull Am Schools Orient Res
+Bull Am Soc Inf Sci
+Bull Anesth Hist
+Bull Anim Health Prod Afr
+Bull Assoc Anat (Nancy)
+Bull Assoc Diplomes Microbiol Fac Pharm Nancy
+Bull Assoc Fr Etud Cancer
+Bull Assoc Geogr Fr
+Bull Assoc Guillaume Bude
+Bull Assoc Med Haiti
+Bull Assoc Tunis Plan Fam
+Bull At Sci
+Bull Atl Cape May Cty Dent Soc
+Bull Audiophonol
+Bull Bergen Cty Dent Soc
+Bull Bibl Nat
+Bull Bibl R Albert
+Bull Bibliogr
+Bull Biol Fr Belg
+Bull Br Assoc Orient
+Bull Br Mus
+Bull Br Psychol Soc
+Bull Br Soc Middle East Stud
+Bull Bronx Cty Dent Soc
+Bull Brooklyn
+Bull Calcutta Sch Trop Med
+Bull Can Hist Med
+Bull Cancer
+Bull Cancer Inst Okayama Univ Med Sch
+Bull Cancer Radiother
+Bull Cent Hist Econ Soc Reg Lyon
+Bull Cercle Benelux Hist Pharm
+Bull Cerp
+Bull Chem Soc Jpn
+Bull Chest Dis Res Inst Kyoto Univ
+Bull Chic Med Soc
+Bull Cincinnati Dent Soc
+Bull Cl Lett Sci Morales Polit
+Bull Cleve Dent Soc
+Bull Clevel Med Libr
+Bull Clin Neurosci
+Bull Coll Lib Arts
+Bull Concern Asian Sch
+Bull Contra Costa Dent Soc
+Bull Cosm Club
+Bull Czechoslovak Law
+Bull Dayton Dent Soc
+Bull Dent Guid Counc Cereb Palsy
+Bull Ec Fr Extr Orient
+Bull Econ Res
+Bull Econ Soc Maroc
+Bull Eighth Dist Dent Soc
+Bull Eleventh Dist Dent Soc
+Bull Endem Dis (Baghdad)
+Bull Entomol Res
+Bull Environ Contam Toxicol
+Bull Epizoot Dis Afr
+Bull Essex Cty Dent Soc
+Bull Etud Orient
+Bull Etud Port Bres
+Bull Etud Valeryennes
+Bull Eugen Soc
+Bull Eur Physiopathol Respir
+Bull Exp Biol Med
+Bull Fed Soc Gynecol Obstet Lang Fr
+Bull Fifth Dist Dent Soc (Fresno)
+Bull Fifth Dist Dent Soc State N Y
+Bull Francoph Afr
+Bull Ga Acad Sci
+Bull Gandhigram Inst Rural Health Fam Welf Trust
+Bull Gastrointest Endosc
+Bull Geisinger
+Bull Georgetown Univ Med Cent
+Bull Ghana Geogr Assoc
+Bull Gloucester Cty Hist Soc
+Bull Group Eur Rech Sci Stomatol Odontol
+Bull Group Int Rech Sci Stomatol
+Bull Group Int Rech Sci Stomatol Odontol
+Bull Haffkine
+Bull High Inst Public Health
+Bull His Sci Auvergne
+Bull Hisp
+Bull Hist Dent
+Bull Hist Electr
+Bull Hist Med
+Bull Hist Med Suppl
+Bull Hist Polit
+Bull Hosp Joint Dis
+Bull Hosp Jt Dis
+Bull Hosp Jt Dis Orthop Inst
+Bull Hosp Spec Surg
+Bull Hudson Cty Dent Soc
+Bull Hunt Inst Bot Doc
+Bull Hyg
+Bull Hyg (Lond)
+Bull Ill Dent Hyg Assoc
+Bull Indian Inst Hist Med Hyderabad
+Bull Indones Econ Stud
+Bull Infirm Cathol Can
+Bull Inform Fed Pharm Mediterr
+Bull Inst Chem Res Kyoto Univ
+Bull Inst Class Stud Univ Lond
+Bull Inst Egypte
+Bull Inst Fond Afr Noire Ser B
+Bull Inst Fr Archeol Orient
+Bull Inst Hist Med Hyderabad
+Bull Inst Hist Res
+Bull Inst Marit Trop Med Gdynia
+Bull Inst Med Res Kuala Lumpur
+Bull Inst Med Res Univ Madr
+Bull Inst Mod Hist Acad Sin
+Bull Inst Natl Sante Rech Med
+Bull Int Acad Pol Sci Let Cl Med
+Bull Int Comm Urgent Anthropol Ethnol Res
+Bull Int Pediatr Assoc
+Bull Int Serv Sante Armees Terre Mer Air
+Bull Int Union Tuberc
+Bull Int Union Tuberc Lung Dis
+Bull Islam Med
+Bull J N Y State Archeol Assoc
+Bull J Rylands Univ Libr Manchester
+Bull Jersey City Margaret Hague Mat Hosp
+Bull Johns Hopkins Hosp
+Bull Kanagawa Dent Coll
+Bull Kresge Eye Inst
+Bull Lat Am Res
+Bull Leo Baeck Inst
+Bull Liaison Demogr Afr
+Bull Los Angel Neuro Soc
+Bull Los Angeles Dent Soc
+Bull Los Angeles Neurol Soc
+Bull Louis A Weiss Mem Hosp
+Bull Mason Clin
+Bull Mass Nurses Assoc
+Bull Math Biol
+Bull Math Biophys
+Bull McGuire Clin St Lukes Hosp Richmond St Lukes Hosp McGuire Clin
+Bull Med
+Bull Med Ethics
+Bull Med Leg Toxicol
+Bull Med Leg Urgence Med Cent Antipoisons
+Bull Med Libr Assoc
+Bull Mem Acad R Med Belg
+Bull Mem Fac Mixte Med Pharm Dakar
+Bull Mem Soc Anthropol Paris
+Bull Mem Soc Chir Paris
+Bull Mem Soc Fr Ophtalmol
+Bull Mem Soc Med Hop Paris
+Bull Menninger Clin
+Bull Mens Soc Med Mil Fr
+Bull Mens Soc Vet Prat Fr
+Bull Methodol Sociol
+Bull Mich Dent Hyg Assoc
+Bull Micr Appl
+Bull Midtown Dent Soc
+Bull Millard Fillmore Hosp Buffalo
+Bull Mo Hist Soc
+Bull Monmouth Ocean Cty Dent Soc
+Bull Montg Bucks Dent Soc
+Bull Moore White Med Found Los Angel
+Bull Mt Desert Isl Biol Lab Salisb Cove Maine
+Bull Munic Off Ville Mars
+Bull Mus Hong B Arts
+Bull Mus Rov Beaux Arts Belg
+Bull N J Coll Med Dent
+Bull N J Soc Dent Child
+Bull N Y Acad Med
+Bull N Y State Dent Soc Anesthesiol
+Bull N Y State Soc Dent Child
+Bull N Z Soc Periodontol
+Bull Narc
+Bull Nat Fam Plan Counc Vic
+Bull Natl Clgh Poison Control Cent
+Bull Natl Guild Cathol Psychiatr
+Bull Natl Inst Health
+Bull Natl Med Dent Assoc Natl Advocates Soc
+Bull Natl Tuberc Assoc
+Bull Natl Tuberc Respir Dis Assoc
+Bull New Engl Med Cent
+Bull Newark Dent Club
+Bull Ninth Dist Dent Soc
+Bull North Dist Dent Soc
+Bull Off Chambre Synd Med Seine
+Bull Off Int Epizoot
+Bull Ophthalmol Soc Egypt
+Bull Osaka Med Coll
+Bull Osaka Med Sch
+Bull Osaka Med Sch Suppl
+Bull Pac Coast Soc Orthod
+Bull Pan Am Health Organ
+Bull Parenter Drug Assoc
+Bull Park Ridge Cent
+Bull Passaic Cty Dent Soc
+Bull Peace Propos
+Bull Pharm Res Inst
+Bull Phila Assoc Psychoanal
+Bull Phila Cty Dent Soc
+Bull Phila Pa Hosp Ayer Clin Lab
+Bull Philol Hist
+Bull Philos Mediev
+Bull Physiopathol Respir (Nancy)
+Bull Plainfield Dent Soc
+Bull Popul Dev Stud Cent
+Bull Postgrad Comm Med Univ Syd
+Bull Postgrad Inst Med Educ Res Chandigarh
+Bull Prosthet Res
+Bull Psychon Soc
+Bull Publ Health Soc (Kuala Lumpur)
+Bull Res Counc Isr
+Bull Res Counc Isr Sect E Exp Med
+Bull Res Humanit
+Bull Rheum Dis
+Bull San Diego Cty Dent Soc
+Bull San Mateo Cty Dent Soc
+Bull Sch Med
+Bull Sch Med Univ Md
+Bull Sch Orient Afr Stud
+Bull Schweiz Akad Med Wiss
+Bull Sci Med (Bologna)
+Bull Sci Technol Soc
+Bull Seances Acad R Sci Outre Mer
+Bull Second Dist Dent Soc
+Bull Sect Hist Mod Contemp
+Bull Sinai Hosp Detroit
+Bull Sloane Hosp Women Columbia Presbyt Med
+Bull Soc Agric Sci Arts Sarthe
+Bull Soc Amis Sci (Med) (Poznan)
+Bull Soc Amis Vieux Toulon
+Bull Soc Archeol Finistere
+Bull Soc Archeol Hist Artist Vieux Pap
+Bull Soc Archeol Hist Litt Sci Gers
+Bull Soc Belg Etud Geogr
+Bull Soc Belge Etud Napoleon
+Bull Soc Belge Ophtalmol
+Bull Soc Borda
+Bull Soc Chim Biol (Paris)
+Bull Soc Chim Fr
+Bull Soc Chir Paris
+Bull Soc Etud His Alpes
+Bull Soc Etud Oceaniennes
+Bull Soc Fr Dermatol Syphiligr
+Bull Soc Fr Hist Hop
+Bull Soc Hist Archeol Perigord
+Bull Soc Hist Mod
+Bull Soc Hist Protestant Fr
+Bull Soc Ind Mulhouse
+Bull Soc Int Chir
+Bull Soc Ital Med Ig Trop Sez Eritrea
+Bull Soc Languedoc Geogr
+Bull Soc Lett Sci Arts Correze
+Bull Soc Liban Hist Med
+Bull Soc Med Afr Noire Lang Fr
+Bull Soc Med Hyg Alger
+Bull Soc Nat Antiq Fr
+Bull Soc Ophtalmol Fr
+Bull Soc Pathol Exot
+Bull Soc Pathol Exot Filiales
+Bull Soc Pharm Bord
+Bull Soc Pharm Strasb
+Bull Soc R Belge Gynecol Obstet
+Bull Soc Ramond
+Bull Soc Sci Lettr Arts Pau
+Bull Soc Sci Med Grand Duche Luxemb
+Bull Soc Univ Cartogr
+Bull St Louis Park Med Cen
+Bull Statec
+Bull Suffolk Cty Dent Soc
+Bull Tenn Nurses Assoc
+Bull Tenth Dist Dent Soc (Rockville Centre)
+Bull Tex Nurses Assoc
+Bull Tokyo Dent Coll
+Bull Tokyo Med Dent Univ
+Bull Toledo Dent Soc
+Bull Train
+Bull Tri Cty Dent Soc
+Bull Trimest Inst Actuaires Francais
+Bull Trimest Plan Fam
+Bull Trimest Soc Mycol Fr
+Bull Tufts N Engl Med Cent
+Bull Tulane Univ Med Fac
+Bull U S Army Med Dep
+Bull Unesco Reg Off Educ Asia Pac
+Bull Union Cty Dent Soc
+Bull Val Dent Soc
+Bull Vanc Med Assoc
+Bull World Health Org Suppl
+Bull World Health Organ
+Bull World Med Assoc
+Bull Yamaguchi Med Sch
+Bulletin NY Med Coll
+Bundesgesundheitsblatt
+Bundesgesundheitsblatt Gesundheitsforschung Gesundheitsschutz
+Bur
+Bur Justice Stat Spec Rep
+Burgenl Heimatbl
+Burgense
+Burlingt Mag
+Burma Med J
+Burns
+Burns Incl Therm Inj
+Bus Econ
+Bus Econ Rev
+Bus Ethics
+Bus Ethics Q
+Bus Health
+Bus Hist
+Bus Hist Rev
+Bus Horiz
+Bus Insur
+Bus Prof Ethics
+Bus Prof Ethics J
+Bus Rev
+Bus Thail
+Bus Week
+BusinessIndia
+Butll Soc Amics Hist Cienc Farm Catalana
+By Bygd
+Bygone Kent
+Byzantinische Forsch
+Byzantinische Z
+Byzantinoslavica
+Byzantion
+C R Acad Sci Gen
+C R Acad Sci Hebd Seances Acad Sci D
+C R Acad Sci II
+C R Acad Sci III
+C R Assoc Anat
+C R Biol
+C R Hebd Seances Acad Sci
+C R Seances Acad Sci D
+C R Seances Acad Sci III
+C R Seances Soc Biol Fil
+C R Soc Fr Gyncol
+C R Ther Pharmacol Clin
+C R Trav Lab Carlsberg
+C R Trav Lab Carlsberg [Chim]
+CA Cancer J Clin
+CAFS News
+CAL
+CANA
+CANNT J
+CAP Today
+CARE Briefs Develop Isssues
+CBE Views
+CCAR J
+CCICA Annual
+CCL Family Found
+CCL News
+CCQ
+CDA J
+CDC AIDS Wkly
+CDE Work Pap
+CDR (Lond Engl Rev)
+CDR (Lond Engl Wkly)
+CDS Rev
+CDT Dig
+CE Focus
+CEDPA Netw
+CEDPA World Wide
+CEPAL Rev
+CES Odontol
+CEX Rep Civ Eff Exerc
+CHA Insight
+CHAC Rev
+CIBA Symp
+CICIAMS Nouv
+CIN Plus
+CIRDAP Dev Dig
+CIRES Cah Ivoir Rech Econ Soc
+CLAO J
+CMAJ
+CNA Bull
+CNS Drug Rev
+CNS Drugs
+CNS Neurol Disord Drug Targets
+CNS Spectr
+COBASHECA
+COMBROAD
+COO Rep
+CPC Res Rev
+CPJ
+CRC Crit Rev Biochem
+CRC Crit Rev Bioeng
+CRC Crit Rev Clin Lab Sci
+CRC Crit Rev Clin Neurobiol
+CRC Crit Rev Clin Radiol Nucl Med
+CRC Crit Rev Diagn Imaging
+CRC Crit Rev Food Sci Nutr
+CRC Crit Rev Immunol
+CRC Crit Rev Microbiol
+CRC Crit Rev Plant Sci
+CRC Crit Rev Radiol Sci
+CRC Crit Rev Toxicol
+CRHCS news
+CRNA
+CVI Forum
+CVI Newswatch
+CVP
+Cabo
+Cad Casa Oswaldo Cruz
+Cad Hist Saude
+Cad Pesqui
+Cad Saude Publica
+Cad Ter Labor
+Caduceus
+Caementum
+Cah Acad Bretagne
+Cah Afr Adm Publique
+Cah Alex
+Cah All Isr Univers
+Cah Alsac Archeol Art Hist
+Cah Am Lat
+Cah Am Lat Ser Sci Homme
+Cah Anal Donnees
+Cah Anesthesiol
+Cah Ann Normandie
+Cah Anthropol
+Cah Archeol Hist Berry
+Cah Brux
+Cah Cent Rech Etud Paris Lle de France
+Cah Civilis Alp
+Cah Clio
+Cah Coll Med Hop Paris
+Cah Confront
+Cah Dix
+Cah Econ Brux
+Cah Econ Soc
+Cah Etud Afr
+Cah Etud Anc
+Cah Etud Mediev
+Cah Fontenay
+Cah Geogr Que
+Cah Geogr Rouen
+Cah Geol
+Cah Haut Marnais
+Cah Hist
+Cah Hist Mond
+Cah Hist Second Guerr Mond
+Cah Int Hist Econ Soc
+Cah Iroise
+Cah Laennec
+Cah Leopold Delisle
+Cah Lorrains
+Cah Med
+Cah Med Inter Prof
+Cah Med Lyon
+Cah Med Vet
+Cah Medicaux Union Fr
+Cah Mediterr Orient Monde Turco Iran
+Cah Monde Russe Sov
+Cah Nurs
+Cah O M
+Cah Odontostomatol Touraine
+Cah Orient
+Cah Orstom (Sci Hum)
+Cah Prothese
+Cah Psychiatr
+Cah Que Demogr
+Cah Ration
+Cah Rech Archit
+Cah Rech Sociol
+Cah Relig Afr
+Cah Rmf Bull Med Guide Pract Rev
+Cah Sci Hum
+Cah Semin Philos
+Cah Sexol Clin
+Cah Sociol Demogr Med
+Cah Tech AFRO
+Cah Tunis
+Cah Victor Edouardiens
+Cah Vilfredo Pareto
+Cairo
+Cairo Today
+Calcif Tissue Int
+Calcif Tissue Res
+Calcutta Med J
+Calif Anthropol
+Calif Clin
+Calif Code Regul
+Calif Counts
+Calif Health
+Calif Hist Q
+Calif Hist Soc Q
+Calif Hosp
+Calif J
+Calif Law Rev
+Calif Mag
+Calif Manage Rev
+Calif Med
+Calif Nurse
+Calif Sociol
+Calif State Bar J
+Calif West Int Law J
+Calif West Law Rev
+Californians
+Calyx
+Camb Anthropol
+Camb Law J
+Camb Munic Code 1988 Camb Mass
+Camb Q Healthc Ethics
+Cambodge Soir
+Cambridge J Econ
+Campbell Law Rev
+Can AIDS News
+Can Aeronaut Space J
+Can Am Slav Stud
+Can Anaesth Soc J
+Can Assoc Radiol J
+Can Bar J
+Can Bar Rev
+Can Bull Cardiovasc Nurs
+Can Bull Med Hist
+Can Commun Dis Rep
+Can Crit Care Nurs J
+Can Dent Hyg
+Can Dimens
+Can Dis Wkly Rep
+Can Doct
+Can Entom
+Can Ethn Stud
+Can Fam Law Q
+Can Fam Physician
+Can Forces Dent Serv Bull
+Can Forces Dent Serv Q
+Can Forum
+Can Geogr
+Can HIV AIDS Policy Law Newsl
+Can HIV AIDS Policy Law Rev
+Can Hist Rev
+Can Hosp
+Can Hum Rights Advocate
+Can Hum Rights Yearb
+Can Int Educ
+Can J Afr Stud
+Can J Aging
+Can J Anaesth
+Can J Appl Physiol
+Can J Appl Sport Sci
+Can J Behav Sci
+Can J Biochem
+Can J Biochem Cell Biol
+Can J Biochem Physiol
+Can J Bot
+Can J Cardiol
+Can J Cardiovasc Nurs
+Can J Chem
+Can J Clin Pharmacol
+Can J Commun Ment Health
+Can J Community Dent
+Can J Comp Med
+Can J Comp Med Vet Sci
+Can J Diet Pract Res
+Can J Earth Sci
+Can J Econ
+Can J Exp Psychol
+Can J Fam Law
+Can J For Res
+Can J Gastroenterol
+Can J Genet Cytol
+Can J Hist
+Can J Hist Sport
+Can J Hist Sport Phys Educ
+Can J Hosp Pharm
+Can J Hum Sex
+Can J Infect Control
+Can J Ital Stud
+Can J Lat Am Caribb Stud
+Can J Law Soc
+Can J Med Radiat Technol
+Can J Med Sci
+Can J Med Technol
+Can J Microbiol
+Can J Native Stud
+Can J Neurol Sci
+Can J Nurs Adm
+Can J Nurs Leadersh
+Can J Nurs Res
+Can J Ob Gyn Womens Health Care
+Can J Occup Ther
+Can J Oncol
+Can J Ophthalmol
+Can J Otolaryngol
+Can J Otolaryngol Suppl
+Can J Philos
+Can J Phys
+Can J Physiol Pharmacol
+Can J Polit Sci
+Can J Psychiatr Nurs
+Can J Psychiatry
+Can J Psychol
+Can J Public Health
+Can J Radiogr Radiother Nucl Med
+Can J Reg Sci
+Can J Res
+Can J Rural Med
+Can J Sociol
+Can J Sport Sci
+Can J Stat
+Can J Surg
+Can J Urban Res
+Can J Urol
+Can J Vet Res
+Can J Women Law
+Can J Womens Health Care Phys Addressing Womens Health Issues
+Can J Zool
+Can J. Criminol
+Can Labour
+Can Med Assoc J
+Can Ment Health
+Can Mineral
+Can Nurse
+Can Oncol Nurs J
+Can Oper Room Nurs J
+Can Pharm J
+Can Psychiatr Assoc J
+Can Psychol
+Can Psychol Rev
+Can Public Adm
+Can Public Policy
+Can Respir J
+Can Rev Am Stud
+Can Rev Sociol Anthropol
+Can Serv Med J
+Can Slavon Pap
+Can Soc Trends
+Can Soc Work Rev
+Can Stat Rev
+Can Stud Popul
+Can Supreme Court Rep Can Supreme Court
+Can Vet J
+Can Welf
+Can Womens Stud
+Can Yearb Int Law
+Canadian
+Cancer
+Cancer Biochem Biophys
+Cancer Biol Ther
+Cancer Biother
+Cancer Biother Radiopharm
+Cancer Bull
+Cancer Causes Control
+Cancer Cell
+Cancer Cell Int
+Cancer Cells
+Cancer Chemother Biol Response Modif
+Cancer Chemother Pharmacol
+Cancer Chemother Rep
+Cancer Chemother Rep 2
+Cancer Chemother Rep 3
+Cancer Clin Trials
+Cancer Commun
+Cancer Control
+Cancer Cytol
+Cancer Detect Prev
+Cancer Detect Prev Suppl
+Cancer Drug Deliv
+Cancer Epidemiol Biomarkers Prev
+Cancer Gene Ther
+Cancer Genet Cytogenet
+Cancer Imaging
+Cancer Immun
+Cancer Immunol Immunother
+Cancer Invest
+Cancer J
+Cancer J Sci Am
+Cancer Lett
+Cancer Metastasis Rev
+Cancer Nurs
+Cancer Pract
+Cancer Prev Control
+Cancer Prog
+Cancer Radiother
+Cancer Res
+Cancer Sci
+Cancer Surv
+Cancer Ther
+Cancer Treat Rep
+Cancer Treat Res
+Cancer Treat Rev
+Cancro
+Canine Pract
+Cantium
+Cap Chem
+Cap Nurs
+Cap Univ Law Rev
+Cape Breton Post
+Capitation Manag Rep
+Capitation Rates Data
+Carbohydr Lett
+Carbohydr Res
+Carcinog Compr Surv
+Carcinogenesis
+Card Electrophysiol Rev
+Cardiol Clin
+Cardiol Manage
+Cardiol Prat
+Cardiol Rev
+Cardiol Young
+Cardiologia
+Cardiology
+Cardioscience
+Cardiovasc Clin
+Cardiovasc Diabetol
+Cardiovasc Dis
+Cardiovasc Drug Rev
+Cardiovasc Drugs Ther
+Cardiovasc Hematol Agents Med Chem
+Cardiovasc Intervent Radiol
+Cardiovasc J S Afr
+Cardiovasc Nurs
+Cardiovasc Pathol
+Cardiovasc Radiat Med
+Cardiovasc Radiol
+Cardiovasc Res
+Cardiovasc Res Cent Bull
+Cardiovasc Rev Rep
+Cardiovasc Revasc Med
+Cardiovasc Surg
+Cardiovasc Toxicol
+Cardiovasc Ultrasound
+Cardozo Law Rev
+Care Giver
+Care Manag J
+Career Dev Q
+Caribb Aff
+Caribb Health
+Caribb Med J
+Caribb Rev
+Caribb Stud
+Caribe Contemp
+Caridad Cienc Arte
+Caries Res
+Caring
+Carinthia I
+Caritas
+Carlsberg Res Commun
+Carmarthen Antiq
+Carmarthensh Hist
+Carnegie Q
+Carnets Enfance
+Carney Hosp J
+Carol J Pharm
+Carol Plann
+Carrefour Afr
+Carta Econ Reg
+Carta Inf
+Cartogr J
+Cas Cesk Lek
+Cas Cesk Vet
+Cas Lek Cesk
+Cas Matice Moravske
+Cas Nar Muz
+Cas Zgodovino Narodop
+Casa Mujer
+Casana
+Case Comment
+Case Manager
+Case Stud Health Adm
+Case West Reserve J Int Law
+Case West Reserve Law Rev
+Catena Suppl
+Cater Health
+Cathet Cardiovasc Diagn
+Catheter Cardiovasc Interv
+Cathol Hist Rev
+Cathol Hosp
+Cathol Lawyer
+Cathol Med Q
+Cathol Mind
+Cathol Nurse
+Cathol Nurse (Wallsend)
+Cathol Stand
+Cathol Univers Law Rev
+Cathol Update
+Cathol Woman
+Cathol World
+Cato J
+Cell
+Cell Adhes Commun
+Cell Biochem Biophys
+Cell Biochem Funct
+Cell Biol Educ
+Cell Biol Int
+Cell Biol Int Rep
+Cell Biol Rev
+Cell Biol Toxicol
+Cell Biophys
+Cell Calcium
+Cell Chromosome
+Cell Commun Adhes
+Cell Commun Signal
+Cell Cycle
+Cell Death Differ
+Cell Differ
+Cell Differ Dev
+Cell Growth Differ
+Cell Immunol
+Cell Metab
+Cell Microbiol
+Cell Mol Biol
+Cell Mol Biol (Noisy-le-grand)
+Cell Mol Biol Incl Cyto Enzymol
+Cell Mol Biol Lett
+Cell Mol Biol Res
+Cell Mol Immunol
+Cell Mol Life Sci
+Cell Mol Neurobiol
+Cell Motil
+Cell Motil Cytoskeleton
+Cell Muscle Motil
+Cell Oncol
+Cell Physiol Biochem
+Cell Prolif
+Cell Regul
+Cell Res
+Cell Signal
+Cell Stress Chaperones
+Cell Struct Funct
+Cell Tissue Bank
+Cell Tissue Kinet
+Cell Tissue Res
+Cell Transplant
+Cell Vis
+Cells Tissues Organs
+Cellule
+Cent Afr J Med
+Cent Asia Monit
+Cent Call
+Cent Est Med
+Cent Estud Recur Odontol Nino
+Cent Eur Hist
+Cent Eur J Public Health
+Cent Home Care Policy Res Policy Briefs
+Cent Issues Anthropol
+Cent Mag
+Cent Med
+Cent Nerv Syst Trauma
+Cent States Speech J
+Cent View
+Centaurus
+Centen Rev
+Centerpoint
+Centerviews
+Centr Asian Surv
+Cephalalgia
+Cereb Cortex
+Cereb Palsy Bull
+Cereb Palsy J
+Cereb Palsy Rev
+Cerebellum
+Cerebrospinal Fluid Res
+Cerebrovasc Brain Metab Rev
+Cerebrovasc Dis
+Cerebrum
+Ceres
+Cerrahpasa Tip Fak Derg
+Certif Dent Tech
+Cervello G Nevrol
+Cervix Low Female Genital Tract
+Cesk Cas Hist
+Cesk Dermatol
+Cesk Epidemiol Mikrobiol Imunol
+Cesk Farm
+Cesk Fysiol
+Cesk Gastroenterol Vyz
+Cesk Gynekol
+Cesk Hyg
+Cesk Morfol
+Cesk Neurol
+Cesk Neurol Neurochir
+Cesk Oftalmol
+Cesk Onkol
+Cesk Otolaryngol
+Cesk Patol
+Cesk Pediatr
+Cesk Psychiatr
+Cesk Psychol
+Cesk Radiol
+Cesk Rentgenol
+Cesk Slov Oftalmol
+Cesk Stomatol
+Cesk Zdrav
+Ceska Gynekol
+Ceska Slov Farm
+Ceska Slov Psychiatr
+Ceskoslov Biol
+Ceskoslov Nemocnice
+Cesra Saule
+Ceylon Dent J
+Ceylon Med J
+Chain Drug Rev
+Chang Gung Med J
+Chang Men
+Change
+Changes
+Changgeng Yi Xue Za Zhi
+Changing Times
+Channels
+Chaos
+Chaos Solitons Fractals
+Chart
+Chatelaine
+Chaucer Rev
+Chekh Fiziol
+Chekhoslovatskaia Biol
+Chem Analityczna
+Chem Ber
+Chem Biol
+Chem Biol Drug Des
+Chem Biol Interact
+Chem Br
+Chem Can
+Chem Commun (Camb)
+Chem Depend
+Chem Drug
+Chem Eng News
+Chem Erde
+Chem Geol
+Chem Herit
+Chem Immunol
+Chem Immunol Allergy
+Chem Ind
+Chem Lett (Jpn)
+Chem Mater
+Chem Pharm Bull (Tokyo)
+Chem Phys
+Chem Phys Lett
+Chem Phys Lipids
+Chem Rec
+Chem Res Toxicol
+Chem Rev
+Chem Scr
+Chem Senses
+Chem Soc Rev
+Chem Week
+Chem Weekbl
+Chem Zvesti
+Chembiochem
+Chemioterapia
+Chemistry
+Chemistry (Easton)
+Chemosphere
+Chemotherapia (Basel)
+Chemotherapy
+Chemphyschem
+Chemtech
+Chesopiean
+Chest
+Chest Surg Clin N Am
+Chesterton Rev
+Chic Hist
+Chic J Int Law
+Chic Kent Law Rev
+Chic Med
+Chic Med Rec
+Chic Med Sch Q
+Chic Pol Rev
+Chic Stud
+Chic Today
+Chicago Bar Rec
+Chicano Law Rev
+Chief Inf Off J
+Chieh Fang Chun I Hsueh Tsa Chih
+Chikwa Kijae Hakhoe Chi
+Child Abuse Negl
+Child Adolesc Psychiatr Clin N Am
+Child Adolesc Social Work J
+Child Care Health Dev
+Child Care Q
+Child Dev
+Child Fam
+Child Health Alert
+Child Health Care
+Child Health Dialogue
+Child Leg Rights J
+Child Maltreat
+Child Nephrol Urol
+Child Neuropsychol
+Child Psychiatry Hum Dev
+Child Psychiatry Q
+Child Surviv Action News
+Child Today
+Child Trop
+Child Welfare
+Child Worldw
+Child Youth Serv Rev
+Children
+Childs Brain
+Childs Nerv Syst
+Chim Ther
+Chimia (Aarau)
+Chin Cult
+Chin Econ Stud
+Chin Environ Dev
+Chin Geogr Sci
+Chin J Biotechnol
+Chin J Dent Res
+Chin J Dig Dis
+Chin J Integr Med
+Chin J Physiol
+Chin J Popul Sci
+Chin J Traumatol
+Chin Med J
+Chin Med J (Engl)
+Chin Med Sci J
+Chin Sci
+Chin Sociol Anthropol
+China Aktuell
+China Bus Rev
+China J
+China Law Pract
+China Newsl
+China Popul Headl
+China Popul Newsl
+China Popul Res Leads
+China Popul Res Newsl
+China Popul Today
+China Q
+China Reconstr
+China Rep
+Chinas Med
+Chir Dent Fr
+Chir Forum Exp Klin Forsch
+Chir Ital
+Chir Main
+Chir Maxillofac Plast
+Chir Narzadow Ruchu Ortop Pol
+Chir Organi Mov
+Chir Patol Sper
+Chir Pediatr
+Chir Urol
+Chirality
+Chirigaku Hyoron
+Chiropr Hist
+Chiropr Osteopat
+Chirurg
+Chirurgia (Bucur)
+Chirurgie
+Chiryo
+Chittys Law J
+Chnia Geogr
+Choice (Middletown)
+Choices
+Choices Respir Manage
+Chonnam Med J
+Chosen Ibo
+Choson Uihak
+Chot Mai Het Kan Phayaban
+Christ Bioeth
+Christ Century
+Christ Crisis
+Christ Med Dent Soc J
+Christ Med Soc J
+Christ Nurse (Mysore)
+Christ Nurse Int
+Christ Sch Rev
+Christ Sci Monitor
+Christ Sci Monitor (East Ed)
+Christ Today
+Christ World
+Christiana Albertina Kiel Univ Z
+Chromatogr Rev
+Chromosoma
+Chromosome Res
+Chron Actual SEDEIS
+Chron CEPED
+Chron Egypte
+Chron High Educ
+Chron Horticult
+Chron Okla
+Chron R Coll Physicians Edinb
+Chron Respir Dis
+Chronic Dis Can
+Chronicle
+Chronicle (Phila)
+Chronobiol Int
+Chronobiologia
+Chudoku Kenkyu
+Chung Ang Ui Dai Chi
+Chung Hua Chien Yen I Hsueh Tsa Chih
+Chung Hua Chuan Jan Ping Tsa Chih
+Chung Hua Wei Sheng Wu Hsueh Ho Mieh I Hsueh Tsa Chih
+Chung Kuo Yao Hsueh Tsa Chih
+Chungang Uihak
+Church Hist
+Church Soc
+Ciba Found Study Group
+Ciba Found Symp
+Ciba Symp
+Ciba Z (Basel)
+Ciel Terre
+Cienc Cult
+Cienc Desarro
+Cienc Invest
+Cienc Soc
+Cinci Hist Soc Bull
+Cinci J Med
+Cincinnati Dent Soc Bull
+Cir Bucal
+Cir Cir
+Cir Esp
+Cir Ginecol Urol
+Cir Pediatr
+Circ Farm
+Circ J
+Circ Odontol San Martin Tres Febr
+Circ Res
+Circ Shock
+Circ Shock Suppl
+Circulation
+Cities
+City J
+Cityscape
+Civ Cattol
+Civ Code Annot State Calif Calif
+Civ Liberties Rev
+Civ War Hist
+Civ War Times Illus
+Civil Lib
+Civilisations
+Cladistics
+Class Antiq
+Class J
+Class Outlook
+Class Philol
+Class Q
+Class World
+Classical Rev
+Clay Miner
+Clays Clay Miner
+Clearing House
+Cleft Palate Craniofac J
+Cleft Palate J
+Clergy Rev
+Cleve Clin J Med
+Cleve Clin Q
+Clevel State Law Rev
+Clgh Rev
+Clim Change
+Climacteric
+Clin Adv Hematol Oncol
+Clin Allergy
+Clin Allergy Immunol
+Clin Anat
+Clin Anesth
+Clin Appl Thromb Hemost
+Clin Auton Res
+Clin Biochem
+Clin Biochem Rev
+Clin Biomech (Bristol, Avon)
+Clin Breast Cancer
+Clin Bull
+Clin Calcium
+Clin Cancer Res
+Clin Cardiol
+Clin Chem
+Clin Chem Lab Med
+Clin Chest Med
+Clin Child Fam Psychol Rev
+Clin Chim Acta
+Clin Colorectal Cancer
+Clin Commun Disord
+Clin Cornerstone
+Clin Dermatol
+Clin Dev Immunol
+Clin Diagn Lab Immunol
+Clin Diagn Ultrasound
+Clin Diagn Virol
+Clin Dysmorphol
+Clin EEG Neurosci
+Clin Electroencephalogr
+Clin Endocrinol (Oxf)
+Clin Endocrinol Metab
+Clin Eng
+Clin Eng Inf Serv
+Clin Eng News
+Clin Ethics Rep
+Clin Eur
+Clin Evid
+Clin Excell Nurse Pract
+Clin Excerpts J Devoted Ther
+Clin Exp Allergy
+Clin Exp Dermatol
+Clin Exp Dial Apheresis
+Clin Exp Hypertens
+Clin Exp Hypertens A
+Clin Exp Hypertens B
+Clin Exp Immunol
+Clin Exp Med
+Clin Exp Metastasis
+Clin Exp Nephrol
+Clin Exp Neurol
+Clin Exp Obstet Gynecol
+Clin Exp Optom
+Clin Exp Pathol
+Clin Exp Pharmacol Physiol
+Clin Exp Pharmacol Physiol Suppl
+Clin Exp Rheumatol
+Clin Experiment Ophthalmol
+Clin Gastroenterol
+Clin Gastroenterol Hepatol
+Clin Genet
+Clin Genitourin Cancer
+Clin Geriatr Med
+Clin Gerontol
+Clin Ginecol
+Clin Haematol
+Clin Hemorheol
+Clin Hemorheol Microcirc
+Clin Imaging
+Clin Immunol
+Clin Immunol Immunopathol
+Clin Immunol Rev
+Clin Implant Dent Relat Res
+Clin Infect Dis
+Clin Intensive Care
+Clin Invest Ginecol Obstet
+Clin Invest Med
+Clin Investig
+Clin J Oncol Nurs
+Clin J Pain
+Clin J Sport Med
+Clin Lab
+Clin Lab (Zaragoza)
+Clin Lab Haematol
+Clin Lab Manage Rev
+Clin Lab Med
+Clin Lab Sci
+Clin Laser Mon
+Clin Lat
+Clin Leadersh Manag Rev
+Clin Linguist Phon
+Clin Liver Dis
+Clin Lung Cancer
+Clin Lymphoma
+Clin Lymphoma Myeloma
+Clin Mater
+Clin Med
+Clin Med (Northfield Il)
+Clin Med Res
+Clin Microbiol Infect
+Clin Microbiol Newsl
+Clin Microbiol Rev
+Clin Mol Allergy
+Clin Mol Pathol
+Clin Nephrol
+Clin Neurol Neurosurg
+Clin Neuropathol
+Clin Neuropharmacol
+Clin Neurophysiol
+Clin Neuropsychol
+Clin Neurosci
+Clin Neurosurg
+Clin Notes Respir Dis
+Clin Nucl Med
+Clin Nuova Rass Prog Med Internazionale
+Clin Nurs Pract Epilepsy
+Clin Nurs Res
+Clin Nurse Spec
+Clin Nutr
+Clin Obstet Gynaecol
+Clin Obstet Gynecol
+Clin Occup Environ Med
+Clin Odontol
+Clin Oncol
+Clin Oncol (R Coll Radiol)
+Clin Oral Implants Res
+Clin Oral Investig
+Clin Orthod Res
+Clin Orthop
+Clin Orthop Relat Res
+Clin Ortop
+Clin Ostet Ginecol
+Clin Otolaryngol
+Clin Otolaryngol Allied Sci
+Clin Otorinolaringoiatr
+Clin Pediatr (Bologna)
+Clin Pediatr (Phila)
+Clin Perform Qual Health Care
+Clin Perinatol
+Clin Pharm
+Clin Pharmacokinet
+Clin Pharmacol Ther
+Clin Phys Physiol Meas
+Clin Physiol
+Clin Physiol Biochem
+Clin Physiol Funct Imaging
+Clin Plast Surg
+Clin Podiatr Med Surg
+Clin Podiatr Med Surg North Am
+Clin Podiatry
+Clin Positron Imaging
+Clin Pract Epidemol Ment Health
+Clin Pract Guidel Quick Ref Guide Clin
+Clin Prev Dent
+Clin Privil White Pap
+Clin Proc Child Hosp Dist Columbia
+Clin Proc Child Hosp Natl Med Cent
+Clin Prostate Cancer
+Clin Psychol
+Clin Psychol Rev
+Clin Radiol
+Clin Rehabil
+Clin Rep
+Clin Reprod Fertil
+Clin Res
+Clin Res Cardiol
+Clin Res Pr Drug Regul Aff
+Clin Resour Manag
+Clin Rev Allergy
+Clin Rev Allergy Immunol
+Clin Rheum Dis
+Clin Rheumatol
+Clin Sci
+Clin Sci (Lond)
+Clin Sci Mol Med
+Clin Sci Mol Med Suppl
+Clin Soc Work J
+Clin Sports Med
+Clin Symp
+Clin Tech Small Anim Pract
+Clin Ter
+Clin Term
+Clin Ther
+Clin Torax
+Clin Toxicol
+Clin Toxicol (Phila)
+Clin Transl Oncol
+Clin Transpl
+Clin Transplant
+Clin Trials
+Clin Trials J
+Clin Trials Metaanal
+Clin Vaccine Immunol
+Clin Vet (Milano)
+Clinic (Paris)
+Clinica
+Clinician (Goa)
+Clinics
+Clinique (Paris)
+Clio
+Clio Med
+Cloning
+Cloning Stem Cells
+Cmd
+Coast Manage
+Cochrane Database Syst Rev
+Code Ala 1975 Ala
+Code Fed Regul Public Welfare
+Code Fed Regul Shipping
+Code Ga Annot Ga
+Code Laws S C 1976 Annot S C
+Code Va 1950 Va
+Coeur
+Coeur Med Interne
+Coevol Q
+Coexistence
+Cogn Affect Behav Neurosci
+Cogn Behav Neurol
+Cogn Behav Pract
+Cogn Behav Ther
+Cogn Neuropsychol
+Cogn Process
+Cognit Neuropsychiatry
+Cognit Psychol
+Cognit Ther Res
+Cognition
+Coimbra Med
+Coin World
+Colby Libr Q
+Cold Spring Harb Symp Quant Biol
+Colecc Estud CIEPLAN
+Colecc Hist Cienc Salud
+Coll Antropol
+Coll Relat Res
+Coll Rev
+Coll Stud J
+Coll Works Cardiopulm Dis
+Collana Monogr Gazz Med Sicil
+Collect N S Hist Soc
+Collections
+Collegian
+Colloids Surf B Biointerfaces
+Colloq Int CNRS
+Colloq Natx Cent Natl Rech Sci
+Colo Herit
+Colo J Int Environ Law Policy
+Colo Lawyer
+Colo Mag
+Colo Med
+Colo Nurse
+Colo Q
+Colo Revis Statut 1973 Colo
+Colomb Med
+Color Res Appl
+Colorectal Dis
+Columbia Forum
+Columbia Human Rights Law Rev
+Columbia J Gend Law
+Columbia J Law Soc Probl
+Columbia J Rev
+Columbia J World Bus
+Columbia Law Rev
+Columbia Sci Technol Law Rev
+Columbus Dent Soc Bull
+Comb Chem High Throughput Screen
+Commentary
+Commer Law Eur
+Commer Law J
+Commitment
+Common Factor
+Common Law World Rev
+Common Place
+Commonweal
+Commun ACM
+Commun Agric Appl Biol Sci
+Commun Behav Biol
+Commun Dis Intell
+Commun Dis Public Health
+Commun Dis Rep CDR Rev
+Commun Dis Rep CDR Suppl
+Commun Dis Rep CDR Wkly
+Commun Hist Artis Med Suppl
+Commun Monogr
+Commun Newsl
+Commun Nurs Res
+Commun Psychopharmacol
+Commun Soil Sci Plant Anal
+Commun Theory
+Communic Res
+Communio
+Communique
+Communique (Wash DC)
+Communist Econ
+Communist Econ Econ Transform
+Communist Post-Communist Stud
+Community Based Public Health Policy Pract
+Community Dent Health
+Community Dent Oral Epidemiol
+Community Dev J
+Community Eye Health
+Community Genet
+Community Health (Bristol)
+Community Health Stud
+Community Jr Coll J
+Community Med
+Community Ment Health J
+Community Ment Health Rev
+Community Nurs
+Community Nurse
+Community Outlook
+Community Pract
+Comp Biochem Physiol
+Comp Biochem Physiol A
+Comp Biochem Physiol A Mol Integr Physiol
+Comp Biochem Physiol A Physiol
+Comp Biochem Physiol B
+Comp Biochem Physiol B Biochem Mol Biol
+Comp Biochem Physiol Biochem Mol Biol
+Comp Biochem Physiol C
+Comp Biochem Physiol C Pharmacol Toxicol Endocrinol
+Comp Biochem Physiol C Toxicol Pharmacol
+Comp Biochem Physiol Comp Physiol
+Comp Biochem Physiol Pharmacol Toxicol Endocrinol
+Comp Biochem Physiol Physiol
+Comp Econ Stud
+Comp Educ Rev
+Comp Gen Pharmacol
+Comp Hepatol
+Comp Immunol Microbiol Infect Dis
+Comp Int Law J South Afr
+Comp Labor Law J
+Comp Med
+Comp Med East West
+Comp Polit
+Comp Polit Stud
+Comp Soc Res
+Comp Stud Soc Hist
+Comp Urban Res
+Compare
+Compend Contin Educ Dent
+Compend Contin Educ Dent Suppl
+Compend Contin Educ Gen Dent
+Compend Suppl
+Compendium
+Compens Benefits Rev
+Compens Rev
+Complement
+Complement Inflamm
+Complement Ther Clin Pract
+Complement Ther Med
+Complement Ther Nurs Midwifery
+Complexity
+Complicat Card Patient
+Compost Sci Util
+Compr Gerontol [A]
+Compr Gerontol [B]
+Compr Gerontol [C]
+Compr Immunol
+Compr Ophthalmol Update
+Compr Psychiatry
+Compr Ther
+Comput Aided Surg
+Comput Appl Biosci
+Comput Biol Chem
+Comput Biol Med
+Comput Biomed Res
+Comput Cardiol
+Comput Chem
+Comput Environ Urban Syst
+Comput Geosci
+Comput Graph
+Comput Graph (ACM)
+Comput Healthc
+Comput Hosp
+Comput Hum
+Comput Human Behav
+Comput Inform Nurs
+Comput Law J
+Comput Math Appl
+Comput Med Imaging Graph
+Comput Methods Biomech Biomed Engin
+Comput Methods Programs Biomed
+Comput Nurs
+Comput Phys Commun
+Comput Programs Biomed
+Comput Radiol
+Comput Support Coop Work
+Comput Tomogr
+Comput Vis Image Underst
+Computertomographie
+Computerworld
+Comunita
+Concepts Immunopathol
+Concern
+Concern (Anaheim)
+Concern Care Aging
+Concern Dying
+Concerned Demogr
+Concienc Latinoam
+Concordia Hist Inst Q
+Concordia J
+Concours Med
+Cond Reflex
+Conditio Jud
+Condorcet Stud
+Conector
+Conf Board Rec
+Conf Estud Hist Organ Cienc
+Conf Hist Med
+Conf Lyon Ophtalmol
+Confed Aust Crit Care Nurses J
+Configurations
+Confin Neurol
+Confin Psychiatr
+Congenit Anom (Kyoto)
+Congest Heart Fail
+Congr Int Stomatol
+Congr Q Wkly Rep
+Congr Rec (Dly Ed)
+Congr. Mon
+Conn Dent Stud J
+Conn Gen Statut Annot Conn
+Conn Health Bull
+Conn Hist Soc Bull
+Conn J Int Law
+Conn Law Rev
+Conn Med
+Conn Nurs News
+Conn Probate Law J
+Connaiss Arts
+Connect Tissue Res
+Conscience
+Conscious Cogn
+Consens Dev Conf Summ Natl Inst Health
+Consens Statement
+Conserv Biol
+Conserv Jud
+Consommation
+Const Comment
+Const Law J
+Constellations
+Constitution
+Consult Pharm
+Consult Specif Eng
+Consultant
+Consum Health Perspect
+Consum Mark Abroad
+Consum Rep
+Consum Rep Health
+Cont Lens Anterior Eye
+Contact
+Contact Dermatitis
+Contact Intraocul Lens Med J
+Contact Point
+Contactologia
+Contam Control
+Contam Control Biomed Environ
+Contemp Adm
+Contemp Adm Long Term Care
+Contemp Anesth Pract
+Contemp Clin Trials
+Contemp Crisis
+Contemp Drug Probl
+Contemp Econ Policy
+Contemp Educ Psychol
+Contemp Eur Hist
+Contemp Fr Civiliz
+Contemp Intern Med
+Contemp Issues Clin Biochem
+Contemp Jew
+Contemp Longterm Care
+Contemp Marx
+Contemp Neurol Ser
+Contemp Nurse
+Contemp Ob Gyn
+Contemp Oncol
+Contemp Orthop
+Contemp Pac
+Contemp Pediatr
+Contemp Pharm Pract
+Contemp Philos
+Contemp Policy Issues
+Contemp Psychoanal
+Contemp Rev
+Contemp Rev Obstet Gynaecol
+Contemp Sociol
+Contemp Southeast Asia
+Contemp Surg
+Contemp Ther
+Contemp Top Immunobiol
+Contemp Top Lab Anim Sci
+Contemp Top Mol Immunol
+Contemp Urol
+Contin Care
+Contin Chang
+Continuity
+Continuum
+Continuum (N Y)
+Continuum Soc Soc Work Leadersh Health Care
+Contracept Deliv Syst
+Contracept Fertil Sex
+Contracept Fertil Sex (Paris)
+Contracept Rep
+Contracept Technol
+Contracept Technol Update
+Contraception
+Contract
+Contract Des
+Contract Healthc
+Contract Inter
+Contradictions
+Contrastes
+Contrat Soc
+Contree
+Contrepoint
+Contrib Asian Stud
+Contrib Biol Geol
+Contrib Embryol
+Contrib Gynecol Obstet
+Contrib Hist Econ Soc
+Contrib Indian Sociol
+Contrib Med Stud
+Contrib Microbiol
+Contrib Microbiol Immunol
+Contrib Mineral Petrol
+Contrib Nepalese Stud
+Contrib Nephrol
+Contrib Polit Econ
+Contrib Primatol
+Contrib Psychol
+Contrib Sens Physiol
+Contributi
+Control Clin Trials
+Control Eng Pract
+Convuls Ther
+Cooley Law Rev
+Coop Confl
+Coop Dent (B Aires)
+Coord Noteb
+Copeia
+Cor Vasa
+Core J Pediatr
+Cornea
+Cornell Hotel Restaur Adm Q
+Cornell Int Law J
+Cornell J Law Public Policy
+Cornell J Soc Relat
+Cornell Law Rev
+Cornell Mag
+Cornell Vet
+Coron Artery Dis
+Corp Comment
+Corps Med (Ettelbruck)
+Corr Farm
+Correct Soc Psych J Behav Tech Methods Ther
+Correctcare
+Correo Poblac Salud
+Cortex
+Cosmetologica
+Cost Containment
+Cost Eff Resour Alloc
+Cost Qual
+Cost Qual Q J
+Cough
+Couns Psychol
+Couns Psychol Q
+Couns Values
+Countdown Istanb
+Courier
+Courr Pays Est
+Courr Unesco
+Courrier
+Courts Health Sci Law
+Covertaction Q
+Coyunt Econ
+Cpr Popul Res
+Cr Trav Lab Carlsberg Ser Physiol
+Cranio
+Cranio Clin Int
+Creat Nurs
+Creces
+Creighton Law Rev
+Crianca Port
+Crim Behav Ment Health
+Crim Justice Behav
+Crim Justice Ethics
+Crim Justice Hist
+Crim Justice J
+Crim Law Bull
+Crim Law Forum
+Crim Law J
+Crim Law Rev
+Crim Rep Can New Ser
+Crime Delinq
+Crime Law Soc Change
+Crime Soc Justice
+Crisis
+Crit Anthropol
+Crit Care
+Crit Care Clin
+Crit Care Med
+Crit Care Nurs Clin North Am
+Crit Care Nurs Q
+Crit Care Nurse
+Crit Care Resusc
+Crit Care Update
+Crit Inq
+Crit Path AIDS Proj
+Crit Rev Biochem Mol Biol
+Crit Rev Bioeng
+Crit Rev Biomed Eng
+Crit Rev Biotechnol
+Crit Rev Clin Lab Sci
+Crit Rev Comput Tomogr
+Crit Rev Diagn Imaging
+Crit Rev Eukaryot Gene Expr
+Crit Rev Food Sci Nutr
+Crit Rev Immunol
+Crit Rev Med Inform
+Crit Rev Microbiol
+Crit Rev Neurobiol
+Crit Rev Oncog
+Crit Rev Oncol Hematol
+Crit Rev Oral Biol Med
+Crit Rev Ther Drug Carrier Syst
+Crit Rev Toxicol
+Crit Soc Res
+Crit Sociol
+Crit Stor
+Crit Stud Mass Commun
+Criterion
+Critica
+Critique
+Croat Med J
+Cron IDI
+Cron Pompeiane
+Crop Sci
+Cross Curr
+Cross Curr (Ann Arbor MI)
+Crossref Hum Resour Manage
+Crossreference
+Crown Agents Rev
+Crucible
+Crux
+Cryo Letters
+Cryobiology
+Ctry Demogr Profiles
+Ctry Life
+Cuad Am
+Cuad Arquit Urban
+Cuad CENDES
+Cuad CLAEH
+Cuad Complut Hist Med Cienc
+Cuad Comun AMIDEP
+Cuad Econ
+Cuad Econ Soc
+Cuad Estud Gallegos
+Cuad Filol Cl
+Cuad Hisp Hist Med Cienc
+Cuad Hispanoam
+Cuad Hist Esp
+Cuad Hist Med Esp
+Cuad Hist Salud Publica
+Cuad Invest Hist
+Cuad Med Soc
+Cuad Nutr
+Cuad Valencia Hist Med Cienc
+Cuban Stud
+Cult Anthropol
+Cult Confl
+Cult Dev
+Cult Divers Ment Health
+Cult Health Sex
+Cult Med
+Cult Med Psychiatry
+Cult Sc
+Cult Surv Q
+Cult Tech
+Culta Bononia
+Cultur Divers Ethnic Minor Psychol
+Culture
+Cumberl Cty Hist
+Cumberland Law Rev
+Cumberland Samford Law Rev
+Cuore Circ
+Curare
+Curationis
+Curr Aff Bull
+Curr Alcohol
+Curr Allergy Asthma Rep
+Curr Allergy Rep
+Curr Alzheimer Res
+Curr Anaesth Crit Care
+Curr Anthropol
+Curr Atheroscler Rep
+Curr Bibliogr Afr Aff
+Curr Biol
+Curr Cancer Drug Targets
+Curr Cardiol Rep
+Curr Clin Top Infect Dis
+Curr Conc Cerebrovasc Dis Stroke
+Curr Concepts Hosp Pharm Manage
+Curr Concepts Nutr
+Curr Contents Clin Med
+Curr Control Trials Cardiovasc Med
+Curr Dev Psychopharmacol
+Curr Diab Rep
+Curr Dig Post Sov Press
+Curr Dig Sov Press
+Curr Dir Autoimmun
+Curr Drug Deliv
+Curr Drug Discov Technol
+Curr Drug Metab
+Curr Drug Targets
+Curr Drug Targets CNS Neurol Disord
+Curr Drug Targets Cardiovasc Haematol Disord
+Curr Drug Targets Immune Endocr Metabol Disord
+Curr Drug Targets Infect Disord
+Curr Drug Targets Inflamm Allergy
+Curr Eye Res
+Curr Gastroenterol Rep
+Curr Gene Ther
+Curr Genet
+Curr HIV Res
+Curr HIV/AIDS Rep
+Curr Heart Fail Rep
+Curr Hematol Rep
+Curr Hist
+Curr Hypertens Rep
+Curr Infect Dis Rep
+Curr Interv Cardiol Rep
+Curr Issues Intest Microbiol
+Curr Issues Mol Biol
+Curr Issues Public Health
+Curr Law Statut Annot GB
+Curr Leg Probl
+Curr Med Atty
+Curr Med Chem
+Curr Med Chem Anticancer Agents
+Curr Med Chem Cardiovasc Hematol Agents
+Curr Med Drugs
+Curr Med Pract
+Curr Med Res Opin
+Curr Microbiol
+Curr Mod Biol
+Curr Mol Med
+Curr Neurol Neurosci Rep
+Curr Neurovasc Res
+Curr Obstet Gynaecol
+Curr Oncol Rep
+Curr Opin Allergy Clin Immunol
+Curr Opin Anaesthesiol
+Curr Opin Biotechnol
+Curr Opin Cardiol
+Curr Opin Cell Biol
+Curr Opin Chem Biol
+Curr Opin Clin Nutr Metab Care
+Curr Opin Cosmet Dent
+Curr Opin Crit Care
+Curr Opin Dent
+Curr Opin Drug Discov Devel
+Curr Opin Gastroenterol
+Curr Opin Gen Surg
+Curr Opin Genet Dev
+Curr Opin Hematol
+Curr Opin Immunol
+Curr Opin Infect Dis
+Curr Opin Investig Drugs
+Curr Opin Lipidol
+Curr Opin Microbiol
+Curr Opin Mol Ther
+Curr Opin Nephrol Hypertens
+Curr Opin Neurobiol
+Curr Opin Neurol
+Curr Opin Neurol Neurosurg
+Curr Opin Obstet Gynecol
+Curr Opin Oncol
+Curr Opin Ophthalmol
+Curr Opin Orthop
+Curr Opin Otolaryngol Head Neck Surg
+Curr Opin Pediatr
+Curr Opin Periodontol
+Curr Opin Pharmacol
+Curr Opin Plant Biol
+Curr Opin Psychiatry
+Curr Opin Pulm Med
+Curr Opin Radiol
+Curr Opin Rheumatol
+Curr Opin Struct Biol
+Curr Opin Urol
+Curr Orthop
+Curr Osteoporos Rep
+Curr Pain Headache Rep
+Curr Pharm Biotechnol
+Curr Pharm Des
+Curr Popul Rep Consum Income
+Curr Popul Rep Popul Charact
+Curr Popul Rep Popul Estim Proj
+Curr Popul Rep [Spec Censuses]
+Curr Pract Gerontol Nurs
+Curr Pract Obstet Gynecol Nurs
+Curr Pract Orthop Surg
+Curr Pract Pediatr Nurs
+Curr Prescr
+Curr Probl Cancer
+Curr Probl Cardiol
+Curr Probl Clin Biochem
+Curr Probl Dermatol
+Curr Probl Diagn Radiol
+Curr Probl Obstet Gynecol
+Curr Probl Obstet Gynecol Fertil
+Curr Probl Pediatr
+Curr Probl Pediatr Adolesc Health Care
+Curr Probl Sov Med
+Curr Probl Surg
+Curr Protein Pept Sci
+Curr Psychiatr Ther
+Curr Psychiatry Rep
+Curr Psychol
+Curr Psychol Cogn
+Curr Res Anesth Analg
+Curr Res Occup Prof
+Curr Rev Pain
+Curr Rheumatol Rep
+Curr Sci
+Curr Sep
+Curr Sociol
+Curr Sports Med Rep
+Curr Stud Hematol Blood Transfus
+Curr Stud Nat Brain Funct
+Curr Surg
+Curr Theol Mission
+Curr Ther (Seaforth)
+Curr Ther Endocrinol Metab
+Curr Ther Res Clin Exp
+Curr Top Cell Regul
+Curr Top Comp Pathobiol
+Curr Top Dev Biol
+Curr Top Exp Endocrinol
+Curr Top Eye Res
+Curr Top Hematol
+Curr Top Med Chem
+Curr Top Med Mycol
+Curr Top Microbiol Immunol
+Curr Top Mol Endocrinol
+Curr Top Pathol
+Curr Top Radiat Res Q
+Curr Treat Options Cardiovasc Med
+Curr Treat Options Gastroenterol
+Curr Treat Options Neurol
+Curr Treat Options Oncol
+Curr Urol Rep
+Curr Vasc Pharmacol
+Curr Womens Health Rep
+Curr World Lead
+Current
+Curso Int Oftalmol
+Cutis
+Cyberpsychol Behav
+Cylchgrawn Llyfrgell Genedlaethol Cymru
+Cyprus Med J
+Cytobiologie
+Cytobios
+Cytogenet Cell Genet
+Cytogenet Genome Res
+Cytogenetics
+Cytojournal
+Cytokine
+Cytokine Growth Factor Rev
+Cytokines
+Cytokines Cell Mol Ther
+Cytokines Mol Ther
+Cytologia (Tokyo)
+Cytometry
+Cytometry A
+Cytometry B Clin Cytom
+Cytometry Suppl
+Cytopathology
+Cytotechnology
+Cytotherapy
+Czas Geogr
+Czas Praw Hist
+Czas Stomatol
+Czech Econ Dig
+Czech Med
+Czech Sociol Rev
+Czechoslov Sociol Rev
+D Cent Am
+D Yucatan
+DA West Asia Rep
+DAR Mag
+DDZ
+DE J Dent Eng
+DEEP Dev Educ Exch Pap
+DEF
+DGHS Chron
+DHS Dimens
+DICP
+DLR Nachr
+DNA
+DNA Cell Biol
+DNA Repair (Amst)
+DNA Res
+DNA Seq
+DP Rep
+DRG Monit
+Dacca Univ Stud A
+Daedalus
+Dakar Med
+Dalhous Law J
+Dalhousie Dent J
+Dalhousie Rev
+Dalton Trans
+Dan Amtsrad
+Dan Med Bull
+Dan Medicinhist Arbog
+Dan Tidsskr Farm
+Daphnis
+Dapim Refuiim
+Data Asia
+Data Bull (Cent Stud Health Syst Change)
+Data Strateg Benchmarks
+Data User News
+Database
+Dauphin Cty Rep
+De Economia
+De Paul Law Rev
+DePaul J Health Care Law
+Deans List
+Deans Notes
+Death Educ
+Death Stud
+Debat
+Deccan Geogr
+Decisions
+Decubitus
+Deep Sea Res A
+Def Couns J
+Def Law J
+Def Natl
+Def Sci J
+Defenders
+Dejiny Ved Tech
+Del Code Annot Del
+Del Hist
+Del Med J
+Del Nurse
+Delft Prog Rep
+Delt Hell Mikrobiol Hygieinol Hetair
+Delt Paidiatr Klin Panepistem Athenon
+Deltion / Iatrocheirourgikes Hetaireias Athenon
+Dement Geriatr Cogn Disord
+Dementia
+Democracy
+Demogr Afr
+Demogr Econ
+Demogr Hist Bull Inf Soc
+Demogr India
+Demogr Inf
+Demogr Issled
+Demogr Res
+Demogr Sveske
+Demografia
+Demografie
+Demography
+Demohrafichni Doslidzhennia
+Demos
+Demosta
+Dens
+Dens (Curitiba)
+Dens Sapiens
+Dent
+Dent Abstr
+Dent Anaesth Sedat
+Dent Angles
+Dent Assist
+Dent Assist (Waco Tx)
+Dent Assist J
+Dent Cadmos
+Dent Clin North Am
+Dent Concepts
+Dent Delin
+Dent Dialogue
+Dent Dienst
+Dent Dig
+Dent Dimens
+Dent Discourse
+Dent Echo (Heidelb)
+Dent Econ
+Dent Health (London)
+Dent Hist
+Dent Hyg (Chic)
+Dent Hyg (San Franc)
+Dent Images
+Dent Implantol Update
+Dent Items Interest
+Dent J
+Dent J Aust
+Dent J Malays
+Dent J Malaysia Singapore
+Dent J Zamb
+Dent Jpn (Tokyo)
+Dent Lab Bl
+Dent Lab Manage Today
+Dent Lab Rev
+Dent Labor (Munch)
+Dent Mag Oral Top
+Dent Manage
+Dent Mater
+Dent Mater J
+Dent Mirror (Atlanta)
+Dent Mirror (Quezon City)
+Dent News
+Dent News (Lond)
+Dent Off
+Dent Outlook
+Dent Pract
+Dent Pract (Cincinnati)
+Dent Pract (Ewell)
+Dent Pract Dent Rec
+Dent Pract Manage
+Dent Press
+Dent Prog (Chic)
+Dent Radiogr Photogr
+Dent Rec (London)
+Dent Res Grad Study
+Dent Rev
+Dent Rundsch
+Dent Sch Q
+Dent Stud
+Dent Surv
+Dent Team
+Dent Teamwork
+Dent Tech
+Dent Ther Newsl
+Dent Today
+Dent Traumatol
+Dent Update
+Dent World
+Dentago
+Dentalhygienistnews
+Dentalpractice
+Dentessence
+Dentist
+Dentistry
+Dentistry (Loma Linda)
+Dentisuto
+Dentomaxillofac Radiol
+Dentomaxillofac Radiol Suppl
+Dentoral (Istanbul)
+Dentoscope
+Denver J Int Law Policy
+Denver Law J
+Denver Univ Law Rev
+Denver West Roundup
+Dep State Bull
+Depress Anxiety
+Depression
+Derm Beruf Umwelt
+Dermatitis
+Dermatol Clin
+Dermatol Iber Lat Am
+Dermatol Int
+Dermatol Monatsschr
+Dermatol Nurs
+Dermatol Online J
+Dermatol Surg
+Dermatol Ther
+Dermatol Trop Ecol Geogr
+Dermatol Wochenschr
+Dermatologica
+Dermatology
+Dermatovenerologia
+Desarro Base
+Desarro Rural Amer
+Desarro Soc
+Desarrollo Econ
+Deseret News
+Desertif Control
+Desmos
+Detroit Coll Law Rev
+Detroit Dent Bull
+Detroit Perspect
+Dev Biol
+Dev Biol (Basel)
+Dev Biol (N Y 1985)
+Dev Biol Stand
+Dev Brief
+Dev Bull
+Dev Cell
+Dev Change
+Dev Civilis
+Dev Commun Rep
+Dev Comp Immunol
+Dev Dialogue
+Dev Dig
+Dev Dir
+Dev Dyn
+Dev Econ
+Dev Forum
+Dev Gend Brief
+Dev Genes Evol
+Dev Genet
+Dev Growth Differ
+Dev Health Econ Public Policy
+Dev Immunol
+Dev Int
+Dev Med Child Neurol
+Dev Med Child Neurol Suppl
+Dev Neuropsychol
+Dev Neurosci
+Dev Ophthalmol
+Dev Peace
+Dev Pharmacol Ther
+Dev Policy Rev
+Dev Pract
+Dev Psychobiol
+Dev Psychol
+Dev Psychopathol
+Dev Que
+Dev Rev
+Dev Sante
+Dev Sci
+Dev Soc
+Dev South Afr
+Dev Suppl
+Dev Toxicol Environ Sci
+Dev Volta
+Developing World Bioeth
+Development
+Deviant Behav
+Devon Assoc Adv Sci
+Devon Hist
+Dhaka Univ Stud B Biol Stud
+Di Yi Jun Yi Da Xue Xue Bao
+Dia Med
+Diab Vasc Dis Res
+Diabet Med
+Diabete
+Diabete Metab
+Diabetes
+Diabetes Care
+Diabetes Educ
+Diabetes Forecast
+Diabetes Metab
+Diabetes Metab Res Rev
+Diabetes Metab Rev
+Diabetes Nutr Metab
+Diabetes Obes Metab
+Diabetes Res
+Diabetes Res Clin Pract
+Diabetes Res Clin Pract Suppl
+Diabetes Self Manag
+Diabetes Technol Ther
+Diabetol Croat
+Diabetologia
+Diagn Afr
+Diagn Clin Immunol
+Diagn Cytopathol
+Diagn Gynecol Obstet
+Diagn Histopathol
+Diagn Imaging
+Diagn Imaging (San Franc)
+Diagn Imaging Clin Med
+Diagn Immunol
+Diagn Interv Radiol
+Diagn Microbiol Infect Dis
+Diagn Mol Pathol
+Diakonia
+Dial Transplant
+Dialect Anthropol
+Dialect Hum
+Dialog
+Dialog Fairleigh Dickinson Univ Sch Dent
+Dialogue
+Dialogue Can Philos Assoc
+Dialogue Diarrhoea
+Dialogues Clin Neurosci
+Dialogues Contracept
+Dialoog
+Diamond
+Diarrhoea Dialogue
+Diastema
+Dickensian
+Dickinson Law Rev
+Dicle Univ Tip Fakul Derg
+Dif Soc
+Differences
+Differentiation
+Dig Antibiot
+Dig Dis
+Dig Dis Sci
+Dig Liver Dis
+Dig Surg
+Digest Public Gen Bills
+Digestion
+Digitale Bilddiagn
+Dimens Crit Care Nurs
+Dimens Health Serv
+Dimens Oncol Nurs
+Dimension
+Dimensions (Wash)
+Diogenes
+Diotima
+Dir Boards
+Dir Lav
+Dirasat Med Biol Sci
+Dirasat Sukkaniyah
+Director
+Dirim
+Dis Aquat Organ
+Dis Chest
+Dis Colon Rectum
+Dis Esophagus
+Dis Manag
+Dis Manag Advis
+Dis Markers
+Dis Mon
+Dis Nerv Syst
+Disabil Handicap Soc
+Disabil Rehabil
+Disabil Soc
+Disaster Manag Response
+Disasters
+Disch Plann Update
+Discov Innov
+Discover
+Discuss Faraday Soc
+Diskussionsforum Med Ethik
+Displays
+Diss Abstr
+Dissent
+Dist Nurs
+Divulg Cult Odontol
+Dix Huit Siecle
+Dly Mail Guard
+Dly Rep China
+Dly Rep Peoples Repub China
+Dly Stat Can
+Dly Times
+Dly Wash Law Report
+Doc Anal Geogr
+Doc Geigy Acta Psychosom (Dtsch Ausg)
+Doc Geigy Acta Rheumatol
+Doc Hist Vocab Sci
+Doc Med Ethics
+Doc Med Geogr Trop
+Doc Neerl Indones Morbis Trop
+Doc Ophthalmol
+Doc Ophthalmol Proc Ser
+Doc Rheumatol
+Documents
+Dodone
+Dok Hoechst Arch
+Dokl Akad Nauk
+Dokl Akad Nauk SSSR
+Dokl Biochem
+Dokl Biochem Biophys
+Dokl Biol Sci
+Dokl Biophys
+Dokl Bulg Acad Nauk
+Dolentium Hominum
+Dom Law Rep
+Domes
+Domest Anim Endocrinol
+Donauraum
+Dong Wu Xue Bao
+Downs Syndr Res Pract
+Drake Law Rev
+Draper Fund Rep
+Draper World Popul Fund Rep
+Dritte Welt
+Droit Soc
+Drosoph Inf Serv
+Drug Alcohol Depend
+Drug Alcohol Rev
+Drug Chem Toxicol
+Drug Cosmet Ind
+Drug Deliv
+Drug Des Deliv
+Drug Des Discov
+Drug Dev Ind Pharm
+Drug Dev Res
+Drug Discov Today
+Drug Forum
+Drug Inf J
+Drug Intell Clin Pharm
+Drug Merch
+Drug Metab Dispos
+Drug Metab Pharmacokinet
+Drug Metab Rev
+Drug Metabol Drug Interact
+Drug News Perspect
+Drug Nutr Interact
+Drug Res
+Drug Res Rep
+Drug Resist Updat
+Drug Saf
+Drug Stand
+Drug Ther
+Drug Ther (NY)
+Drug Ther Bull
+Drug Topics
+Drug Trade News
+Drugs
+Drugs Aging
+Drugs Exp Clin Res
+Drugs Made Ger
+Drugs R D
+Drugs Soc (New York)
+Drugs Today (Barc)
+Dtsch Apoth
+Dtsch Apoth Ztg
+Dtsch Arch Klin Med
+Dtsch Arztebl
+Dtsch Aussenpolit
+Dtsch Gesundheit
+Dtsch Gesundheitspolit
+Dtsch Gesundheitsw
+Dtsch Jahrb Volkskd
+Dtsch Krankenpflegez
+Dtsch Lebensmitt Rundsch
+Dtsch Med J
+Dtsch Med Wochenschr
+Dtsch Rentenversicher
+Dtsch Schiffahrtsarch
+Dtsch Schwesternztg
+Dtsch Stomatol
+Dtsch Tierarztl Wochenschr
+Dtsch Vierteljahresschr Litt Wiss Geistesgesch
+Dtsch Z Gesamte Gerichtl Med
+Dtsch Z Mund Kiefer Gesichtschir
+Dtsch Z Nervenheilkd
+Dtsch Z Philos
+Dtsch Z Sportmed
+Dtsch Z Verdau Stoffwechselkr
+Dtsch Zahn Mund Kieferheilkd Zentralbl
+Dtsch Zahn Mund Kieferheilkd Zentralbl Gesamte
+Dtsch Zahnarztl Z
+Dtsch Zentralbl Krankenpfl
+Dtschl Arch
+Dubl Univ Law J
+Dublin Hist Rec
+Dubrovnik
+Duke J Gend Law Policy
+Duke Law J
+Duke Law Technol Rev
+Dunel Notes
+Duodecim
+Duodecim Suppl
+Duquesne Law Rev
+Durham Univ J
+Durius
+Dusseld Arb Gesch Med
+Dusseld Arb Gesch Med Beih
+Dusseld Geogr Schr
+Dyn Med
+Dyn Psychiatr
+Dynamics
+Dynamis
+Dyny Ysrl
+Dyslexia
+Dysphagia
+E
+EBRI Issue Brief
+ED Manag
+EDS Mag
+EDTNA ERCA J
+EEG EMG Z Elektroenzephalogr Elektromyogr Verwandte Geb
+EHP Toxicogenomics
+EITV
+EMBO J
+EMBO Rep
+EMT J
+ENLB Emerg Nurse Leg Bull
+ENO FO
+EOS
+EPI Newsl
+ERS Spectr
+ESA Bull
+ESA J
+ESRC Data Arch Bull
+ET J
+EURE
+EURO Rep Stud
+EXS
+Ear Hear
+Ear Nose Throat J
+Early Am Lit
+Early Child Dev Care
+Early Child Res Q
+Early Educ Dev
+Early Hum Dev
+Early Pregnancy
+Early Sci Med
+Earth (Waukesha)
+Earth Miner Sci
+Earth Moon Planets
+Earth Negot Bull
+Earth Planet Sci Lett
+Earth Sci Hist
+Earth Space
+Earth Space Rev
+Earthwatch
+East Afr Econ Rev
+East Afr Geogr Rev
+East Afr J Rural Dev
+East Afr Med J
+East Afr Soc Sci Res Rev
+East Anthropol
+East Cent Eur
+East Econ J
+East Eur Jew Aff
+East Eur Polit Soc
+East Eur Q
+East Europ Econ
+East Horiz
+East Mediterr Health J
+East South Afr Geogr J
+East Tenn Soc Publ
+Eat Behav
+Eat Weight Disord
+Ebony
+Echo Med Cevennes
+Echo Med Nord
+Echocardiography
+Echoes
+Ecol Appl
+Ecol Dis
+Ecol Econ
+Ecol Eng
+Ecol Entomol
+Ecol Food Nutr
+Ecol Law Q
+Ecol Lett
+Ecol Modell
+Ecol Monogr
+Ecologist
+Ecology
+Econ Bot
+Econ Bull Asia Pac
+Econ Bull Ghana
+Econ Comput Econ Cybern Stud Res
+Econ Desarro
+Econ Educ Rev
+Econ Eye
+Econ Geogr
+Econ Geol
+Econ Hist (Sweden)
+Econ Hist Rev
+Econ Hum Biol
+Econ Humanisme
+Econ Inq
+Econ J Nepal
+Econ Lav
+Econ Lett
+Econ Med Anim
+Econ Merid
+Econ Model
+Econ Outlook USA
+Econ Philos
+Econ Polit Wkly
+Econ Prospect Int
+Econ Pubblica
+Econ Rec
+Econ Reun
+Econ Rev
+Econ Soc
+Econ Soc Hist Jaarb
+Econ Soc Rev (Irel)
+Econ Sociol
+Econ Stat
+Econ Stat Ber
+Econ Stor
+Econometrica
+Economia
+Economia Umana Rassegna Medica Internazionale
+Economica
+Economist
+Economist (Leiden)
+Ecotoxicol Environ Saf
+Ecotoxicology
+Ecrits Paris
+Ecum Rev
+Edcentric
+Edinb Dent Hosp Gaz
+Edinburgh Med J
+Editor Res Rep
+Educ Adm Q
+Educ Broadcast Int
+Educ Dent (Ica)
+Educ Dir Dent Aux
+Educ Dir Dent Hyg
+Educ Forum
+Educ Gerontol
+Educ Health (Abingdon)
+Educ Ind Telev
+Educ Leadersh
+Educ Med Salud
+Educ Psychol Meas
+Educ Rec
+Educ Stud
+Educ Theory
+Educ Train Ment Retard
+Educ Update
+Educ Urban Soc
+Educafrica
+Eesti Nsv Tead Akad TOIM Biol
+Eff Clin Pract
+Eff Health Care
+Effluent Water Treat J
+Egeszsegtudomany
+Egypt Dent J
+Egypt J Bilharz
+Egypt J Immunol
+Egypt J Psychiatry
+Egypt Med J
+Egypt Popul Fam Plann Rev
+Egypte Contemp
+Egypte Monde Arabe
+Eichsfelder Heimath
+Eicosanoids
+Eighteenth Century (Lubbock)
+Eighteenth Century Irel
+Eighteenth Century Life
+Eighteenth Century Stud
+Einstein Q J Biol Med
+Eire Irel
+Eisei Dobutsu
+Eisei Shikenjo Hokoku
+Ekistics Probl Sci Hum Settl
+Ekologiia
+Ekon Cas
+Ekon Keuangan Indones
+Ekon Misao
+Ekon Pregl
+Ekon Revija
+Ekon Sov Ukr
+Eksp Khir Anesteziol
+Eksp Khirurgiia
+Eksp Klin Farmakol
+Eksp Klin Gastroenterol
+Eksp Med Morfol
+Eksp Onkol
+Elder Care
+Elder Law J
+Electrochim Acta
+Electrodiagn Ther
+Electroencephalogr Clin Neurophysiol
+Electroencephalogr Clin Neurophysiol Suppl
+Electromagn Biol Med
+Electromyogr Clin Neurophysiol
+Electromyography
+Electron Microsc Rev
+Electrophoresis
+Elektromed Biomed Tech
+Embryologia (Nagoya)
+Eme Eme Estud Domin
+Emerg Dep News
+Emerg Health Serv Q
+Emerg Health Serv Rev
+Emerg Infect Dis
+Emerg Med
+Emerg Med (Fremantle)
+Emerg Med Australas
+Emerg Med Clin North Am
+Emerg Med J
+Emerg Med Rep
+Emerg Med Serv
+Emerg Nurse
+Emerg Plann Dig
+Emerg Radiol
+Emerg Themes Epidemiol
+Emergency
+Emerita
+Emisor Demogr
+Emory Int Law Rev
+Emory Law J
+Emory Mag
+Emotion
+Emp State Rep
+Emphasis Nurs
+Empir Econ
+Empl Benefits J
+Empl Desempl
+Empl Health Fit
+Employ Relat Today
+Employee Benefit Plan Rev
+Employee Relat Law J
+Emporia State Res Stud
+Enantiomer
+Encephale
+Encounter
+Encounter (Lond)
+Endeavour
+Endocr Dev
+Endocr J
+Endocr Metab Immune Disord Drug Targets
+Endocr Pathol
+Endocr Pract
+Endocr Regul
+Endocr Relat Cancer
+Endocr Res
+Endocr Res Commun
+Endocr Rev
+Endocr Terap
+Endocrine
+Endocrinol Exp
+Endocrinol Jpn
+Endocrinol Metab Clin North Am
+Endocrinol Sci Cost
+Endocrinologie
+Endocrinologist
+Endocrinology
+Endod Dent Traumatol
+Endod Prac
+Endod Rep
+Endodoncia
+Endodoncia (Mex)
+Endokrinologie
+Endokrynol Diabetol Chor Przemiany Materii Wieku Rozw
+Endokrynol Pol
+Endosc Surg Allied Technol
+Endoscopy
+Endothelium
+Energy Fuels
+Energy Policy
+Enfance
+Enfant Milieu Trop
+Enferm Infecc Microbiol Clin
+Enferm Intensiva
+Enferm Torax
+Enfermeras
+Enfermeria
+Enfoque
+Enfoque Fem
+Enfoques Aten Prim
+Eng Med
+Eng Sci
+Engage Soc Action
+Engl Hist Rev
+Engl Lang Notes
+Enlace
+Enquetes Mus Vie Wallonne
+Entechnology
+Entomol Exp Appl
+Entomol News
+Entre Nous Cph Den
+Entret Bichat Med Entret Bichat
+Entret Bichat Pitie Salpetriere Ther
+Entscheid Bundesgerichtshofes Zivilsachen
+Env Polit
+Environ Aciton
+Environ Aff
+Environ Afr
+Environ Behav
+Environ Biol Med
+Environ Biosafety Res
+Environ Change Secur Proj Rep
+Environ Conserv
+Environ Educ Inf
+Environ Entomol
+Environ Ethics
+Environ Exp Bot
+Environ Folio
+Environ Geochem Health
+Environ Health
+Environ Health Perspect
+Environ Health Ser [Radiol Health]
+Environ Hist Rev
+Environ Impact Assess Rev
+Environ Int
+Environ Law
+Environ Lett
+Environ Manage
+Environ Med
+Environ Microbiol
+Environ Mol Mutagen
+Environ Monit Assess
+Environ Mutagen
+Environ Physiol Biochem
+Environ Plan A
+Environ Plan D
+Environ Plann B Plann Des
+Environ Plann C Gov Policy
+Environ Pollut
+Environ Qual Annu Rep Counc Environ Qual
+Environ Qual Saf
+Environ Qual Saf Suppl
+Environ Res
+Environ Rev
+Environ Sci
+Environ Sci Pollut Res Int
+Environ Sci Technol
+Environ Technol
+Environ Toxicol
+Environ Toxicol Chem
+Environ Urban
+Environ Values
+Environment
+Environmentalist
+Environments
+Enzyme
+Enzyme Microb Technol
+Enzyme Protein
+Enzymol Biol Clin (Basel)
+Enzymologia
+Eos (Washington DC)
+Epa J
+Epatologia
+Epeirotike Hestia
+Epet Etaireias Stereoelladikon Meleton
+Epheta
+Epidemiol Bull
+Epidemiol Community Health
+Epidemiol Infect
+Epidemiol Mikrobiol Imunol
+Epidemiol Perspect Innov
+Epidemiol Prev
+Epidemiol Psichiatr Soc
+Epidemiol Rev
+Epidemiology
+Epigraph Stud
+Epigraphica
+Epilepsia
+Epilepsy Behav
+Epilepsy Curr
+Epilepsy Res
+Epilepsy Res Suppl
+Epileptic Disord
+Epione
+Episteme
+Epithelial Cell Biol
+Epitheor Koin Ereun
+Epopteia
+Equilib Res
+Equilibrium
+Equine Vet J
+Equine Vet J Suppl
+Equinoxe
+Era Social
+Erde
+Erdkd Wissen
+Erdkunde
+Erfahrungsheilkunde
+Ergeb Allg Pathol Pathol Anat
+Ergeb Anat Entwicklungsgesch
+Ergeb Biol
+Ergeb Chir Orthop
+Ergeb Enzymforsch
+Ergeb Gesamten Tuberkuloseforsch
+Ergeb Hyg Bakteriol Immunitatsforsch Exp Ther
+Ergeb Inn Med Kinderheilkd
+Ergeb Mikrobiol Immunitatsforsch Exp Ther
+Ergeb Physiol
+Ergon Des
+Ergonomics
+Ernahrungsforsch Ber Mitt
+Ernst Schering Res Found Workshop
+Erzieh Unterr
+Esencia Odontol
+Esope
+Espace Geogr
+Espace Popul Soc
+Espaces Soc
+Espiral
+Esprit
+Esquire
+Essays Arts Sci
+Essays Biochem
+Essays Fundam Immunol
+Essays Neurochem Neuropharmacol
+Essays Stud (Lond)
+Essence (Downsview)
+Essent Drugs Monit
+Essent Psychopharmacol
+Essex Inst Hist Collect
+Essex J
+Estad Panamena
+Estad Panamena Situac Demogr Secc 231 Migr Migr Int
+Estad Venez
+Estadastica
+Estampille
+Este Pais
+Estodont Press
+Estomatol Cult
+Estomatologia
+Estud Asia Afr
+Estud CEBRAP
+Estud Cent Am
+Estud Cl
+Estud Cult Nahuatl
+Estud Demogr
+Estud Demogr Urbanos Col Mex
+Estud Dep Hist Mod
+Estud Econ
+Estud Fem
+Estud Geogr
+Estud Hist Doc Arxius Protoc
+Estud Hist Novohisp
+Estud Hist Saude
+Estud Hist Soc
+Estud Hist Soc Econ Am
+Estud Latinoam
+Estud Migr Latinoam
+Estud Parag
+Estud Poblac
+Estud Publicos
+Estud Rom (Barcelona)
+Estud Rurales Latinoam
+Estud Segov
+Estud Soc
+Estud Soc Centroam
+Estud Sociol
+Estudios
+Estudis
+Eta Sigma Gamman
+Etc
+Ethical Currents
+Ethical Hum Psychol Psychiatry
+Ethical Hum Sci Serv
+Ethical Perspect
+Ethical Theory Moral Pract
+Ethics
+Ethics Behav
+Ethics Comm Newsl
+Ethics Inf Technol
+Ethics Int Aff
+Ethics Intellect Disabil
+Ethics Med
+Ethics Medics
+Ethics Sci Med
+Ethiop Herald
+Ethiop J Dev Res
+Ethiop Med J
+Ethiop Obs
+Ethique
+Ethn Dis
+Ethn Forum
+Ethn Health
+Ethn Racial Stud
+Ethnic Groups
+Ethnicity
+Ethnographie.
+Ethnohistory
+Ethnol Eur
+Ethnol Fr
+Ethnol Scand
+Ethnology
+Ethnomedizin
+Ethnos
+Ethol Sociobiol
+Ethology
+Ethos
+Etnogr Pol
+Etnol Stud
+Etud Angl
+Etud Balk
+Etud Can
+Etud Class
+Etud Doc Groupe Demogr Afr
+Etud Expans
+Etud Ger
+Etud Hist Afr
+Etud Int
+Etud Inuit
+Etud Irl
+Etud Limousines
+Etud Napoleon
+Etud Polemol
+Etud Rurales
+Etud Soins Serv Infirm
+Etud Togol Popul
+Etud Vauclus
+Etud Zair
+Etude Popul Afr
+Etudes (Paris)
+Etudes Freud
+Etudes Mali
+Etudes Neonatales
+Etudes Rwandaises
+Etudes Stat Inst Natl Stat
+Eubios J Asian Int Bioeth
+Eugen News
+Eugen Q
+Eugen Rev
+Eugen Soc Symp
+Eukaryot Cell
+Eur Addict Res
+Eur Arch Otorhinolaryngol
+Eur Arch Otorhinolaryngol Suppl
+Eur Arch Psychiatry Clin Neurosci
+Eur Arch Psychiatry Neurol Sci
+Eur Asia Stud
+Eur Biophys J
+Eur Cell Mater
+Eur Child Adolesc Psychiatry
+Eur Cytokine Netw
+Eur Econ Rev
+Eur Ethn
+Eur Foreign Aff Rev
+Eur Heart J
+Eur Heart J Suppl
+Eur Hist Q
+Eur Hochschulschr VII Med B Gesch Med
+Eur Hum Rights Law Rev
+Eur Intellect Prop Rev
+Eur J Anaesthesiol
+Eur J Anaesthesiol Suppl
+Eur J Appl Physiol
+Eur J Appl Physiol Occup Physiol
+Eur J Basic Appl Histochem
+Eur J Biochem
+Eur J Cancer
+Eur J Cancer B Oral Oncol
+Eur J Cancer Care (Engl)
+Eur J Cancer Clin Oncol
+Eur J Cancer Prev
+Eur J Cardiol
+Eur J Cardiothorac Surg
+Eur J Cardiovasc Nurs
+Eur J Cardiovasc Prev Rehabil
+Eur J Cell Biol
+Eur J Cell Biol Suppl
+Eur J Clin Chem Clin Biochem
+Eur J Clin Invest
+Eur J Clin Microbiol
+Eur J Clin Microbiol Infect Dis
+Eur J Clin Nutr
+Eur J Clin Pharmacol
+Eur J Contracept Reprod Health Care
+Eur J Dent Educ
+Eur J Dermatol
+Eur J Disord Commun
+Eur J Drug Metab Pharmacokinet
+Eur J Echocardiogr
+Eur J Emerg Med
+Eur J Endocrinol
+Eur J Epidemiol
+Eur J Gastroenterol Hepatol
+Eur J Gen Pract
+Eur J Genet Soc
+Eur J Gynaecol Oncol
+Eur J Haematol
+Eur J Haematol Suppl
+Eur J Health Econ
+Eur J Health Law
+Eur J Heart Fail
+Eur J Histochem
+Eur J Hum Genet
+Eur J Immunogenet
+Eur J Immunol
+Eur J Int Aff
+Eur J Intensive Care Med
+Eur J Intern Med
+Eur J Mark
+Eur J Mass Spectrom (Chichester, Eng)
+Eur J Med
+Eur J Med Chem
+Eur J Med Genet
+Eur J Med Res
+Eur J Morphol
+Eur J Neurol
+Eur J Neurosci
+Eur J Nucl Med
+Eur J Nucl Med Mol Imaging
+Eur J Nutr
+Eur J Obstet Gynecol
+Eur J Obstet Gynecol Reprod Biol
+Eur J Oncol Nurs
+Eur J Oper Res
+Eur J Ophthalmol
+Eur J Oral Sci
+Eur J Orthod
+Eur J Paediatr Dent
+Eur J Paediatr Neurol
+Eur J Pain
+Eur J Pediatr
+Eur J Pediatr Surg
+Eur J Pharm Biopharm
+Eur J Pharm Sci
+Eur J Pharmacol
+Eur J Polit Res
+Eur J Popul
+Eur J Prosthodont Restor Dent
+Eur J Protistol
+Eur J Public Health
+Eur J Radiol
+Eur J Respir Dis
+Eur J Respir Dis Suppl
+Eur J Rheumatol Inflamm
+Eur J Sex Transm Dis
+Eur J Soc Psychol
+Eur J Surg
+Eur J Surg Oncol
+Eur J Surg Suppl
+Eur J Toxicol
+Eur J Toxicol Environ Hyg
+Eur J Toxicol Hyg Environ
+Eur J Ultrasound
+Eur J Vasc Endovasc Surg
+Eur J Vasc Surg
+Eur Law Rev
+Eur Med (Paris)
+Eur Neurol
+Eur Neuropsychopharmacol
+Eur Om
+Eur Phys J E Soft Matter
+Eur Psychiatry
+Eur Qual Assur Netw Newsl
+Eur Radiol
+Eur Reg
+Eur Respir J
+Eur Respir J Suppl
+Eur Rev
+Eur Rev Appl Psychol
+Eur Rev Med Pharmacol Sci
+Eur Sociol Rev
+Eur Spine J
+Eur Stud Rev
+Eur Surg Res
+Eur Urban Reg Stud
+Eur Urol
+Eur Water Pollut Control
+Eura Medicophys
+Euro Surveill
+Eurobiologiste
+Europ Demogr Inf Bull
+Europa Archiv
+Europace
+Europe
+Europhys Lett
+Euthan Rev
+Euthanasia News
+Eval Health Prof
+Eval Pract
+Eval Program Plann
+Eval Q
+Eval Rev
+Evaluation
+Evening Sun
+Evid Based Cardiovasc Med
+Evid Based Complement Alternat Med
+Evid Based Dent
+Evid Based Ment Health
+Evid Based Nurs
+Evid Rep Technol Assess (Summ)
+Evocations
+Evol Anthropol
+Evol Comput
+Evol Dev
+Evol Hum Behav
+Evol Med
+Evol Psychiatr (Paris)
+Evolution Int J Org Evolution
+Except Child
+Excerpta Med
+Excerpta Med (Dermatol)
+Excerpta Med (Ophthalmol)
+Excerpta Med (Urol)
+Excerpta Medica 15 Chest Dis
+Excerpta Medica 20 Gerontol Geriatr
+Exec Housekeep Today
+Exec Housekeeper
+Exec Intell Rev
+Exec Solut Healthc Manag
+Exerc Immunol Rev
+Exerc Sport Sci Rev
+Exp Aging Res
+Exp Anim
+Exp Appl Acarol
+Exp Biol
+Exp Biol Med
+Exp Biol Med (Maywood)
+Exp Brain Res
+Exp Cell Biol
+Exp Cell Res
+Exp Cell Res Suppl
+Exp Clin Endocrinol
+Exp Clin Endocrinol Diabetes
+Exp Clin Immunogenet
+Exp Clin Psychopharmacol
+Exp Clin Transplant
+Exp Dermatol
+Exp Diabesity Res
+Exp Embryol Teratol
+Exp Eye Res
+Exp Gerontol
+Exp Hematol
+Exp Lung Res
+Exp Med Pathol Klin
+Exp Med Surg
+Exp Mol Med
+Exp Mol Pathol
+Exp Mycol
+Exp Nephrol
+Exp Neurol
+Exp Oncol
+Exp Parasitol
+Exp Pathol
+Exp Pathol (Jena)
+Exp Pathol Suppl
+Exp Physiol
+Exp Psychol
+Exp Toxicol Pathol
+Expedition
+Experientia
+Experientia Suppl
+Expert Opin Biol Ther
+Expert Opin Drug Deliv
+Expert Opin Drug Saf
+Expert Opin Emerg Drugs
+Expert Opin Investig Drugs
+Expert Opin Pharmacother
+Expert Opin Ther Targets
+Expert Rev Anti Infect Ther
+Expert Rev Anticancer Ther
+Expert Rev Cardiovasc Ther
+Expert Rev Med Devices
+Expert Rev Mol Diagn
+Expert Rev Mol Med
+Expert Rev Neurother
+Expert Rev Proteomics
+Expert Rev Vaccines
+Explor Econ Hist
+Explor J
+Explor Knowl
+Explor Renaiss Cult
+Explorer (Hayward)
+Explorer (Kansas City)
+Expo Times
+Expos Annu Biochim Med
+Extremophiles
+Eye
+Eye Contact Lens
+Eye Ear Nose Throat Mon
+FAO Fish Circ
+FAO Food Nutr Pap
+FAO Food Nutr Ser
+FAO Nutr Meet Rep Ser
+FAO Nutr Stud
+FAS Prof Bull
+FAS Public Interest Rep
+FASEB J
+FDA Consum
+FDA Drug Bull
+FDA Med Bull
+FDC Rep Drugs Cosmet
+FDI World
+FEBS J
+FEBS Lett
+FEMNET News
+FEMS Immunol Med Microbiol
+FEMS Microbiol Ecol
+FEMS Microbiol Immunol
+FEMS Microbiol Lett
+FEMS Microbiol Rev
+FEMS Yeast Res
+FLA Bar J
+FNIB
+FNIB Info
+FO
+FPM Bull
+Fa Yi Xue Za Zhi
+Fac Cathol Lille
+Fac Notes (New Orleans La)
+Facial Orthop Temporomandibular Arthrol
+Facial Plast Surg
+Facial Plast Surg Clin North Am
+Fact Sheets Swed
+Factor Odontol
+Factotum
+Facts Infant Feed
+Fag Tidsskr Sykepleien
+Fait Tend
+Faith Philos
+Fam Cancer
+Fam Circle
+Fam Community Health
+Fam Concil Courts Rev
+Fam Consum Sci Res J
+Fam Coord
+Fam Dev
+Fam Econ Rev
+Fam Health
+Fam Law Q
+Fam Law Rep
+Fam Law Rev
+Fam Life Coord
+Fam Life Educ
+Fam Life Matters
+Fam Matters
+Fam Med
+Fam Plan Manag
+Fam Plann
+Fam Plann (Palo Alto)
+Fam Plann Dig
+Fam Plann Inf Serv
+Fam Plann Perspect
+Fam Plann Popul Rep
+Fam Plann Q
+Fam Plann Resume
+Fam Plann Today
+Fam Pract
+Fam Pract Manag
+Fam Pract News
+Fam Pract Res J
+Fam Process
+Fam Relat
+Fam Soc
+Fam Syst Health
+Fam Syst Med
+Fam Ther
+Fam Urol
+Familia
+Family Law
+Family Plan World
+Famplanco News
+Far East Aff
+Far East Econ Rev
+Faraday Discuss
+Faraday Discuss Chem Soc
+Farm Clin
+Farm Glas
+Farm Hosp
+Farm Nueva
+Farm Obz
+Farm Pol
+Farm Tid
+Farm Vestn
+Farm Zh
+Farmacihist Sallsk Ars
+Farmaco
+Farmaco [Prat]
+Farmaco [Sci]
+Farmacognosia
+Farmacoter Actual
+Farmakol Toksikol
+Farmakoterapi
+Farmatsiia
+Farmatsiia (Sofia)
+Fasett
+Fauchard
+Faulkner Grays Med Health
+Fed Bar J
+Fed Bull
+Fed Insur Corp Couns Q
+Fed Insur Couns Q
+Fed Law Rep
+Fed Oper Dent
+Fed Probat
+Fed Proc
+Fed Proc Transl Suppl
+Fed Regist
+Fed Report
+Fed Rules Decis
+Fed Suppl
+Feedback
+Fegato
+Feldsher Akush
+Feline Pract
+Fem Econ
+Fem Issues
+Fem Psychol
+Fem Rev
+Fem Stud
+Female Health Top Diagn Report
+Female patient
+Femina
+Fennia
+Fertil Contracept
+Fertil Contracept Sex
+Fertil Determ Res Notes
+Fertil Orthog
+Fertil Steril
+Fetal Diagn Ther
+Fetal Pediatr Pathol
+Fetal Ther
+Feuill Biol
+Feuill Prat
+Fibrinolysis
+Fides Et Hist
+Field Staff Rep
+Fieldstaff Rep
+Fieldstaff Rep West Eur Ser
+Fiji Med J
+Filaria J
+Film Hist
+Filson Club Hist Q
+Fin Lakaresallsk Handl
+Financ Times
+Finance Dev
+Finanzarchiv
+Find Brief
+Finisterra
+Fire J
+Fire Technol
+First People
+First Things
+Fish Shellfish Immunol
+Fission Prod Inhal Proj
+Fitoterapia
+Fiziol Cheloveka
+Fiziol Norm Patol
+Fiziol Zh
+Fiziol Zh Im I M Sechenova
+Fiziol Zh SSSR Im I M Sechenova
+Fla Dent J
+Fla Entomol
+Fla Hist Q
+Fla J Int Law
+Fla Law Rev
+Fla Nurse
+Fla State Univ Law Rev
+Fla Suppl
+Flambeau
+Flash Inf
+Fletcher Forum
+Fletcher Forum World Aff
+Flight Saf Dig
+Flinders J Hist Polit
+Flintknappers Exch
+Florilegium
+Flying Saf
+Focus
+Focus AACN
+Focus Autism Other Dev Disabl
+Focus Crit Care
+Focus Gend
+Focus MDA
+Focus Ohio Dent
+Focus Tech Coop
+Fogorv Sz
+Foi Lang
+Fol Ophthalmol
+Fold Des
+Foldr Ert
+Folha Med
+Folia Allergol (Roma)
+Folia Biol (Krakow)
+Folia Biol (Praha)
+Folia Cardiol
+Folia Clin Biol (Sao Paulo)
+Folia Clin Int (Barc)
+Folia Endocrinol
+Folia Endocrinol Mens Incretologia Incretoterapia
+Folia Geogr Ser Geogr Oecon
+Folia Haematol (Frankf)
+Folia Haematol Int Mag Klin Morphol Blutforsch
+Folia Hered Pathol (Milano)
+Folia Histochem Cytobiol
+Folia Histochem Cytochem (Krakow)
+Folia Humanist
+Folia Med (Napoli)
+Folia Med (Plovdiv)
+Folia Med Cracov
+Folia Med Neerl
+Folia Mendeliana
+Folia Microbiol (Praha)
+Folia Morphol (Praha)
+Folia Morphol (Warsz)
+Folia Neuropathol
+Folia Orient
+Folia Parasitol (Praha)
+Folia Phoniatr (Basel)
+Folia Phoniatr Logop
+Folia Primatol (Basel)
+Folia Psychiatr
+Folia Psychiatr Neurol Jpn
+Folia Psychiatr Neurol Neurochir Neerl
+Folia Stomatol
+Folia Vet
+Folia Vet Lat
+Folk
+Folkl Am
+Folkl Brabancon
+Folkl Champagne
+Folkl Suisse
+Folklore
+Fontanus
+Fonti Stor Sanita
+Food Addit Contam
+Food Biotechnol
+Food Chem Toxicol
+Food Cosmet Toxicol
+Food Drug Cosmet Law J
+Food Drug Cosmet Law Q
+Food Drug Cosmet Med Device Law Dig
+Food Drug Law J
+Food Foodways
+Food Manage
+Food Microbiol
+Food Nutr (Roma)
+Food Nutr Bull
+Food Policy
+Food Res
+Food Res Inst Stud
+Food Res Inst Stud Agric Econ Trade Dev
+Food Technol
+Foodborne Pathog Dis
+Fookien Times Yearb
+Foot Ankle
+Foot Ankle Clin
+Foot Ankle Int
+For Ecol Manage
+Forbes
+Ford Found Rep
+Fordham Int Law J
+Fordham Law Rev
+Fordham Urban Law J
+Foreign Aff
+Foreign Policy
+Forensic Sci
+Forensic Sci Int
+Foresight
+Formos J Med Humanit
+Formulary
+Foro Int
+Foro Mund Salud
+Forsch Fortbild Chir Bewegungsapparates
+Forsch J Neue Soz Beweg
+Forsch Komplementarmed
+Forsch Komplementarmed Klass Naturheilkd
+Forsch Volks Landeskd
+Forsvarsmedicin
+Fort Concho Rep
+Fortn Rev Chic Dent Soc
+Fortpflanz Besamung Aufzucht Haustiere
+Fortschr Androl
+Fortschr Angew Radioisot Grenzgeb
+Fortschr Arzneimittelforsch
+Fortschr Chem Org Naturst
+Fortschr Geb Rontgenstr
+Fortschr Geb Rontgenstr Nuklearmed
+Fortschr Geb Rontgenstrahlen Neuen Bildgeb Verfahr Erganzungsbd
+Fortschr Geb Rontgenstrahlen Nuklearmed Erganzungsbd
+Fortschr Geburtshilfe Gynakol
+Fortschr Hals Nasen Ohrenheilkd
+Fortschr Immunitatsforsch
+Fortschr Kiefer Gesichtschir
+Fortschr Kieferorthop
+Fortschr Med
+Fortschr Med Monogr
+Fortschr Med Orig
+Fortschr Med Suppl
+Fortschr Neurol Psychiatr
+Fortschr Neurol Psychiatr Grenzgeb
+Fortschr Ophthalmol
+Fortschr Psychosom Med
+Fortschr Tierphysiol Tierernahr
+Fortschr Verhaltensforsch
+Fortschr Zool
+Fortuna Vitrea
+Fortune
+Forum
+Forum (Genova)
+Forum (Wash)
+Forum Appl Res Public Policy
+Forum Dev Stud
+Forum Fam Plan West Hemisph
+Forum Med
+Forum Mond Sante
+Forum Nutr
+Forum Rev Invat Super
+Forum Stat
+Found News
+Found News Comment
+Foundations
+Fpop Bull
+Fprc
+Fr Am Rev
+Fr Hist Stud
+Fr Med
+Fr Stud
+Fra Sundhedsstyr
+Fracastoro
+Fractals
+Francia
+Frankf Hefte
+Frankf Z Pathol
+Frater Psi Omega
+Free China Rev
+Free Inq
+Free Inq Creat Sociol
+Free Radic Biol Med
+Free Radic Res
+Free Radic Res Commun
+Free World Horiz
+Freedom Issue
+Freedom Rev
+Freedomways
+Freeman
+Freib Forsch Medizingesch
+Freie Zahnarzt
+Frenesie
+Fresenius J Anal Chem
+Friends P I Nixon Med Hist Libr
+Friends Women Newsl
+Friuli Med
+Front Aging Ser
+Front Biol
+Front Biosci
+Front Gastrointest Res
+Front Health Policy Res
+Front Health Serv Manage
+Front Horm Res
+Front Lines
+Front Lines Res
+Front Med Biol Eng
+Front Neuroendocrinol
+Front Nurs Serv Q Bull
+Front Oral Physiol
+Front Radiat Ther Oncol
+Front Zool
+Frontiers (Boulder)
+Fruhmittelalt Stud
+Ftiziologia
+Fugitive Leaves Hist Collect
+Fukuoka Igaku Zasshi
+Fukuoka Shika Daigaku Gakkai Zasshi
+Fukushima Igaku Zasshi
+Fukushima J Med Sci
+Fulorrgegegyogyaszat
+Funct Dev Morphol
+Funct Integr Genomics
+Funct Neurol
+Funct Orthod
+Funct Plant Biol
+Fund Raising Manage
+Fundam Appl Toxicol
+Fundam Clin Pharmacol
+Fundam Sci
+Fungal Genet Biol
+Furrow
+Futur Anter
+Future
+Future Child
+Future Dent
+Future Oncol
+Futures
+Futuribles
+Futurist
+Fysiatr Revmatol Vestn
+Fysiatr Vestn Cesk Fysiatr Spol
+G Accad Med Torino
+G Anest Stomatol
+G Batteriol Immunol
+G Batteriol Virol Immunol
+G Batteriol Virol Immunol Clin
+G Batteriol Virol Immunol Microbiol
+G Biochim
+G Bot Ital
+G Chir
+G Clin Med
+G Crit Filos Ital
+G E N
+G Econ Ann Econ
+G Endodonzia
+G Fis Sanit Prot Radiaz
+G Gerontol
+G Gerontol Suppl
+G Ig Med Prev
+G Ital Anestesiol
+G Ital Cardiol
+G Ital Cardiol (Rome)
+G Ital Chemioter
+G Ital Chir
+G Ital Della Tuberc
+G Ital Dermatol
+G Ital Dermatol Minerva Dermatol
+G Ital Dermatol Venereol
+G Ital Endod
+G Ital Filol
+G Ital Mal Torace
+G Ital Med Lav
+G Ital Med Lav Ergon
+G Ital Nefrol
+G Ital Oftalmol
+G Ital Oncol
+G Ital Patol
+G Ital Tuberc Mal Torace
+G Mal Infett Parassit
+G Med Mil
+G Pneumol
+G Psichiatr Neuropatol
+G Sci Mediche
+G Stomatol Ortognatodonzia
+G Veneto Sci Med
+GAEA An Soc Argent Estud Geogr
+GHA Today
+GHAA J
+GIRE
+GLQ
+GMDA Bull
+GMHC Treat Issues
+GMU Law Rev
+GP
+GRMA News
+GROOTS
+GSA Today
+Ga Epidemiol Rep
+Ga Hist Q
+Ga Hosp Today
+Ga J Sci
+Ga Nurse
+Ga Pharm Q
+Ga State Univ Law Rev
+Gac CONASIDA
+Gac Med (Guayaquil)
+Gac Med Caracas
+Gac Med Esp
+Gac Med Mex
+Gac Med Norte
+Gac Sanit
+Gait Posture
+Galenika Cas Med Farm Hem Srod Nauke
+Galicia Clin
+Gallup Opin Index
+Gallup Rep
+Gamete Res
+Gan
+Gan No Rinsho
+Gan To Kagaku Ryoho
+Ganka
+Gann
+Gaoxiong Yi Xue Ke Xue Za Zhi
+Gard Hist
+Gastric Cancer
+Gastroenterol Clin Biol
+Gastroenterol Clin North Am
+Gastroenterol Fortbildungskurse Prax
+Gastroenterol Hepatol
+Gastroenterol J
+Gastroenterol Jpn
+Gastroenterol Nurs
+Gastroenterologia
+Gastroenterologist
+Gastroenterology
+Gastrointest Endosc
+Gastrointest Endosc Clin N Am
+Gastrointest Radiol
+Gateway Herit
+Gaz Arch
+Gaz Egypt Paediatr Assoc
+Gaz Grolier Club
+Gaz Hop Civ Mil Empire Ottoman
+Gaz Law Soc Upper Can
+Gaz Med Fr
+Gaz Med Lomb
+Gaz Med Port
+Gaz Woda Tech
+Gazette
+Gazz Chim Ital
+Gazz Int Med Chir
+Gazz Med Ital
+Gazz Osp Clin
+Gazz Sanit
+Ge Mag
+Geburtshilfe Frauenheilkd
+Gedrag Gezond
+Gegenbaurs Morphol Jahrb
+Geka Chiryo
+Gelis Derg Orta dogu Tek Univ
+Geloof Wet
+Gem State RN News Lett
+Gematol Transfuziol
+Gen
+Gen Comp Endocrinol
+Gen Cytochem Methods
+Gen Dent
+Gen Diagn Pathol
+Gen Hosp Psychiatry
+Gen Inf Programme UNISISTNewsl
+Gen Laws R I 1956 R I
+Gen Pharmacol
+Gen Physiol Biophys
+Gen Pract
+Gen Statut N C N C
+Gen Surg Laparosc News
+Gen Syst
+Gencho Hiroshima Igaku
+Gend Action
+Gend CG Newsl
+Gend Dev
+Gend Educ
+Gend Hist
+Gend Med
+Gend Place Cult
+Gend Soc
+Gend Technol Dev
+Gender Issues
+Genders (Austin Tex)
+Gene
+Gene Amplif Anal
+Gene Anal Tech
+Gene Expr
+Gene Expr Patterns
+Gene Geogr
+Gene Ther
+Gene Ther Mol Biol
+Geneeskd Bl
+Geneeskd Gids
+Geneeskd Sport
+Generations
+Genes Brain Behav
+Genes Cells
+Genes Chromosomes Cancer
+Genes Dev
+Genes Funct
+Genes Genet Syst
+Genes Immun
+Genesis
+Genet Anal
+Genet Anal Tech Appl
+Genet Couns
+Genet Eng
+Genet Eng (N Y)
+Genet Epidemiol
+Genet Epidemiol Suppl
+Genet Genet Eng
+Genet Iber
+Genet Med
+Genet Mol Res
+Genet Psychol Monogr
+Genet Res
+Genet Sel Evol
+Genet Soc Gen Psychol Monogr
+Genet Test
+Genet Vaccines Ther
+Genethics News
+Genetic Resour
+Genetica
+Genetics
+Genetika
+Geneva Afr
+Genewatch
+Genitif
+Genitourin Med
+Genome
+Genome Biol
+Genome Inform Ser Workshop Genome Inform
+Genome Res
+Genome Sci Technol
+Genomics
+Genomics Proteomics Bioinformatics
+Genre
+Genus
+Geo Eco Trop
+GeoJournal
+Geoadria
+Geobios Mem Spec
+Geocarto Int
+Geochem J
+Geochem Trans
+Geochim Cosmochim Acta
+Geoforum
+Geog Glas
+Geoge Mason Univ Civ Rights Law J
+Geogr Anal
+Geogr Ann Ser B
+Geogr Ber
+Geogr Bull
+Geogr Cas
+Geogr Helv
+Geogr J
+Geogr Mag
+Geogr Med
+Geogr Med Suppl
+Geogr Obs
+Geogr Perspect
+Geogr Pol
+Geogr Pregl
+Geogr Rech
+Geogr Rev
+Geogr Rev India
+Geogr Rev Jpn
+Geogr Rundsch
+Geogr Shkole
+Geogr Surv
+Geogr Tidsskr
+Geogr Tydschr
+Geogr Vestn
+Geogr Z
+Geographica
+Geographicalia
+Geography
+Geol Mag
+Geol Rundsch
+Geol Soc Am Bull
+Geology
+Geomicrobiol J
+Geophys Res Lett
+George Washington J Int Law Econ
+George Washington Law Rev
+George Wright Forum
+Georget Immgr Law J
+Georget J Leg Ethics
+Georget Mag
+Georgetown Dent J
+Georgetown Law J
+Georgetown Med Bull
+Georgetown Univ Right to Life J
+Georgetown Univ Sch Dent Mirror
+Georgia Law Rev
+Georgia State Bar J
+Georgian Med News
+Geotimes
+Ger Comments
+Ger J Ophthalmol
+Ger Life Lett
+Ger Med
+Ger Med Mon
+Ger Polit
+Ger Rom Monatsschr
+Ger Stud Rev
+Ger Yearb Int Law
+Geriatr Nephrol Urol
+Geriatr Nurs
+Geriatr Nurs (Lond)
+Geriatr Nurs (Minneap)
+Geriatr Nurs Home Care
+Geriatrics
+Germanistik
+Germant Crier
+Gerodontics
+Gerodontology
+Geron
+Gerontion
+Gerontol Clin (Basel)
+Gerontol Geriatr Educ
+Gerontol Soc
+Gerontologia
+Gerontologie
+Gerontologist
+Gerontology
+Gesch Ges
+Gesch Wiss Unterr
+Gesnerus
+Gesnerus Suppl
+Gesund Ing
+Gesundheit
+Gesundheitswesen
+Gewina
+Ghana J Sociol
+Ghana Med J
+Ghana Nurse
+Ghana Off News Bull
+Ghana Soc Sci J
+Gibralfaro
+Gifu Shika Gakkai Zasshi
+Gig Sanit
+Gig Tr Prof Zabol
+Ginecol Clin
+Ginecol Obstet (Lima)
+Ginecol Obstet Mex
+Ginecologia
+Ginekol Pol
+Glamorgan Hist
+Glas Belgrad Hig Inst NR Srb
+Glas Srp Akad Nauka [Med]
+Glas Zavodzdrav
+Glasg Dent J
+Glasgow Med J
+Glasnik
+Glasra
+Glaxo Vol Occas Contrib Sci Art Med
+Glendale Law Rev
+Glia
+Glimpse
+Glob AIDSnews
+Glob Access STD Diagn
+Glob Cent News
+Glob Child Health New Rev
+Glob Environ Change
+Glob Impacts
+Glob Issues
+Glob Planet Change
+Global Biogeochem Cycles
+Global Futures Dig
+Global Health
+Globe
+Glotta
+Glycobiology
+Glycoconj J
+God Vojnomed Akad
+God Zb Med Fak Skopje
+Gold Bull
+Gold Gate Law Rev
+Gold Gate Univ Law Rev
+Gonzaga Law Rev
+Good Housekeeping
+Gott Jahrb
+Gov Inf Q
+Gov Publ Rev
+Gov Relat Note
+Governing
+Grace Hosp Bul
+Grad Fac Philos J
+Graefes Arch Clin Exp Ophthalmol
+Grants Mag
+Grantsmanship Cent News
+Graph Models
+Grassroots Dev
+Gratz Coll Annual Jew Stud
+Gravit Space Biol Bull
+Great Ideas Today
+Great Ormond St J
+Greater Milw Dent Bull
+Greater St Louis Dent Soc Bull
+Greek Econ Rev
+Greek Roman Byz Stud (Cambridge Mass)
+Grenzgeb Wiss
+Groniek
+Ground Water
+Group Dyn
+Group Health J
+Group Pract
+Group Pract J
+Growth
+Growth Change
+Growth Dev Aging
+Growth Factors
+Growth Horm IGF Res
+Growth Regul
+Grud Serdechnososudistaia Khir
+Grudn Khir
+Grundfragen Silikoseforsch
+Gruzlica
+Gt Concern
+Guam Rec
+Guang Ming Ri Bao
+Guang Pu Xue Yu Guang Pu Fen Xi
+Guatem Indig
+Guerr Mond Conflits Contemp
+Guide Prat
+Guigoz Sci Rev
+Guildhall Misc
+Guildhall Stud Lond Hist
+Gujarat Stat Rev
+Gulf Coast Hist Rev
+Gunma J Med Sci
+Guo Li Taiwan Shi Fan Da Xue Di li Yan Jiu Suo Di Li Yan Jiu Bao Gaob
+Guru Nanak J Sociol
+Gut
+Gutenberg Jahrb
+Guthrie Bull
+Guthrie Clin Bull
+Guthrie J Donald Guthrie Found Med Res
+Guttmacher Rep Public Policy
+Guys Hosp Gaz
+Guys Hosp Rep
+Gyenbug Uidai Jabji
+Gyermekgyogyaszat
+Gymnasium
+Gynaecologia
+Gynakol Geburtshilfliche Rundsch
+Gynakol Prax
+Gynakol Rundsch
+Gynakologe
+Gynecol Endocrinol
+Gynecol Invest
+Gynecol Obstet (Paris)
+Gynecol Obstet Fertil
+Gynecol Obstet Invest
+Gynecol Oncol
+Gynecol Prat
+Gynecologie
+Gyogyszereink
+Gyogyszereszet
+HASL Rep
+HBI Rep
+HEC Forum
+HELR Harvard Environ Law Rev
+HIV AIDS Policy Law Rev
+HIV Capsule Rep
+HIV Clin
+HIV Clin Trials
+HIV Hotline
+HIV Inside
+HIV Med
+HIV Prev Plus
+HMO
+HMO Pract
+HNO
+HPB Surg
+HPN Hosp Purch News
+HRC CC J High Resolut Chromatogr Chromatogr Commun
+HRMAGAZINE
+HRSA Careaction
+HSMHA Health Rep
+Habitat Debate
+Habitat Int
+Habitation (Elmsford)
+Hacettepe Bull Med Surg
+Hacettepe Sosyal Bilimler Derg
+Hadassah Mag
+Hades
+Hadtort Kozl
+Haematol Blood Transfus
+Haematol Lat
+Haematologia (Budap)
+Haematologica
+Haemophilia
+Haemostasis
+Hahnemannian
+Halcyon
+Halsburys Statut Engl G B
+Halve Maen
+Hamatol Bluttransfus
+Hamb Arztebl
+Hamdard Islam
+Hamdard Med
+Hamline Law Rev
+Hamostaseologie
+Hampshire
+Hand
+Hand Clin
+Hand Surg
+Handb Exp Pharmacol
+Handchir Mikrochir Plast Chir
+Handchirurgie
+Handel Maatsch Geschied Oudheidk Gent
+Hanguk Kyun Hakoe Chi
+Hanguk Nonghwa Hakhoe Chi
+Hanguk Saenghwahakhoe Chi
+Hanguk Uiyak
+Hansenol Int
+Harb Dent Log
+Harefuah
+Harlem Hosp Bull
+Harlem Hosp Bull (N Y)
+Harm Reduct J
+Harofe Haivri Heb Med J
+Harper Hosp Bull
+Harpers (N Y N Y)
+Hartford Hosp Bull
+Harv AIDS Rev
+Harv Blacklett J
+Harv Blacklett Law J
+Harv Bus Rev
+Harv Civ Rights-Civil Lib Law Rev
+Harv Dent Alumni Bull
+Harv Dent Bull
+Harv Educ Rev
+Harv Health Lett
+Harv Heart Lett
+Harv Hum Rights J
+Harv J Asiat Stud
+Harv J Law Public Policy
+Harv J Law Technol
+Harv J Minor Public Health
+Harv Law Rev
+Harv Libr Bull
+Harv Mag
+Harv Med Alumni Bull
+Harv Mens Health Watch
+Harv Ment Health Lett
+Harv Rev Psychiatry
+Harv Stud Classic Philol
+Harv Theol Rev
+Harv Women's Law J
+Harv Womens Health Watch
+Harvard Int Law J
+Harvard Int Rev
+Harvard J Legis
+Harvey Lect
+Harz Z
+Hasifrut
+Hastane
+Hastings Cent Rep
+Hastings Constit Law Q
+Hastings Int Comp Law Rev
+Hastings Law J
+Hastings Womens Law J
+Hautarzt
+Haw Bar J
+Hawaii Dent J
+Hawaii J Hist
+Hawaii Med J
+Hawaii Nurse
+Hawaii Nurse (Honol)
+Hawaii Nurses Pipeline
+Hawaii Revis Statut Hawaii
+Hayes Hist J
+Head Face Med
+Head Neck
+Head Neck Surg
+Headache
+Heal Light
+Health (London)
+Health (N Y)
+Health Aff
+Health Aff (Millwood)
+Health Bull
+Health Bull (Edinb)
+Health Can Soc
+Health Care (Don Mills)
+Health Care Anal
+Health Care Can
+Health Care Cost Reengineering Rep
+Health Care Dimen
+Health Care Educ
+Health Care Ethics USA
+Health Care Financ Rev
+Health Care Financ Rev Annu Suppl
+Health Care Financ Rev Stat Suppl
+Health Care Financ Trends
+Health Care Food Nutr Focus
+Health Care Innov
+Health Care Law Mon
+Health Care Law Newsl
+Health Care Manag
+Health Care Manag (Frederick)
+Health Care Manag Sci
+Health Care Manage Rev
+Health Care Mark
+Health Care Mark Target Market
+Health Care Newsl
+Health Care Plann Mark
+Health Care Reform Week
+Health Care Secur Saf Manage
+Health Care Strateg Manage
+Health Care Superv
+Health Care Syst
+Health Care Week
+Health Care Women Int
+Health Commun
+Health Commun Informatics
+Health Cost Manage
+Health Data Manag
+Health Devices
+Health Econ
+Health Educ
+Health Educ Behav
+Health Educ Bull
+Health Educ J
+Health Educ Monogr
+Health Educ Q
+Health Educ Rep
+Health Educ Res
+Health Educ Work
+Health Estate
+Health Estate J
+Health Expect
+Health Facil Manage
+Health Forum J
+Health Hum Rights
+Health Ind Today
+Health Inf Manag
+Health Inf Syst Telemed
+Health Info Libr J
+Health Lab Sci
+Health Law Can
+Health Law J
+Health Law News
+Health Law Proj Libr Bull
+Health Law Rev
+Health Law Vigil
+Health Libr Rev
+Health Manag Technol
+Health Manage
+Health Manage Forum
+Health Manage Q
+Health Manpow Lit
+Health Manpow Manage
+Health Manpow Rep
+Health Mark Q
+Health Matrix
+Health Matrix Clevel
+Health Med
+Health Med Care Serv Rev
+Health Millions
+Health News
+Health PAC Bull
+Health People
+Health Perspect
+Health Phys
+Health Place
+Health Plann Manpow Rep
+Health Policy
+Health Policy (New York)
+Health Policy Educ
+Health Policy Plan
+Health Policy Q
+Health Policy Week
+Health Popul Perspect Issues
+Health Pract Physician Assist
+Health Prog
+Health Promot
+Health Promot Int
+Health Promot J Austr
+Health Promot Pract
+Health Psychol
+Health Qual Life Outcomes
+Health Reform Prior Serv
+Health Rep
+Health Res Policy Syst
+Health Risk Soc
+Health Saf Code Annot State Calif Adopt April 7 1939 Calif
+Health Serv J
+Health Serv Manage
+Health Serv Manage Res
+Health Serv Manager
+Health Serv Manpow Rev
+Health Serv Rep
+Health Serv Res
+Health Sex
+Health Soc Care Community
+Health Soc Serv J
+Health Soc Work
+Health Stat Nord Ctries
+Health Stat Q
+Health Syst Lead
+Health Syst Manage
+Health Syst Rev
+Health Technol
+Health Technol Assess
+Health Technol Assess (Rockv)
+Health Technol Assess Rep
+Health Technol Dir
+Health Transit Rev
+Health Trends
+Health Values
+Health Visit
+HealthAction
+Healthc Ala
+Healthc Benchmarks
+Healthc Bottom Line
+Healthc Comput Commun
+Healthc Demand Dis Manag
+Healthc Exec
+Healthc Exec Curr
+Healthc Facil Manag Ser
+Healthc Financ Manage
+Healthc Foodserv
+Healthc Foodserv Mag
+Healthc Forum
+Healthc Forum J
+Healthc Hazard Manage Monit
+Healthc Hazard Mater Manage
+Healthc Hum Resour
+Healthc Hum Resour Spec Rep
+Healthc Inf Manage
+Healthc Inform
+Healthc Leadersh Manag Rep
+Healthc Leadersh Rep
+Healthc Manage Forum
+Healthc Pap
+Healthc Prot Manage
+Healthc Q
+Healthc Strateg
+Healthc Syst Strategy Rep
+Healthc Trends Transit
+Healthcare Benchmarks Qual Improv
+Healthcover
+Healthmarketing
+Healthplan
+Healthscan
+Healthspan
+Healthtexas
+Healthy People 2000 Stat Notes
+Healthy People 2000 Stat Surveill
+Healthy People 2010 Stat Notes
+Hear Res
+Heart
+Heart Advis
+Heart Bull
+Heart Cent Bull (Roslyn)
+Heart Dis
+Heart Dis Stroke
+Heart Fail
+Heart Fail Monit
+Heart Fail Rev
+Heart Lung
+Heart Lung Circ
+Heart Rhythm
+Heart Surg Forum
+Heart Transplant
+Heart Vessels
+Heart Vessels Suppl
+Heartbeat
+Heat Piping Air Cond
+Hefte Unfallheilkd
+Heidelb Jahrb
+Heilkd Heilwege
+Heilkunst
+Heilpadagog Werkbl
+Heimen
+Helicobacter
+Hell Adelphe
+Hell Cheirourgike
+Hell Iatr
+Hell J Nucl Med
+Hell Period Stomat Gnathopathoprosopike Cheir
+Hell Stomatol Chron
+Hellenic J Cardiol
+Hellenike Pyrenike Iatr
+Helmantica
+Helv Chim Acta
+Helv Chir Acta
+Helv Chir Acta Suppl
+Helv Med Acta
+Helv Med Acta Suppl
+Helv Odontol Acta
+Helv Paediatr Acta
+Helv Paediatr Acta Suppl
+Helv Physiol Pharmacol Acta
+Helv Physiol Pharmacol Acta Suppl 1
+Hematol Cell Ther
+Hematol J
+Hematol Oncol
+Hematol Oncol Clin North Am
+Hematol Pathol
+Hematology
+Hematology (Am Soc Hematol Educ Program)
+Hematopathol Mol Hematol
+Hemingway Rev
+Hemispheres
+Hemodial Int
+Hemoglobin
+Hemostase
+Henry E Sigerist Suppl Bull Hist Med
+Henry Ford Hosp Med Bull
+Henry Ford Hosp Med J
+Hepatitis Wkly
+Hepatobiliary Pancreat Dis Int
+Hepatogastroenterology
+Hepatol Res
+Hepatology
+Her Geneal
+Herbal Rev
+Herbarist
+Hercynia
+Hereditas
+Heredity
+Herit Rev
+Hermathena
+Hermes (Wiesb)
+Hernia
+Herpes
+Herz
+Herzschrittmacherther Elektrophysiol
+Hesperian Found News
+Hesperis Tamuda
+Hess Arztebl
+Hess Familienkd
+Hexagon Roche [Engl]
+Hifu
+Hifu To Hinyo Dermatol Urol
+Hifuka Kiyo
+Hifuka No Rinsho
+Hig Cas Hig Mikrobiol Epidemiol Sanit Teh
+Hig Zdraveopaz
+Higashi Nippon Shigaku Zasshi
+High Alt Med Biol
+High Technol (Boston)
+High Technol Law J
+Higiene
+Hillside J Clin Psychiatry
+Him Prir Soedin
+Hindsight
+Hindu
+Hindustan Antibiot Bull
+Hinyokika Kiyo
+Hip
+Hippocampus
+Hippocrates
+Hippocrates (Sausalito)
+Hippokrates
+Hippokrates (Helsinki)
+Hiroshima Daigaku Shigaku Zasshi
+Hiroshima J Med Sci
+Hisp Am Hist Rev
+Hisp J Behav Sci
+Hisp Med
+Hisp Sacra
+Hispania
+Hist Accid Trav
+Hist Afr
+Hist Agric
+Hist Aikak
+Hist Anthropol
+Hist Archaeol
+Hist Ark
+Hist Casopis
+Hist Child Q
+Hist Cienc Saude Manguinhos
+Hist Comput
+Hist Cult
+Hist Demogr
+Hist Demogr Mitt
+Hist Econ Soc
+Hist Educ
+Hist Educ Q
+Hist Eur Ideas
+Hist Fam
+Hist Hosp
+Hist Human Sci
+Hist J
+Hist J Auckl Waikato
+Hist J Mass
+Hist J West Mass
+Hist Jahrb
+Hist Jahrb Stadt Linz
+Hist Litteraturhist Stud
+Hist Mag
+Hist Mag Protestant Episcop Church
+Hist Med
+Hist Med Univ South Ala Coll Med
+Hist Med Vet
+Hist Mes
+Hist Methods
+Hist Mex
+Hist N H
+Hist Nat
+Hist Nauk Biol Med
+Hist Neurosci
+Hist News
+Hist Nurs Bull
+Hist Nurs Soc J
+Hist Pap Can Hist Assoc
+Hist Philos Life Sci
+Hist Polit Econ
+Hist Polit Thought
+Hist Preserv
+Hist Psychiatry
+Hist Psychol
+Hist Rec Aust Sci
+Hist Reflect
+Hist Relig
+Hist Res
+Hist Sci
+Hist Sci (Tokyo)
+Hist Sci Med
+Hist Soc
+Hist Soz Forsch
+Hist Stud
+Hist Stud Phys Biol Sci
+Hist Teacher
+Hist Theory
+Hist Tidskr
+Hist Tidskr Finl
+Hist Tidsskr
+Hist Today
+Hist Vida
+Hist Workshop
+Hist Workshop J
+Hist Z
+Histochem Cell Biol
+Histochem J
+Histochemie
+Histochemistry
+Histoire
+Histoire Soc
+Histol Histopathol
+Histopathology
+Historama
+Historia
+Historia (Argentina)
+Historian
+Historie (Arhus)
+Historiel Foren Arsskr
+History (Lond)
+Hitotsubashi J Law Polit
+Hofstra Law Rev
+Hoitotiede
+Hoja Tisiol
+Hokenfu Zasshi
+Hokkaido Igaku Zasshi
+Hokkaido Shika Ishikai Shi
+Holdsworth Law Rev
+Holist Nurs Pract
+Holistic Assertive Nurse
+Holocaust Genocide Stud
+Home Care Econ
+Home Care Manag
+Home Care Provid
+Home Health Care Serv Q
+Home Health J
+Home Health Rev
+Home Healthc Nurse
+Home Healthc Nurse Manag
+Homemak Mag
+Homeopath Fr
+Homeopathy
+Homeost Health Dis
+Homilet Pastor Rev
+Homme
+Hommes Migr
+Hommes Terres Nord
+Homo
+Hong Kong Law J
+Hong Kong Med J
+Hong Kong Mon Dig Stat
+Honvedorvos
+Hopital
+Hopkins HIV Rep
+Hoppe Seylers Z Physiol Chem
+Horiz Biochem Biophys
+Horiz Med
+Horizon
+Horm Behav
+Horm Metab Res
+Horm Metab Res Suppl
+Horm Res
+Hormoner
+Hormones
+Hormones (Athens Greece)
+Hormoon
+HortScience
+Hortic Rev (Am Soc Hortic Sci)
+Horttechnology
+Horumon To Rinsho
+Hosp Adm (Chic)
+Hosp Adm (New Delhi)
+Hosp Adm Can
+Hosp Admin Curr
+Hosp Admitting Mon
+Hosp Aviat
+Hosp Bond Rev
+Hosp Cap Finance
+Hosp Case Manag
+Hosp Community Psychiatry
+Hosp Corps Q
+Hosp Cost Manag Account
+Hosp Dev
+Hosp Employee Health
+Hosp Eng
+Hosp Entrep Newsl
+Hosp Equip Supplies
+Hosp Ethics
+Hosp Financ Manage
+Hosp Food Nutr Focus
+Hosp Formul
+Hosp Forum
+Hosp Gen (Madr)
+Hosp Gift Shop Manage
+Hosp Guest Relations Rep
+Hosp Hazard Mater Manage
+Hosp Health Netw
+Hosp Health Netw 360
+Hosp Health Serv Adm
+Hosp Health Serv Rev
+Hosp Infect Control
+Hosp J
+Hosp Law Newsl
+Hosp Libr
+Hosp Manage
+Hosp Manage Commun
+Hosp Manage Q
+Hosp Manager
+Hosp Mater Manage
+Hosp Mater Manage Q
+Hosp Med
+Hosp Med Staff
+Hosp Outlook
+Hosp Patient Relat Rep
+Hosp Peer Rev
+Hosp Pharm
+Hosp Physician
+Hosp Pract
+Hosp Pract (Hosp Ed)
+Hosp Pract (Minneap)
+Hosp Pract (Off Ed)
+Hosp Prog
+Hosp Purch Manage
+Hosp Q
+Hosp Rec Study
+Hosp Revenue Rep
+Hosp Risk Manage
+Hosp Secur Saf Manage
+Hosp Strategy Rep
+Hosp Superv
+Hosp Superv Bull
+Hosp Technol Ser
+Hosp Top
+Hosp Trib
+Hosp Trustee
+Hospital (Rio J)
+Hospitalis
+Hospitalist
+Hospitals
+Hospitals (Lond)
+Hotetsu Rinsho
+Houst J Health Law Policy
+Houst J Int Law
+Houst Law Rev
+Houst Lawyer
+Houston Rev
+How Eval Health Programs
+Howard Law J
+Hstc Bull
+Hu Li Yan Jiu
+Hu Li Za Zhi
+Hua Xi Kou Qiang Yi Xue Za Zhi
+Hua Xi Yi Ke Da Xue Xue Bao
+Hua Zhong Shi Fan Da Xue Xue Bao Zi Ran Ke Xue Ban
+Huan Jing Ke Xue
+Huisarts Wet
+Hum Antibodies
+Hum Antibodies Hybridomas
+Hum Behav
+Hum Biol
+Hum Biol Oceania
+Hum Brain Mapp
+Hum Cell
+Hum Commun Res
+Hum Dev
+Hum Ecol
+Hum Ecol Interdiscip J
+Hum Events
+Hum Exp Toxicol
+Hum Factors
+Hum Fertil
+Hum Fertil (Camb)
+Hum Gene Ther
+Hum Genet
+Hum Genet Suppl
+Hum Genome News
+Hum Genomics
+Hum Health Care
+Hum Health Care Int
+Hum Hered
+Hum Immunol
+Hum Life Rev
+Hum Mol Genet
+Hum Mov Sci
+Hum Mutat
+Hum Nat
+Hum Neurobiol
+Hum Nutr Appl Nutr
+Hum Nutr Clin Nutr
+Hum Organ
+Hum Pathol
+Hum Perf Extrem Environ
+Hum Physiol
+Hum Psychopharmacol
+Hum Relat
+Hum Reprod
+Hum Reprod Genet Ethics
+Hum Reprod Update
+Hum Res Rep
+Hum Resour Health
+Hum Resour Manage
+Hum Rights Q
+Hum Stud
+Hum Toxicol
+Human Rights
+Humane Med
+Humangenetik
+Humanismus Tech
+Humanist
+Humanit Rep
+Humanitas
+Humanitas (Monterey N L)
+Humanity Soc
+Humboldt J Soc Relat
+Hunan Yi Ke Da Xue Xue Bao
+Hung Acta Physiol
+Hung Stud Rev
+Huntia
+Huntingt Libr Q
+Hybrid Hybridomics
+Hybridoma
+Hybridoma (Larchmt)
+Hydrobiologia
+Hydrother Physiother
+Hyg Ment
+Hyg Revy
+Hygie
+Hypatia
+Hypertens Pregnancy
+Hypertens Res
+Hypertension
+Hypotenuse
+IADS Newsl
+IAL News
+IAPAC Mon
+IARC Monogr Eval Carcinog Risk Chem Hum
+IARC Monogr Eval Carcinog Risk Chem Hum Suppl
+IARC Monogr Eval Carcinog Risk Chem Man
+IARC Monogr Eval Carcinog Risks Hum
+IARC Monogr Eval Carcinog Risks Hum Suppl
+IARC Sci Publ
+IASSIST Q
+IAVI Rep
+IAWA J
+IBLA
+IBS Materi
+ICAO J
+ICCW J
+ICCW News Bull
+ICD Sci Educ J
+ICMH Newsl
+ICPD 94
+ICRS J Int Res Commun
+ICRS Med Rep
+ICSSR Newsl
+IDAA Commun
+IDEA J Law Technol
+IDO Rep
+IDRC Rep
+IDS Bull
+IDrugs
+IEE Proc Nanobiotechnol
+IEEE Comput Graph Appl
+IEEE Eng Med Biol Mag
+IEEE Instrum Meas Mag
+IEEE Netw
+IEEE Pervasive Comput
+IEEE Sens J
+IEEE Trans Acoust
+IEEE Trans Aerosp Electron Syst
+IEEE Trans Biomed Eng
+IEEE Trans Comput
+IEEE Trans Image Process
+IEEE Trans Inf Technol Biomed
+IEEE Trans Magn
+IEEE Trans Med Imaging
+IEEE Trans Nanobioscience
+IEEE Trans Neural Netw
+IEEE Trans Neural Syst Rehabil Eng
+IEEE Trans Nucl Sci
+IEEE Trans Pattern Anal Mach Intell
+IEEE Trans Plasma Sci IEEE Nucl Plasma Sci Soc
+IEEE Trans Rehabil Eng
+IEEE Trans Rob Autom
+IEEE Trans Syst Man Cybern
+IEEE Trans Syst Man Cybern A Syst Hum
+IEEE Trans Syst Man Cybern B Cybern
+IEEE Trans Syst Man Cybern C Appl Rev
+IEEE Trans Ultrason Ferroelectr Freq Control
+IEEE Trans Vis Comput Graph
+IFO Stud
+IGCC News
+IHRIM
+IHS Prim Care Provid
+IIASA Rep
+IIC Int Rev Ind Prop Copyr Law
+IIPS Newsl
+ILAR J
+ILSA J Int Law
+IMA J Math Appl Med Biol
+IME bull
+IMF Surv
+IMJ Ill Med J
+IMS Ind Med Surg
+INS Rep
+INSTRAW News
+IPPA Newsl
+IPPF Eur Reg Inf
+IPPF Med Bull
+IPPF News
+IPPF Open File
+IPPF WHR News Serv
+IRB
+IRCS J Med Sci
+IRCS Med Sci
+IRCS Med Sci Biomed Technol
+IRCS Med Sci Cardiovasc Syst
+IRE Trans Med Electron
+ISA Trans
+ISL Law Rev
+IUBMB Life
+IUSSP Pap
+Icarus
+Icea News
+Icmr Bull
+Icsu Rev
+Ida Code Ida
+Ida Law Rev
+Ideas Prod
+Ideggyogy Sz
+Ig Mod
+Ig Sanita Pubbl
+Ig Sanita Pubblica Collana Monogr
+Igaku Butsuri
+Igaku Kenkyu
+Igaku To Seibutsugaku
+Igakushi Kenkyu
+Igiena
+Ilar News
+Ill Bar J
+Ill Classic Stud
+Ill Dent J
+Ill Med J
+Ill Res
+Ill Revis Statut Ill Ill
+Illimani
+Image (IN)
+Image J Nurs Sch
+Images Marquette Univ Dent Reflections
+Imago Mundi
+Imbonezamuryango
+Immigr Minor
+Immun Ageing
+Immun Infekt
+Immunity
+Immunobiol Suppl
+Immunobiology
+Immunochemistry
+Immunodefic Rev
+Immunodeficiency
+Immunogenetics
+Immunohematol
+Immunol Allergy Clin North Am
+Immunol Cell Biol
+Immunol Clin
+Immunol Commun
+Immunol Invest
+Immunol Lett
+Immunol Res
+Immunol Rev
+Immunol Ser
+Immunol Suppl
+Immunol Today
+Immunology
+Immunome Res
+Immunomethods
+Immunopharmacol Immunotoxicol
+Immunopharmacology
+Immunotechnology
+Impact HIV
+Impact Sci Soc
+Implant Dent
+Implant Soc
+Implantologist
+Important Adv Oncol
+Imprensa Medica
+Impressions (Orange)
+Imprimatur
+Imprint
+Imprint (N Y N Y 1976)
+Improv Hum Performance Q
+Impulse (Sydney)
+In Fire Ethics
+In Health
+In Point Fact
+In Pract
+In Silico Biol
+In Vitr Mol Toxicol
+In Vitro
+In Vitro Cell Dev Biol
+In Vitro Cell Dev Biol Anim
+In Vitro Cell Dev Biol Plant
+In Vitro Monogr
+In Vivo
+InTouch
+Inchiesta
+Ind Archaeol Rev
+Ind Eng
+Ind Eng Chem Res
+Ind Free China
+Ind Health
+Ind Health Care
+Ind Health Care (Cambridge Ma)
+Ind Health Mon
+Ind Hyg Newsl
+Ind Labor Relat Rev
+Ind Med Gaz
+Ind Med Surg
+Ind Rob
+Ind Week
+Indent [Engl]
+Indexer
+India Q
+Indian Arch
+Indian Econ J
+Indian Econ Rev
+Indian Econ Soc Hist Rev
+Indian Heart J
+Indian Heart J Teach Ser
+Indian Hist
+Indian J Am Stud
+Indian J Anim Sci
+Indian J Behav
+Indian J Biochem
+Indian J Biochem Biophys
+Indian J Cancer
+Indian J Chem
+Indian J Chest Dis
+Indian J Chest Dis Allied Sci
+Indian J Child Health
+Indian J Clin Psychol
+Indian J Dent Res
+Indian J Dermatol
+Indian J Dermatol Venereol Leprol
+Indian J Econ
+Indian J Environ Health
+Indian J Exp Biol
+Indian J Gastroenterol
+Indian J Gend Stud
+Indian J Hist Med
+Indian J Hist Sci
+Indian J Hosp Pharm
+Indian J Ind Relat
+Indian J Lepr
+Indian J Malariol
+Indian J Matern Child Health
+Indian J Med Educ
+Indian J Med Ethics
+Indian J Med Microbiol
+Indian J Med Res
+Indian J Med Sci
+Indian J Med Surg
+Indian J Nutr Diet
+Indian J Ophthalmol
+Indian J Otolaryngol
+Indian J Pathol Bacteriol
+Indian J Pathol Microbiol
+Indian J Pediatr
+Indian J Pharm
+Indian J Pharm Sci
+Indian J Pharmacol
+Indian J Phys Anthropol Hum Genet
+Indian J Physiol Pharmacol
+Indian J Prev Soc Med
+Indian J Psychiatry
+Indian J Psychol
+Indian J Public Adm
+Indian J Public Health
+Indian J Quant Econ
+Indian J Sex Transm Dis
+Indian J Soc Res
+Indian J Soc Sci
+Indian J Soc Work
+Indian J Surg
+Indian Med J
+Indian Med Trib
+Indian Pediatr
+Indian Pol Sci Rev
+Indian Pract
+Indian Psychol Rev
+Indian Sociol Bull
+Indian Vet J
+Indiana Code Indiana
+Indiana Folkl
+Indiana Int Comp Law Rev
+Indiana Law J
+Indiana Law Rev
+Indiana Mag Hist
+Indiana Med
+Indiana Med Hist Q
+Indiana Nurse
+Indiana Soc Stud Q
+Indicator
+Individ Psychol
+Indo Iran J
+Indo Koten Kenkyu
+Indones J Geogr
+Indones Q
+Indoor Air
+Inf Behav
+Inf Demogr
+Inf Dent
+Inf Geogr
+Inf Hist
+Inf Hist Art
+Inf Litt
+Inf Manage
+Inf Med Paramed (Montreal)
+Inf Medicas
+Inf Odontostomatol
+Inf Orthod Kieferorthop
+Inf Priv
+Inf Process Med Imaging
+Inf Psychiatr
+Inf Raumentwickl
+Inf Sci (Ny)
+Inf Summ
+Infancy
+Infant Ment Health J
+Infants Young Child
+Infanz Anorm
+Infect Agents Dis
+Infect Control
+Infect Control Can
+Infect Control Dig
+Infect Control Hosp Epidemiol
+Infect Control Rounds
+Infect Control Urol Care
+Infect Control Wkly
+Infect Dis
+Infect Dis Clin North Am
+Infect Dis Obstet Gynecol
+Infect Genet Evol
+Infect Immun
+Infect Med
+Infection
+Infertility
+Infez Med
+Infirm Aux
+Infirm Can
+Infirm Fr
+Infirm Haiti
+Infirm Que
+Infirmiere
+Infirmiers
+Inflamm Allergy Drug Targets
+Inflamm Bowel Dis
+Inflamm Res
+Inflammation
+Inflammopharmacology
+Info Trends
+Infocare
+Inform
+Inform Prim Care
+Inforum
+Infrared Phys
+Infusionsther Klin Ernahr
+Infusionsther Klin Ernahr Sonderh
+Infusionsther Transfusionsmed
+Infusionstherapie
+Ingu Pogon Nonjip
+Ingu munje nonjip
+Inhal Toxicol
+Inhaled Part
+Initiat Reprod Health Policy
+Initiatives Popul
+Inj Control Saf Promot
+Inj Prev
+Injury
+Inland Seas
+Inn Med
+Innov Oncol Nurs
+Innovations
+Inorg Chem
+Inorg Chim Acta
+Inquiry
+Inquiry (Oslo)
+Insect Biochem
+Insect Biochem Mol Biol
+Insect Mol Biol
+Insectes Sociaux
+Insight
+Inst Vol Feed
+Institutions
+Instr Course Lect
+Insur Couns J
+Insur Law J
+Insur Math Econ
+Int Abstr Surg
+Int Adv Surg Oncol
+Int Aff
+Int Anesthesiol Clin
+Int Angiol
+Int Arch Allergy Appl Immunol
+Int Arch Allergy Immunol
+Int Arch Arbeitsmed
+Int Arch Gewerbepathol Gewerbehyg
+Int Arch Occup Environ Health
+Int Asianforum
+Int Biodeterior Biodegradation
+Int Braz J Urol
+Int Child Health
+Int Clin Psychopharmacol
+Int Comp Law Q
+Int Congr Ser
+Int Demogr
+Int Dent J
+Int Dev Rev
+Int Dig Health Legis
+Int Disabil Stud
+Int Drug Ther Newsl
+Int Econ Insights
+Int Econ J
+Int Endod J
+Int Fam Plan Perspect
+Int Fam Plann Dig
+Int Fam Plann Persp
+Int Health Dev
+Int Health News
+Int Heart J
+Int Hist Nurs J
+Int Hist Rev
+Int Immunol
+Int Immunopharmacol
+Int J
+Int J Addict
+Int J Adolesc Med Health
+Int J Adolesc Youth
+Int J Adult Orthodon Orthognath Surg
+Int J Adv Couns
+Int J Afr Hist Stud
+Int J Aging Hum Dev
+Int J Air Pollut
+Int J Androl
+Int J Anesth
+Int J Anthropol
+Int J Antimicrob Agents
+Int J Appl Philos
+Int J Appl Radiat Isot
+Int J Artif Organs
+Int J Audiol
+Int J Aviat Psychol
+Int J Behav Dev
+Int J Behav Med
+Int J Behav Nutr Phys Act
+Int J Bifurcat Chaos
+Int J Biochem
+Int J Biochem Cell Biol
+Int J Biol Macromol
+Int J Biol Markers
+Int J Biol Res Pregnancy
+Int J Biol Sci
+Int J Biomed Comput
+Int J Biometeorol
+Int J Cancer
+Int J Cancer Suppl
+Int J Card Imaging
+Int J Cardiol
+Int J Cardiovasc Imaging
+Int J Cardiovasc Intervent
+Int J Cell Cloning
+Int J Child Psychother
+Int J Childbirth Educ
+Int J Chronobiol
+Int J Circumpolar Health
+Int J Clin Exp Hypn
+Int J Clin Lab Res
+Int J Clin Monit Comput
+Int J Clin Oncol
+Int J Clin Pharmacol
+Int J Clin Pharmacol Biopharm
+Int J Clin Pharmacol Res
+Int J Clin Pharmacol Ther
+Int J Clin Pharmacol Ther Toxicol
+Int J Clin Pract
+Int J Clin Pract Suppl
+Int J Cogn Ergon
+Int J Colorectal Dis
+Int J Comp Psychol
+Int J Comp Sociol
+Int J Comput Dent
+Int J Contemp Sociol
+Int J Control
+Int J Cosmet Sci
+Int J Crude Drug Res
+Int J Dent Hyg
+Int J Dent Symp
+Int J Dermatol
+Int J Dev Biol
+Int J Dev Neurosci
+Int J Eat Disord
+Int J Educ Dev
+Int J Emerg Ment Health
+Int J Environ Anal Chem
+Int J Environ Health Res
+Int J Environ Stud
+Int J Epidemiol
+Int J Equilib Res
+Int J Equity Health
+Int J Exp Diabesity Res
+Int J Exp Diabetes Res
+Int J Exp Pathol
+Int J Fertil
+Int J Fertil Menopausal Stud
+Int J Fertil Womens Med
+Int J Food Microbiol
+Int J Food Sci Nutr
+Int J Forecast
+Int J Forensic Dent
+Int J Gastrointest Cancer
+Int J Geogr Inf Sci
+Int J Geriatr Psychiatry
+Int J Group Psychother
+Int J Gynaecol Obstet
+Int J Gynecol Cancer
+Int J Gynecol Pathol
+Int J Health Care Finance Econ
+Int J Health Care Qual Assur
+Int J Health Care Qual Assur Inc Leadersh Health Serv
+Int J Health Educ
+Int J Health Geogr
+Int J Health Plann Manage
+Int J Health Sci
+Int J Health Serv
+Int J Hematol
+Int J Hist Sport
+Int J Hum Comput Interact
+Int J Hum Comput Stud
+Int J Hydrogen Energy
+Int J Hyg Environ Health
+Int J Hyperthermia
+Int J Immunogenet
+Int J Immunopathol Pharmacol
+Int J Immunopharmacol
+Int J Impact Eng
+Int J Impot Res
+Int J Ind Ergon
+Int J Infect Dis
+Int J Infrared Millimeter Waves
+Int J Inj Contr Saf Promot
+Int J Instr Media
+Int J Lang Commun Disord
+Int J Law Fam
+Int J Law Psychiatry
+Int J Legal Med
+Int J Lepr
+Int J Lepr Other Mycobact Dis
+Int J Low Extrem Wounds
+Int J Man Mach Stud
+Int J Mass Emerg Disasters
+Int J Mass Spectrom
+Int J Mass Spectrom Ion Process
+Int J Med Inform
+Int J Med Law
+Int J Med Microbiol
+Int J Med Sci
+Int J Ment Health
+Int J Ment Health Nurs
+Int J Methods Psychiatr Res
+Int J Microcirc Clin Exp
+Int J Middle East Stud
+Int J Mod Phys A
+Int J Mol Med
+Int J Nanomedicine
+Int J Neural Syst
+Int J Neurol
+Int J Neuropharmacol
+Int J Neuropsychiatry
+Int J Neuropsychopharmacol
+Int J Neurosci
+Int J Nucl Med Biol
+Int J Nurs Educ Scholarsh
+Int J Nurs Pract
+Int J Nurs Stud
+Int J Nurs Terminol Classif
+Int J Obes
+Int J Obes (Lond)
+Int J Obes Relat Metab Disord
+Int J Obstet Anesth
+Int J Occup Environ Health
+Int J Occup Health Saf
+Int J Occup Med Environ Health
+Int J Occup Saf Ergon
+Int J Offender Ther Comp Criminol
+Int J Oncol
+Int J Oral Implantol
+Int J Oral Maxillofac Implants
+Int J Oral Maxillofac Surg
+Int J Oral Myol
+Int J Oral Surg
+Int J Orofacial Myology
+Int J Orthod
+Int J Orthod Milwaukee
+Int J Paediatr Dent
+Int J Palliat Nurs
+Int J Pancreatol
+Int J Parasitol
+Int J Partial Hosp
+Int J Pediatr Nephrol
+Int J Pediatr Otorhinolaryngol
+Int J Pept Protein Res
+Int J Periodontics Restorative Dent
+Int J Pharm
+Int J Phytoremediation
+Int J Plant Sci
+Int J Polit
+Int J Popul Geogr
+Int J Primatol
+Int J Prosthodont
+Int J Protein Res
+Int J Psychiatr Nurs Res
+Int J Psychiatry
+Int J Psychiatry Med
+Int J Psychoanal
+Int J Psychoanal Psychother
+Int J Psychol
+Int J Psychol Relig
+Int J Psychophysiol
+Int J Psychosom
+Int J Qual Health Care
+Int J Quantum Chem
+Int J Quantum Chem Quantum Biol Symp
+Int J Rad Appl Instrum B
+Int J Rad Appl Instrum D
+Int J Rad Appl Instrum [A]
+Int J Radiat Biol
+Int J Radiat Biol Relat Stud Phys Chem Med
+Int J Radiat Oncol Biol Phys
+Int J Rehabil Res
+Int J Remote Sens
+Int J STD AIDS
+Int J Sexol
+Int J Soc Econ
+Int J Soc Educ
+Int J Soc Lang
+Int J Soc Psychiatry
+Int J Sociol Fam
+Int J Sociol Soc Policy
+Int J Spec Educ
+Int J Sport Nutr
+Int J Sport Nutr Exerc Metab
+Int J Sport Psychol
+Int J Sports Med
+Int J Study Anim Probl
+Int J Supercomput Appl
+Int J Surg Investig
+Int J Surg Pathol
+Int J Syst Bacteriol
+Int J Syst Evol Microbiol
+Int J Syst Sci
+Int J Technol Aging
+Int J Technol Assess Health Care
+Int J Technol Manag
+Int J Tissue React
+Int J Toxicol
+Int J Trauma Nurs
+Int J Tuberc Lung Dis
+Int J Urban Reg Res
+Int J Urol
+Int J Vitam Nutr Res
+Int J Vitam Nutr Res Suppl
+Int J Womens Stud
+Int J Zoonoses
+Int Labour Rev
+Int Libr Rev
+Int MS J
+Int Marit Health
+Int Microbiol
+Int Migr
+Int Migr Rev
+Int Nurs Rev
+Int Ophthalmol
+Int Ophthalmol Clin
+Int Organ
+Int Orthop
+Int Pharmacopsychiatry
+Int Philos Q
+Int Plann Parenthood News
+Int Psychiatry Clin
+Int Psychogeriatr
+Int Q Community Health Educ
+Int Rec Med
+Int Rec Med Gen Pract Clin
+Int Reg Sci Rev
+Int Rehabil Med
+Int Rescuer
+Int Rev
+Int Rev Connect Tissue Res
+Int Rev Cytol
+Int Rev Cytol Suppl
+Int Rev Educ
+Int Rev Exp Pathol
+Int Rev Immunol
+Int Rev Missions
+Int Rev Mod Sociol
+Int Rev Nat Fam Plann
+Int Rev Neurobiol
+Int Rev Physiol
+Int Rev Psychiatry
+Int Rev Psychoanal
+Int Rev Res Ment Retard
+Int Rev Soc Hist
+Int Rev Trop Med
+Int Sci Rev Ser
+Int Secur
+Int Semin Surg Oncol
+Int Ser Monogr Oral Biol
+Int Soc Sci J
+Int Soc Sci Rev
+Int Soc Secur Rev
+Int Soc Work
+Int Social Rev
+Int Sociol
+Int Spect
+Int Stat Rev
+Int Stud Q
+Int Surg
+Int Symp Fluorid Prev Dent
+Int Tinnitus J
+Int Urogynecol J Pelvic Floor Dysfunct
+Int Urol Nephrol
+Int Wildl
+Int Wound J
+Int Z Angew Physiol
+Int Z Klin Pharmakol Ther Toxikol
+Int Z Phys Med Rehabil
+Int Z Vitam Ernahrungsforsch Beih
+Int Z Vitaminforsch
+Int Z Vitaminforsch Beih
+Integr Cancer Ther
+Integr Environ Assess Manag
+Integr Healthc Rep
+Integr Physiol Behav Sci
+Integr Psychiatry
+Integration
+Intellect
+Intellect Dig
+Intellect Prop J
+Intelligence
+Intensive Care Med
+Intensive Care Nurs
+Intensive Care World
+Intensive Crit Care Nurs
+Intensivmed Prax
+Inter Am Econ Aff
+Inter Des
+Inter Econ
+Inter Nord
+Interact Comput
+Interaction
+Interam Rev Bibliogr
+Interciencia
+Intercom (Des Moines)
+Interdiscip Sci Rev
+Interferon
+Interiors
+Intermediar
+Intern Med
+Intern Med J
+Internet Healthc Strateg
+Internist
+Internist (Berl)
+Internist Prax
+Intersezioni
+Interuniv Fac Work Conf
+Intervirology
+Intus
+Invasion Metastasis
+Inverse Probl
+Invert Neurosci
+Invest Cell Pathol
+Invest Clin
+Invest Econ
+Invest Hist
+Invest Med Int
+Invest New Drugs
+Invest Ophthalmol
+Invest Ophthalmol Vis Sci
+Invest Radiol
+Invest Urol
+Investig Cienc
+Investig Urol (Berl)
+Investor Owned Hosp Rev
+Ion Channels
+Ion Exch Membr
+Iowa Code Annot Iowa
+Iowa Dent Bull
+Iowa Dent J
+Iowa Law Rev
+Iowa Med
+Iowa Orthop J
+Iowa State Univ Vet
+Ir Bank Rev
+Ir Econ Soc Hist
+Ir Geogr
+Ir Hist Stud
+Ir J Med Sci
+Ir J Psychol Med
+Ir Law Rep Mon
+Ir Med J
+Ir Nurs Hosp World
+Ir Nurs News
+Ir Nurses J
+Ir Sword
+Ir Vet J
+Iran J Public Health
+Iran Stud
+Iraq
+Iraqi Dent J
+Irc Med Sci Reprod Obstet Gynecol
+Ire Trans Biomed Electron
+Irish Theol Q
+Iryo
+Isis
+Isl Mag
+Isla
+Islam Q
+Islam Stud
+Isokinet Exerc Sci
+Isot Geosci
+Isotopes Environ Health Stud
+Isozymes Curr Top Biol Med Res
+Isr Ann Psychiatr Relat Discip
+Isr J Chem
+Isr J Dent Sci
+Isr J Med Sci
+Isr J Psychiatry Relat Sci
+Isr J Zool
+Isr Law Rev
+Isr Med Assoc J
+Isr Med J
+Isr Orient Stud
+Isr Soc Sci Res
+Issled Genet
+Issue Brief (Commonw Fund)
+Issue Brief (George Wash Univ Med Cent Ensuring Solut Alcohol Probl)
+Issue Brief (Inst Health Care Costs Solut)
+Issue Brief (Mass Health Policy Forum)
+Issue Brief (Public Policy Inst (Am Assoc Retired Pers))
+Issue Brief Cent Medicare Educ
+Issue Brief Cent Stud Health Syst Change
+Issue Brief George Wash Univ Cent Health Serv Res Policy
+Issue Brief Health Policy Track Serv
+Issue Brief Natl Health Policy Forum
+Issue brief (Grantmakers Health)
+Issues
+Issues (St Louis Mo)
+Issues Brief (Alan Guttmacher Inst)
+Issues Ciminol
+Issues Compr Pediatr Nurs
+Issues Emerg Health Technol
+Issues Ethics
+Issues Health Care
+Issues Health Care Women
+Issues Law Med
+Issues Med Ethics
+Issues Ment Health Nurs
+Issues Reprod Genet Eng
+Issues Sci Technol
+Issues Stud
+Istanbul Tip Fak Mecmuasi
+Istanbul Univ Dishekim Fak Derg
+Istor Biol Issled
+Istor Cas
+Istor Pregl
+Istor SSSR
+Istor Zapisi
+Istorija
+Ital Contemp
+Ital Gen Rev Dermatol
+Ital Gen Rev Otorhinolaryngol
+Ital Heart J
+Ital Heart J Suppl
+Ital J Anat Embryol
+Ital J Biochem
+Ital J Gastroenterol
+Ital J Gastroenterol Hepatol
+Ital J Neurol Sci
+Ital J Orthop Traumatol
+Ital J Orthop Traumatol Suppl
+Ital J Surg Sci
+Ital Medioev Um
+Italy Doc Notes
+Items
+Items Issues
+Itogi Nauk
+Iugosl Physiol Pharmacol Acta
+Iura
+Iustitia
+Iyodenshi To Seitai Kogaku
+Izv Akad Gruz Ssr. Ser Biol
+Izv Akad Nauk Kirg Ssr [Biol]
+Izv Akad Nauk SSSR Biol
+Izv Akad Nauk SSSR Ser Geogr
+Izv Akad Nauk Ser Biol
+Izv Akad Nauk Sssr Otdelenie Khim Nauk
+Izv Bulg Akad Naukite Sofia Nauchnoizsledovatelski Inst protivorak antibiot
+Izv Inst Fiziol (Sofiia)
+Izv Inst Istor BKP
+Izv Meditsinskite Inst Bulg Akad Naukite Sofia Otd Biol Meditsinski Nauki
+Izv Mikrobiol Inst (Sofiia)
+Izv Russ Geogr Obshchestva
+Izv Seriia Fiziol Meditsiny Qazaq SSR Ghylum Akad
+Izv Vses Geogr Obshchestva
+J AAPOS
+J AHIMA
+J AOAC Int
+J APDSA (Tokyo)
+J Abdom Surg
+J Abnorm Child Psychol
+J Abnorm Psychol
+J Abnorm Soc Psychol
+J Acad Gen Dent
+J Acad Hosp Adm
+J Accid Emerg Med
+J Acoust Soc Am
+J Acquir Immune Defic Syndr
+J Acquir Immune Defic Syndr Hum Retrovirol
+J Addict Dis
+J Adhes Dent
+J Adolesc
+J Adolesc Health
+J Adolesc Health Care
+J Adolesc Res
+J Adv Med Surg Nurs
+J Adv Nurs
+J Advert Res
+J Aerosol Med
+J Aerosol Sci
+J Aerosp Eng
+J Affect Disord
+J Afr Econ
+J Afr Hist
+J Afr Law
+J Afr Stud
+J Afro Am Hist Geneal Soc
+J Aging Health
+J Aging Jud
+J Aging Phys Act
+J Aging Soc Policy
+J Aging Stud
+J Agric Environ Ethics
+J Agric Ethics
+J Agric Food Chem
+J Agric Res
+J Agric Saf Health
+J Agric Tradit Bot Appl
+J Agric Trop Bot Appl
+J Agromedicine
+J Air Med Transp
+J Air Pollut Control Assoc
+J Air Waste Manag Assoc
+J Air Waste Manage Assoc
+J Aircr
+J Ala Dent Assoc
+J Albert Einstein Med Cent (Phila)
+J Alcohol Drug Educ
+J All India Dent Assoc
+J All India Ophthalmol Soc
+J Allergy
+J Allergy Clin Immunol
+J Allied Health
+J Altern Complement Med
+J Alzheimers Dis
+J Am Acad Audiol
+J Am Acad Child Adolesc Psychiatry
+J Am Acad Child Psychiatry
+J Am Acad Dermatol
+J Am Acad Gnathol Orthop
+J Am Acad Gold Foil Oper
+J Am Acad Nurse Pract
+J Am Acad Orthop Surg
+J Am Acad Physician Assist
+J Am Acad Psychiatry Law
+J Am Acad Psychoanal
+J Am Acad Psychoanal Dyn Psychiatry
+J Am Acad Relig
+J Am Anim Hosp Assoc
+J Am Assoc Gynecol Laparosc
+J Am Assoc Lab Anim Sci
+J Am Assoc Med Transcr
+J Am Assoc Nephrol Nurses Tech
+J Am Assoc Nurse Anesth
+J Am Aud Soc
+J Am Audiol Soc
+J Am Board Fam Med
+J Am Board Fam Pract
+J Am Chem Soc
+J Am Coll Cardiol
+J Am Coll Dent
+J Am Coll Health
+J Am Coll Health Assoc
+J Am Coll Neuropsychiatr
+J Am Coll Nutr
+J Am Coll Surg
+J Am Coll Toxicol
+J Am Cult
+J Am Dent Assoc
+J Am Dent Assoc (Ed Ital)
+J Am Dent Hyg Assoc
+J Am Dent Soc Anesthesiol
+J Am Diet Assoc
+J Am Ethn Hist
+J Am Folk
+J Am Geriatr Soc
+J Am Health Care
+J Am Health Care Assoc
+J Am Health Policy
+J Am Hist
+J Am Hist Soc Ger Russ
+J Am Inst Homeopath
+J Am Inst Plann
+J Am Insur
+J Am Intraocul Implant Soc
+J Am Med Assoc
+J Am Med Dir Assoc
+J Am Med Inform Assoc
+J Am Med Rec Assoc
+J Am Med Technol
+J Am Med Womens Assoc
+J Am Mosq Control Assoc
+J Am Mosq Control Assoc Suppl
+J Am Oil Chem Soc
+J Am Optom Assoc
+J Am Orient Soc
+J Am Osteopath Assoc
+J Am Paraplegia Soc
+J Am Pharm Assoc
+J Am Pharm Assoc (Wash DC)
+J Am Pharm Assoc (Wash)
+J Am Pharm Assoc Am Pharm Assoc
+J Am Pharm Assoc Am Pharm Assoc (Baltim)
+J Am Phys Ther Assoc
+J Am Plann Assoc
+J Am Podiatr Med Assoc
+J Am Podiatry Assoc
+J Am Psychiatr Nurses Assoc
+J Am Psychoanal Assoc
+J Am Sci Affil
+J Am Soc Echocardiogr
+J Am Soc Geriatr Dent
+J Am Soc Hortic Sci
+J Am Soc Inf Sci
+J Am Soc Mass Spectrom
+J Am Soc Nephrol
+J Am Soc Prev Dent
+J Am Soc Psychosom Dent Med
+J Am Soc Study Orthod
+J Am Stat Assoc
+J Am Stud
+J Am Vener Dis Assoc
+J Am Vet Med Assoc
+J Am Water Works Assoc
+J Ambul Care Manage
+J Ambul Care Mark
+J Anal Psychol
+J Anal Toxicol
+J Anat
+J Anat Soc India
+J Androl
+J Anesth
+J Anglocont Dent Soc
+J Anim Breed Genet
+J Anim Ecol
+J Anim Physiol Anim Nutr (Berl)
+J Anim Sci
+J Anthropol Res
+J Anti Aging Med
+J Antibiot (Tokyo)
+J Antibiot [B]
+J Antimicrob Chemother
+J Anxiety Disord
+J Appl Anim Welf Sci
+J Appl Bacteriol
+J Appl Behav Anal
+J Appl Behav Sci
+J Appl Biochem
+J Appl Biomater
+J Appl Biomech
+J Appl Cardiol
+J Appl Clin Med Phys
+J Appl Commun Res
+J Appl Dev Psychol
+J Appl Genet
+J Appl Gerontol
+J Appl Meas
+J Appl Microbiol
+J Appl Nutr
+J Appl Philos
+J Appl Phys
+J Appl Physiol
+J Appl Probab
+J Appl Psychol
+J Appl Soc Psychol
+J Appl Soc Sci
+J Appl Stat
+J Appl Toxicol
+J Arab Aff
+J Archaeol Sci
+J Archit Plann Res
+J Arid Environ
+J Ariz Hist
+J Ark Med Soc
+J Arnold Arbor
+J Arthroplasty
+J Artif Organs
+J Asian Afr Stud
+J Asian Econ
+J Asian Fed Obstet Gynaecol
+J Asian Nat Prod Res
+J Asian Stud
+J Asiat
+J Asiat Soc
+J Assam Science Society
+J Assist Reprod Genet
+J Assoc Acad Minor Phys
+J Assoc Adv Med Instrum
+J Assoc Care Child Health
+J Assoc Care Child Hosp
+J Assoc Genet Technol
+J Assoc Healthc Philanthr
+J Assoc Hosp Med Educ
+J Assoc Med Illus
+J Assoc Med Women India
+J Assoc Nurses AIDS Care
+J Assoc Off Anal Chem
+J Assoc Pediatr Oncol Nurses
+J Assoc Pers Sev Handicaps
+J Assoc Phys Ment Rehabil
+J Assoc Physicians India
+J Assoc Res Otolaryngol
+J Asthma
+J Asthma Res
+J Atheroscler Res
+J Atheroscler Thromb
+J Athl Train
+J Atmos Chem
+J Atten Disord
+J Aud Res
+J Audio Eng Soc
+J Audiov Media Med
+J Aust Inst Surg Dent Tech
+J Aust Popul Assoc
+J Autism Child Schizophr
+J Autism Dev Disord
+J Autoimmun
+J Autoimmune Dis
+J Auton Nerv Syst
+J Auton Pharmacol
+J Aux Odontol
+J Aviat Med
+J Ayub Med Coll Abbottabad
+J Back Musculoskeletal Rehabil
+J Bacteriol
+J Bahrain Med Soc
+J Balt Stud
+J Baltimore Coll Dent Surg
+J Basic Clin Physiol Pharmacol
+J Basic Microbiol
+J Behav Assess
+J Behav Econ
+J Behav Health Serv Res
+J Behav Med
+J Behav Ther Exp Psychiatry
+J Belge Med Phys
+J Belge Med Phys Rehabil
+J Belge Med Phys Rhumatol
+J Belge Radiol
+J Belge Rhumatol Med Phys
+J Belge Urol
+J Bergen Cty Dent Soc
+J Biblic Ethics Med
+J Bioact Compat Polym
+J Biochem (Tokyo)
+J Biochem Biophys Methods
+J Biochem Mol Biol
+J Biochem Mol Biol Biophys
+J Biochem Mol Toxicol
+J Biochem Toxicol
+J Biocommun
+J Bioenerg
+J Bioenerg Biomembr
+J Bioeng
+J Bioeth
+J Bioeth Inq
+J Biogeogr
+J Bioinform Comput Biol
+J Biol
+J Biol Buccale
+J Biol Chem
+J Biol Educ
+J Biol Inorg Chem
+J Biol Photogr
+J Biol Photogr Assoc
+J Biol Regul Homeost Agents
+J Biol Response Mod
+J Biol Rhythms
+J Biol Sci
+J Biol Stand
+J Biolaw Bus
+J Biolumin Chemilumin
+J Biomater Appl
+J Biomater Dent
+J Biomater Sci Polym Ed
+J Biomech
+J Biomech Eng
+J Biomed Biotechnol
+J Biomed Eng
+J Biomed Inform
+J Biomed Mater Res
+J Biomed Mater Res A
+J Biomed Mater Res B Appl Biomater
+J Biomed Opt
+J Biomed Sci
+J Biomol NMR
+J Biomol Screen
+J Biomol Struct Dyn
+J Biomol Tech
+J Biopharm Stat
+J Biophys Biochem Cytol
+J Biosci
+J Biosci Bioeng
+J Biosoc Sci
+J Biosoc Sci Suppl
+J Biotechnol
+J Black Health Perspect
+J Black Psychol
+J Black Stud
+J Bone Joint Surg
+J Bone Joint Surg Am
+J Bone Joint Surg Br
+J Bone Miner Metab
+J Bone Miner Res
+J Br Endod Soc
+J Br Fer Soc
+J Br Interplanet Soc
+J Br Menopause Soc
+J Br Stud
+J Bras Doencas Torac
+J Bras Ginecol
+J Bras Med
+J Bras Neurol
+J Bras Psiquiatr
+J Bryol
+J Buddh Ethics
+J Burn Care Rehabil
+J Burn Care Res
+J Bus
+J Bus Ethics
+J Bus Psychol
+J Bus Res
+J Bus Soc Stud
+J Bus Strategy
+J CANNT
+J Calif Alliance Ment Ill
+J Calif Dent Assoc
+J Calif Gt Basin Anthropol
+J Can Assoc Radiol
+J Can Church Hist Soc
+J Can Dent Assoc
+J Can Diet Assoc
+J Can Stud
+J Cancer Educ
+J Cancer Epidemiol Prev
+J Cancer Res
+J Cancer Res Clin Oncol
+J Capillary Electrophor
+J Carcinog
+J Card Fail
+J Card Surg
+J Cardiogr
+J Cardiogr Suppl
+J Cardiol
+J Cardiol Suppl
+J Cardiopulm Rehabil
+J Cardiothorac Anesth
+J Cardiothorac Vasc Anesth
+J Cardiovasc Diagn Proced
+J Cardiovasc Electrophysiol
+J Cardiovasc Magn Reson
+J Cardiovasc Manag
+J Cardiovasc Med
+J Cardiovasc Med (Hagerstown)
+J Cardiovasc Nurs
+J Cardiovasc Pharmacol
+J Cardiovasc Pharmacol Ther
+J Cardiovasc Risk
+J Cardiovasc Surg (Torino)
+J Caribb Hist
+J Case Manag
+J Cataract Refract Surg
+J Cathol Nurses Guild Engl Wales
+J Cell Biochem
+J Cell Biochem Suppl
+J Cell Biol
+J Cell Comp Physiol
+J Cell Mol Med
+J Cell Physiol
+J Cell Physiol Suppl
+J Cell Sci
+J Cell Sci Suppl
+J Cereb Blood Flow Metab
+J Charles H. Tweed Int Found
+J Chem Doc
+J Chem Ecol
+J Chem Educ
+J Chem Inf Comput Sci
+J Chem Inf Model
+J Chem Neuroanat
+J Chem Phys
+J Chem Soc
+J Chem Soc Chem Commun
+J Chem Soc [Perkin 1]
+J Chem Technol Biotechnol
+J Chem Thermodyn
+J Chemother
+J Cherokee Stud
+J Chester Archaeol Soc
+J Chiba Med Soc
+J Child Adolesc Psychiatr Ment Health Nurs
+J Child Adolesc Psychiatr Nurs
+J Child Adolesc Psychopharmacol
+J Child Asthma Res Inst Hosp Denver
+J Child Fam Nurs
+J Child Health Care
+J Child Lang
+J Child Neurol
+J Child Psychiatry
+J Child Psychol Psychiatry
+J Child Sex Abus
+J Chin Med Assoc
+J Chin Univ Hong Kong
+J Chir (Paris)
+J Christ Med Assoc India
+J Christ Nurs
+J Chromatogr
+J Chromatogr A
+J Chromatogr B Analyt Technol Biomed Life Sci
+J Chromatogr B Biomed Appl
+J Chromatogr B Biomed Sci Appl
+J Chromatogr Sci
+J Chromatogr Suppl
+J Chronic Dis
+J Church State
+J Circadian Rhythms
+J Clim
+J Clin Anesth
+J Clin Apher
+J Clin Chem Clin Biochem
+J Clin Child Adolesc Psychol
+J Clin Child Psychol
+J Clin Comput
+J Clin Densitom
+J Clin Dent
+J Clin Dysmorphol
+J Clin Endocrinol
+J Clin Endocrinol Metab
+J Clin Eng
+J Clin Epidemiol
+J Clin Ethics
+J Clin Exp Neuropsychol
+J Clin Exp Psychopathol
+J Clin Exp Psychopathol Q Rev Psychiatry Neurol
+J Clin Forensic Med
+J Clin Gastroenterol
+J Clin Hosp Pharm
+J Clin Hypertens
+J Clin Hypertens (Greenwich)
+J Clin Immunol
+J Clin Invest
+J Clin Issues Psychol
+J Clin Lab Anal
+J Clin Lab Immunol
+J Clin Laser Med Surg
+J Clin Microbiol
+J Clin Monit
+J Clin Monit Comput
+J Clin Neuroophthalmol
+J Clin Neurophysiol
+J Clin Neuropsychol
+J Clin Neurosci
+J Clin Nurs
+J Clin Oncol
+J Clin Orthod
+J Clin Pathol
+J Clin Pathol Suppl (Assoc Clin Pathol)
+J Clin Pathol Suppl (R Coll Pathol)
+J Clin Pathol Suppl Coll Pathol
+J Clin Pediatr Dent
+J Clin Periodontol
+J Clin Pharm Ther
+J Clin Pharmacol
+J Clin Pharmacol J New Drugs
+J Clin Pharmacol New Drugs
+J Clin Psychiatry
+J Clin Psychol
+J Clin Psychol Med Settings
+J Clin Psychopharmacol
+J Clin Res Drug Dev
+J Clin Rheumatol
+J Clin Sleep Med
+J Clin Stomatol Conf
+J Clin Ultrasound
+J Clin Virol
+J Cogn Neurosci
+J Coll Gen Pract
+J Coll Physicians Surg Pak
+J Coll Radiol Australas
+J Coll Sci Teach
+J Coll Univ Law
+J Collect Negotiations Public Sect
+J Colloid Interface Sci
+J Colloid Sci
+J Colo Dent Assoc
+J Comb Chem
+J Comb Optim
+J Common Mark Stud
+J Commonw Comp Polit
+J Commonw Polit Stud
+J Commun
+J Commun Dis
+J Commun Disord
+J Commun Inq
+J Community Appl Soc Psychol
+J Community Dev Soc
+J Community Health
+J Community Health Nurs
+J Community Psychol
+J Comp Econ
+J Comp Fam Stud
+J Comp Neurol
+J Comp Pathol
+J Comp Pathol Ther
+J Comp Physiol A Neuroethol Sens Neural Behav Physiol
+J Comp Physiol Psychol
+J Comp Physiol [A]
+J Comp Physiol [B]
+J Comp Psychol
+J Compliance Health Care
+J Comput Aided Mol Des
+J Comput Assist Microsc
+J Comput Assist Tomogr
+J Comput Biol
+J Comput Chem
+J Comput Electron
+J Comput Neurosci
+J Comput Tomogr
+J Conf Workshop
+J Conflict Resolut
+J Conn State Dent Assoc
+J Consult Clin Psychol
+J Consult Psychol
+J Consum Aff
+J Consum Res
+J Contam Hydrol
+J Contemp Afr Stud
+J Contemp Asia
+J Contemp Dent Pract
+J Contemp Ethnogr
+J Contemp Health Law Policy
+J Contemp Hist
+J Contemp Law
+J Contin Educ Health Prof
+J Contin Educ Nurs
+J Contra Costa Dent Soc
+J Contracept
+J Control Release
+J Corp Law
+J Cosmet Laser Ther
+J Cosmet Sci
+J Couns Dev
+J Couns Psychol
+J Craniofac Genet Dev Biol
+J Craniofac Genet Dev Biol Suppl
+J Craniofac Surg
+J Craniomandib Disord
+J Craniomandibular Pract
+J Craniomaxillofac Surg
+J Craniomaxillofac Trauma
+J Creat Behav
+J Crim Justice
+J Crim Law Criminol
+J Crim Law Criminol Police Sci
+J Crit Care
+J Crit Illn
+J Cross Cult Gerontol
+J Cross Cult Psychol
+J Crustacean Biol
+J Cryst Growth
+J Cult Divers
+J Curr Adolesc Med
+J Curr Soc Issues
+J Cutan Laser Ther
+J Cutan Med Surg
+J Cutan Pathol
+J Cyclic Nucleotide Protein Phosphor Res
+J Cyclic Nucleotide Res
+J Cyst Fibros
+J Dairy Res
+J Dairy Sci
+J Deaf Stud Deaf Educ
+J Dent
+J Dent Assoc S Afr
+J Dent Assoc Thai
+J Dent Aux
+J Dent Belge
+J Dent Child
+J Dent Child (Chic)
+J Dent Educ
+J Dent Guid Counc Handicap
+J Dent Handicap
+J Dent Hyg
+J Dent Med
+J Dent Pract Adm
+J Dent Que
+J Dent Res
+J Dent Sch Natl Univ Iran
+J Dent Symp
+J Dent Technol
+J Dermatol
+J Dermatol Sci
+J Dermatol Surg
+J Dermatol Surg Oncol
+J Dermatolog Treat
+J Dev Areas
+J Dev Behav Pediatr
+J Dev Comm
+J Dev Econ
+J Dev Physiol
+J Dev Soc
+J Dev Stud
+J Diabet Complications
+J Diabetes Complications
+J Dial
+J Diarrhoeal Dis Res
+J Digit Imaging
+J Dist Columbia Dent Soc
+J Divorce
+J Divorce & Remarriage
+J Doc
+J Drug Educ
+J Drug Issues
+J Drug Res
+J Drug Target
+J Drugs Dermatol
+J Dtsch Dermatol Ges
+J Dyn Syst Meas Control
+J ECT
+J ET Nurs
+J Early Adolesc
+J Early Repub
+J East Afr Res Dev
+J East Asian Aff
+J East West Stud
+J Ecclesiast Hist
+J Ecol
+J Econ Behav Organ
+J Econ Bus
+J Econ Dyn Control
+J Econ Educ
+J Econ Entomol
+J Econ Hist
+J Econ Issues
+J Econ Lit
+J Econ Perspect
+J Econ Psychol
+J Econ Soc Hist Orient
+J Econ Soc Meas
+J Econ Stud
+J Econ Surv
+J Econ Theory
+J Econom
+J Ecumenical Stud
+J Educ Adm Hist
+J Educ Psychol
+J Educ Res
+J Educ Soc Work
+J Egypt Archaeol
+J Egypt Med Assoc
+J Egypt Natl Canc Inst
+J Egypt Public Health Assoc
+J Egypt Soc Obstet Gynecol
+J Egypt Soc Parasitol
+J Elast
+J Elder Abuse Negl
+J Electr Electron Eng Aust
+J Electrocardiol
+J Electromyogr Kinesiol
+J Electron Microsc (Tokyo)
+J Electron Microsc Tech
+J Embryol Exp Morphol
+J Emerg Med
+J Emerg Nurs
+J Endocrinol
+J Endocrinol Invest
+J Endod
+J Endotoxin Res
+J Endourol
+J Endovasc Surg
+J Endovasc Ther
+J Eng Psychol
+J Enterostomal Ther
+J Entwickl Polit
+J Environ Biol
+J Environ Econ Manage
+J Environ Educ
+J Environ Health
+J Environ Manage
+J Environ Monit
+J Environ Pathol Toxicol
+J Environ Pathol Toxicol Oncol
+J Environ Psychol
+J Environ Qual
+J Environ Radioact
+J Environ Sci (China)
+J Environ Sci Eng
+J Environ Sci Health A Environ Sci Eng Toxic Hazard Subst Control
+J Environ Sci Health A Tox Hazard Subst Environ Eng
+J Environ Sci Health B
+J Environ Sci Health C
+J Environ Sci Health C Environ Carcinog Ecotoxicol Rev
+J Enzyme Inhib
+J Enzyme Inhib Med Chem
+J Epidemiol
+J Epidemiol Biostat
+J Epidemiol Community Health
+J Epilepsy
+J Erie Stud
+J Esthet Dent
+J Esthet Restor Dent
+J Ethics
+J Ethics Law Aging
+J Ethiop Stud
+J Ethn Migr Stud
+J Ethn Stud
+J Ethn Subst Abuse
+J Ethnobiol Ethnomedicine
+J Ethnopharmacol
+J Eukaryot Microbiol
+J Eur Acad Dermatol Venereol
+J Eur Econ Hist
+J Eur Soc Policy
+J Eur Stud
+J Eval Clin Pract
+J Evang Theol Soc
+J Evol Biochem Physiol
+J Evol Biol
+J Existent
+J Exp Anal Behav
+J Exp Anim Sci
+J Exp Biol
+J Exp Bot
+J Exp Child Psychol
+J Exp Clin Assist Reprod
+J Exp Clin Cancer Res
+J Exp Med
+J Exp Med Sci
+J Exp Pathol
+J Exp Pathol (Oxford)
+J Exp Psychol
+J Exp Psychol Anim Behav Process
+J Exp Psychol Appl
+J Exp Psychol Gen
+J Exp Psychol Hum Percept Perform
+J Exp Psychol Learn Mem Cogn
+J Exp Psychol [Hum Learn]
+J Exp Soc Psychol
+J Exp Ther Oncol
+J Exp Zool
+J Exp Zool Suppl
+J Exp Zoolog A Comp Exp Biol
+J Exp Zoolog B Mol Dev Evol
+J Expo Anal Environ Epidemiol
+J Expo Sci Environ Epidemiol
+J Extra Corpor Technol
+J Fac Med Baghdad
+J Fam Cult
+J Fam Econ Issues
+J Fam Health Care
+J Fam Hist
+J Fam Issues
+J Fam Law
+J Fam Nurs
+J Fam Plann Reprod Health Care
+J Fam Pract
+J Fam Psychol
+J Fam Psychother
+J Fam Violence
+J Fam Welf
+J Famil Health Train
+J Feline Med Surg
+J Fem Fam Ther
+J Fish Biol
+J Fish Dis
+J Fla Med Assoc
+J Fla State Dent Soc
+J Fluency Disord
+J Fluid Mech
+J Fluor Chem
+J Fluoresc
+J Foetal Med
+J Food Compost Anal
+J Food Process Eng
+J Food Prot
+J Food Qual
+J Food Sci
+J Foot Ankle Surg
+J Foot Surg
+J Forecast
+J Forensic Med
+J Forensic Odontostomatol
+J Forensic Sci
+J Forensic Sci Soc
+J Formos Med Assoc
+J Fr Med Chir Thorac
+J Fr Ophtalmol
+J Fr Otorhinolaryngol Audiophonol Chir Maxillofac
+J Fr Otorhinolaryngol Chir Maxillofac
+J Free Radic Biol Med
+J Friends Hist Soc
+J Ga Dent Assoc
+J Gambl Stud
+J Gard Hist
+J Gastroenterol
+J Gastroenterol Hepatol
+J Gastrointest Surg
+J Gastrointestin Liver Dis
+J Gay Lesbian Med Assoc
+J Gen Appl Microbiol
+J Gen Educ
+J Gen Intern Med
+J Gen Microbiol
+J Gen Orthod
+J Gen Physiol
+J Gen Psychol
+J Gen Virol
+J Gend Specif Med
+J Gene Med
+J Genet
+J Genet Couns
+J Genet Hum
+J Genet Psychol
+J Geog
+J Geol
+J Geol Soc London
+J Geophys Res
+J Geriatr Phys Ther
+J Geriatr Psychiatry
+J Geriatr Psychiatry Neurol
+J Germantown Hosp
+J Gerontol
+J Gerontol A Biol Sci Med Sci
+J Gerontol B Psychol Sci Soc Sci
+J Gerontol Nurs
+J Gerontol Soc Work
+J Gesch
+J Glass Stud
+J Glaucoma
+J Gnathol
+J Gov Inf
+J Gravit Physiol
+J Great Lakes Res
+J Group Psychother Psychodrama Sociom
+J Gt Houst Dent Soc
+J Guid Control Dyn
+J Gynaecol Endocrinol
+J Gynecol Obstet Biol Reprod (Paris)
+J Gynecol Surg
+J HIV Ther
+J Halacha Contemporary Society
+J Hand Surg [Am]
+J Hand Surg [Br]
+J Hand Ther
+J Hawaii Dent Assoc
+J Hawaii State Dent Assoc
+J Hazard Mater
+J Head Trauma Rehabil
+J Headache Pain
+J Health Adm Educ
+J Health Care Benefits
+J Health Care Chaplain
+J Health Care Finance
+J Health Care Inter Des
+J Health Care Law Policy
+J Health Care Mark
+J Health Care Poor Underserved
+J Health Care Technol
+J Health Commun
+J Health Econ
+J Health Educ
+J Health Hosp Law
+J Health Hum Behav
+J Health Hum Resour Adm
+J Health Hum Serv Adm
+J Health Law
+J Health Manag
+J Health Organ Manag
+J Health Polit Policy Law
+J Health Popul Dev Ctries
+J Health Popul Nutr
+J Health Psychol
+J Health Serv Res Policy
+J Health Soc Behav
+J Health Soc Policy
+J Healthc Des
+J Healthc Educ Train
+J Healthc Inf Manag
+J Healthc Manag
+J Healthc Mater Manage
+J Healthc Prot Manage
+J Healthc Qual
+J Healthc Resour Manag
+J Healthc Risk Manag
+J Heart Lung Transplant
+J Heart Transplant
+J Heart Valve Dis
+J Hell Stud
+J Helminthol
+J Hematother
+J Hematother Stem Cell Res
+J Hepatobiliary Pancreat Surg
+J Hepatol
+J Hepatol Suppl
+J Herb Pharmacother
+J Hered
+J Heterocycl Chem
+J High Resolut Chromatogr
+J Higher Educ
+J Hillside Hosp
+J Hirnforsch
+J Hist Arabic Sci
+J Hist Behav Sci
+J Hist Biol
+J Hist Dent
+J Hist Geogr
+J Hist Ideas
+J Hist Med
+J Hist Med Allied Sci
+J Hist Neurosci
+J Hist Philos
+J Hist Sex
+J Hist Sociol
+J Histochem Cytochem
+J Histotechnol
+J Holist Nurs
+J Home Econ
+J Homosex
+J Hong Kong Branch R Asiat Soc
+J Hortic Sci Biotechnol
+J Hosp Admit Manage
+J Hosp Dent Pract
+J Hosp Infect
+J Hosp Mark
+J Hosp Mark Public Relations
+J Hosp Supply Process Distrib
+J Houston Dist Dent Soc
+J Huazhong Univ Sci Technolog Med Sci
+J Hum Behav Soc Environ
+J Hum Ecol
+J Hum Ergol (Tokyo)
+J Hum Evol
+J Hum Genet
+J Hum Hypertens
+J Hum Lact
+J Hum Nutr
+J Hum Nutr Diet
+J Hum Resour
+J Hum Virol
+J Human Stress
+J Humanist Educ Dev
+J Hyg (Lond)
+J Hyg Epidemiol Microbiol Immunol
+J Hypertens
+J Hypertens Suppl
+J Idaho Acad Sci
+J Image Guid Surg
+J Immigr Health
+J Immigr Minor Health
+J Immune Based Ther Vaccines
+J Immunoassay
+J Immunoassay Immunochem
+J Immunogenet
+J Immunol
+J Immunol Methods
+J Immunopharmacol
+J Immunother
+J Immunother Emphasis Tumor Immunol
+J Imp Commonw Hist
+J In Vitro Fert Embryo Transf
+J Ind Econ
+J Ind Hyg Toxicol
+J Ind Microbiol
+J Ind Microbiol Biotechnol
+J Indian Acad Dent
+J Indian Anthropol Soc
+J Indian Assoc Commun Dis
+J Indian Dent Assoc
+J Indian Forensic Sci
+J Indian Law Inst
+J Indian Med Assoc
+J Indian Med Prof
+J Indian Orthod Soc
+J Indian Soc Pedod Prev Dent
+J Indiana Dent Assoc
+J Indiana State Dent Assoc
+J Indiana State Med Assoc
+J Indianap Dist Dent Soc
+J Individ Psychol
+J Inf Image Manage
+J Infect
+J Infect Chemother
+J Infect Dis
+J Inflamm
+J Inflamm (Lond)
+J Infor Ethics
+J Infus Chemother
+J Infus Nurs
+J Inherit Metab Dis
+J Inorg Biochem
+J Insect Physiol
+J Insect Sci
+J Inst Actuar
+J Inst Chart Account Sri Lanka
+J Inst Econ Res
+J Inst Hosp Eng
+J Inst Med
+J Inst Sterile Serv Manage
+J Inst Theor Econ
+J Insur Med
+J Int Acad Periodontol
+J Int Aff
+J Int Assoc Dent Child
+J Int Assoc Physicians AIDS Care
+J Int Assoc Physicians AIDS Care (Chic Ill)
+J Int Bioethique
+J Int Chir
+J Int Coll Dent (Jpn)
+J Int Coll Surg
+J Int Dev
+J Int Econ
+J Int Fed Clin Chem
+J Int Fed Gynaecol Obstet
+J Int Law Econ
+J Int Med Res
+J Int Neuropsychol Soc
+J Int Trade Econ Dev
+J Int Wine Food Soc
+J Integr Neurosci
+J Intellect Disabil
+J Intellect Disabil Res
+J Intensive Care Med
+J Inter Am Stud World Aff
+J Intercult Stud
+J Interdiscip Hist
+J Interdiscipl Cycle Res
+J Interferon Cytokine Res
+J Interferon Res
+J Intern Med
+J Intern Med Suppl
+J Interpers Violence
+J Interprof Care
+J Interv Card Electrophysiol
+J Interv Cardiol
+J Intraven Nurs
+J Invasive Cardiol
+J Invertebr Pathol
+J Invest Dermatol
+J Invest Surg
+J Investig Allergol Clin Immunol
+J Investig Dermatol Symp Proc
+J Investig Med
+J Iowa Med Soc
+J Iowa State Med Soc
+J Ir Coll Physicians Surg
+J Ir Dent Assoc
+J Ir Med Assoc
+J Jew Communal Serv
+J Jew Med Ethics Halacha
+J Jj Group Hosp Grant Med Coll
+J Jpn Int Econ
+J Jpn Obstet Gynecol Soc
+J Kans Bar Assoc
+J Kans Dent Assoc
+J Kans Entomol Soc
+J Kans Med Soc
+J Kans State Dent Assoc
+J Knee Surg
+J Korean Astron Soc
+J Korean Med Sci
+J Korean Res Soc Dent Hypn
+J Kuwait Med Assoc
+J Ky Dent Assoc
+J Ky Med Assoc
+J Ky State Med Assoc
+J La Dent Assoc
+J La State Med Soc
+J Lab Clin Med
+J Labelled Comp Radiopharm
+J Labelled Compd
+J Labor Econ
+J Labor Res
+J Lanc Cty Hist Soc
+J Lancet
+J Laparoendosc Adv Surg Tech A
+J Laparoendosc Surg
+J Laryngol Otol
+J Laryngol Otol Suppl
+J Laser Appl
+J Lat Am Lore
+J Lat Am Stud
+J Law Econ
+J Law Ethics Dent
+J Law Health
+J Law Med
+J Law Med Ethics
+J Law Policy
+J Law Polit
+J Law Relig
+J Law Soc
+J Law Technol
+J Learn Disabil
+J Leg Med
+J Leg Med (N Y)
+J Legal Educ
+J Legal Hist
+J Legal Stud
+J Legis
+J Leukoc Biol
+J Leukoc Biol Suppl
+J Libert Stud
+J Libr
+J Libr Hist
+J Lipid Mediat
+J Lipid Mediat Cell Signal
+J Lipid Res
+J Liposome Res
+J Liq Chromatogr
+J Lithotr Stone Dis
+J Long Isl Hist
+J Long Term Care Adm
+J Long Term Eff Med Implants
+J Long Term Home Health Care
+J Low Genit Tract Dis
+J Macomb Dent Soc
+J Macroecon
+J Magn Reson
+J Magn Reson B
+J Magn Reson Imaging
+J Maine Dent Assoc
+J Maine Med Assoc
+J Mal Vasc
+J Malays Branch R Asiat Soc
+J Mamm Evol
+J Mammal
+J Mammary Gland Biol Neoplasia
+J Manag Care Pharm
+J Manag Med
+J Manipulative Physiol Ther
+J Marital Fam Ther
+J Mark
+J Mark Ment Health
+J Mark Prof
+J Mark Res
+J Mark Res Soc
+J Marmara Univ Dent Fac
+J Marriage Fam
+J Marriage Fam Couns
+J Mass Dent Soc
+J Mass Spectrom
+J Mater Res
+J Mater Sci Lett
+J Mater Sci Mater Med
+J Matern Fetal Investig
+J Matern Fetal Med
+J Matern Fetal Neonatal Med
+J Math Anal Appl
+J Math Biol
+J Math Chem
+J Math Psychol
+J Math Sociol
+J Maxillofac Orthop
+J Maxillofac Surg
+J Md State Dent Assoc
+J Mechanochem Cell Motil
+J Med
+J Med (Oporto)
+J Med Assoc Eire
+J Med Assoc Ga
+J Med Assoc Prev War
+J Med Assoc State Ala
+J Med Assoc Thai
+J Med Besancon
+J Med Biogr
+J Med Bord
+J Med Caen
+J Med Chem
+J Med Chir Prat
+J Med Dent Sci
+J Med Educ
+J Med Eng Technol
+J Med Entomol
+J Med Entomol Suppl
+J Med Ethics
+J Med Food
+J Med Genet
+J Med Humanit
+J Med Humanit Bioeth
+J Med Internet Res
+J Med Invest
+J Med Lab Technol
+J Med Leg Droit Med
+J Med Liban
+J Med Libr Assoc
+J Med Lyon
+J Med Microbiol
+J Med Nantes
+J Med Paris
+J Med Pernamb
+J Med Pharm Chem
+J Med Philos
+J Med Pract Manage
+J Med Primatol
+J Med Sci
+J Med Screen
+J Med Soc N J
+J Med Strasb
+J Med Syst
+J Med Technol
+J Med Vet Mycol
+J Med Virol
+J Medicaid Manage
+J Mediev Hist
+J Mediev Renaiss Stud
+J Mem Lang
+J Memb Sci
+J Membr Biol
+J Ment Defic Res
+J Ment Health Adm
+J Ment Health Policy Econ
+J Ment Sci
+J Ment Subnorm
+J Mercer Dent Soc
+J Mex Am Hist
+J Mich Dent Assoc
+J Mich State Dent Assoc
+J Mich State Med Soc
+J Microbiol
+J Microbiol Biotechnol
+J Microbiol Epidemiol Immunobiol
+J Microbiol Immunol Infect
+J Microbiol Methods
+J Microcolumn Sep
+J Microelectromech Syst
+J Microencapsul
+J Microgr
+J Microsc
+J Microsc (Paris)
+J Microsc Biol Cell
+J Microsurg
+J Microw Power
+J Microw Power Electromagn Energy
+J Midwifery Womens Health
+J Mil Hist
+J Minim Invasive Gynecol
+J Minn Acad Sci
+J Miss Dent Assoc
+J Miss Hist
+J Miss State Med Assoc
+J Mo Bar
+J Mo Dent Assoc
+J Mod Afr Stud
+J Mod Hist
+J Mol Appl Genet
+J Mol Biol
+J Mol Cell Cardiol
+J Mol Cell Immunol
+J Mol Diagn
+J Mol Endocrinol
+J Mol Evol
+J Mol Graph
+J Mol Graph Model
+J Mol Histol
+J Mol Med
+J Mol Microbiol Biotechnol
+J Mol Model (Online)
+J Mol Neurosci
+J Mol Recognit
+J Mol Spectrosc
+J Mol Struct
+J Molluscan Stud
+J Moral Educ
+J Mormon Hist
+J Morphol
+J Mot Behav
+J Mt Sinai Hosp N Y
+J Multicult Couns Devel
+J Multicult Soc Work
+J Muscle Res Cell Motil
+J Musculoskelet Neuronal Interact
+J Music Ther
+J N C Dent Soc
+J N H Dent Soc
+J N J Dent Assoc
+J N J Dent Hyg Assoc
+J N J State Dent Soc
+J N Y State Nurses Assoc
+J N Y State Sch Nurse Teach Assoc
+J N Z Soc Periodontol
+J NIH Res
+J Nal Assoc
+J Nanobiotechnology
+J Nanosci Nanotechnol
+J Nat Hist Mus Inst Chiba
+J Nat Prod
+J Nat Toxins
+J Natl Analg Soc
+J Natl Assoc Chiropodists
+J Natl Assoc Hosp Dev
+J Natl Assoc Priv Psychiatr Hosp
+J Natl Assoc Seventh Day Advent Dent
+J Natl Black Nurses Assoc
+J Natl Cancer Inst
+J Natl Cancer Inst Monogr
+J Natl Compr Canc Netw
+J Natl Malar Soc
+J Natl Med Assoc
+J Near East Stud
+J Nebr Dent Assoc
+J Negat Results Biomed
+J Negro Educ
+J Negro Hist
+J Nematol
+J Nephrol
+J Nephrol Nurs
+J Nerv Ment Dis
+J Neural Eng
+J Neural Transm
+J Neural Transm Gen Sect
+J Neural Transm Park Dis Dement Sect
+J Neural Transm Suppl
+J Neural Transplant
+J Neural Transplant Plast
+J Neurobiol
+J Neurochem
+J Neurocytol
+J Neuroendocrinol
+J Neuroengineering Rehabil
+J Neurogenet
+J Neuroimaging
+J Neuroimmunol
+J Neuroimmunol Suppl
+J Neuroinflammation
+J Neurol
+J Neurol Neurosurg Psychiatry
+J Neurol Phys Ther
+J Neurol Rehabil
+J Neurol Sci
+J Neurolinguistics
+J Neurooncol
+J Neuroophthalmol
+J Neuropathol Exp Neurol
+J Neurophysiol
+J Neuropsychiatr
+J Neuropsychiatry Clin Neurosci
+J Neuroradiol
+J Neurosci
+J Neurosci Methods
+J Neurosci Nurs
+J Neurosci Res
+J Neurosurg
+J Neurosurg Anesthesiol
+J Neurosurg Nurs
+J Neurosurg Sci
+J Neurosurg Spine
+J Neurotrauma
+J Neurovirol
+J Neurovisc Relat
+J New Drugs
+J Newark Beth Isr Hosp
+J Niger Assoc Dent Stud
+J Nigeria Med Assoc
+J Nihon Univ Sch Dent
+J Nippon Med Sch
+J North La Hist Assoc
+J Nucl Biol Med
+J Nucl Cardiol
+J Nucl Med
+J Nucl Med Allied Sci
+J Nucl Med Technol
+J Nurs Adm
+J Nurs Care
+J Nurs Care Qual
+J Nurs Educ
+J Nurs Ethics
+J Nurs Hist
+J Nurs Law
+J Nurs Manag
+J Nurs Meas
+J Nurs Qual Assur
+J Nurs Res
+J Nurs Scholarsh
+J Nurs Staff Dev
+J Nurse Midwifery
+J Nurses Staff Dev
+J Nutr
+J Nutr Biochem
+J Nutr Diet
+J Nutr Educ
+J Nutr Educ Behav
+J Nutr Elder
+J Nutr Health Aging
+J Nutr Sci Vitaminol (Tokyo)
+J Obstet Gynaecol
+J Obstet Gynaecol Br Commonw
+J Obstet Gynaecol Br Emp
+J Obstet Gynaecol Can
+J Obstet Gynaecol East Cent Africa
+J Obstet Gynaecol India
+J Obstet Gynaecol Res
+J Obstet Gynecol Neonatal Nurs
+J Occup Environ Hyg
+J Occup Environ Med
+J Occup Health
+J Occup Health Psychol
+J Occup Med
+J Occup Rehabil
+J Ocul Pharmacol
+J Ocul Pharmacol Ther
+J Odontol Conserv
+J Off Repub Fr Ed Lois Decrets
+J Off Stat
+J Okla Dent Assoc
+J Okla State Dent Assoc
+J Okla State Med Assoc
+J Oncol Manag
+J Oncol Pharm Pract
+J Ont Dent Assoc
+J Oper Res Soc
+J Oper Room Res Inst
+J Ophthalmic Nurs Technol
+J Ophthalmic Photogr
+J Opt Soc Am
+J Opt Soc Am A
+J Opt Soc Am A Opt Image Sci Vis
+J Opt Soc Am B
+J Oral Implant Transplant Surg
+J Oral Implantol
+J Oral Maxillofac Surg
+J Oral Med
+J Oral Pathol
+J Oral Pathol Med
+J Oral Rehabil
+J Oral Sci
+J Oral Surg
+J Oral Surg Anesth Hosp Dent Serv
+J Oral Ther Pharmacol
+J Oreg Dent Assoc
+J Org Chem
+J Organomet Chem
+J Orofac Orthop
+J Orofac Pain
+J Orthod
+J Orthop Res
+J Orthop Sci
+J Orthop Sports Phys Ther
+J Orthop Surg (Hong Kong)
+J Orthop Trauma
+J Osaka Dent Univ
+J Osaka Univ Dent Sch
+J Oslo City Hosp
+J Osteopath (Kirksvill)
+J Otolaryngol
+J Otolaryngol Soc Aust
+J Otolaryngol Suppl
+J Otto Rank Assoc
+J Outcome Meas
+J Pa Acad Sci
+J Pac Hist
+J Paediatr Child Health
+J Paediatr Dent
+J Pain
+J Pain Palliat Care Pharmacother
+J Pain Symptom Manage
+J Pak Med Assoc
+J Paleolimnol
+J Paleontol
+J Palest Stud
+J Palliat Care
+J Palliat Med
+J Parapsychol
+J Parasitol
+J Parenter Drug Assoc
+J Parenter Sci Technol
+J Parodontol
+J Pastoral Care
+J Pastoral Care Counsel
+J Pat Off Soc
+J Pat Trademark Off Soc
+J Pathol
+J Pathol Bacteriol
+J Patient Acc Manage
+J Peace Res
+J Peasant Stud
+J Pediatr
+J Pediatr (Rio J)
+J Pediatr Adolesc Gynecol
+J Pediatr Endocrinol
+J Pediatr Endocrinol Metab
+J Pediatr Gastroenterol Nutr
+J Pediatr Health Care
+J Pediatr Hematol Oncol
+J Pediatr Nurs
+J Pediatr Oncol Nurs
+J Pediatr Ophthalmol
+J Pediatr Ophthalmol Strabismus
+J Pediatr Orthop
+J Pediatr Orthop B
+J Pediatr Perinat Nutr
+J Pediatr Psychol
+J Pediatr Surg
+J Pedod
+J Pept Res
+J Pept Sci
+J Perianesth Nurs
+J Perinat Med
+J Perinat Neonatal Nurs
+J Perinatol
+J Periodontal Res
+J Periodontal Res Suppl
+J Periodontol
+J Perioper Pract
+J Peripher Nerv Syst
+J Pers
+J Pers Assess
+J Pers Soc Psychol
+J Personal Disord
+J Pharm Belg
+J Pharm Biomed Anal
+J Pharm Law
+J Pharm Mark Manage
+J Pharm Pharm Sci
+J Pharm Pharmacol
+J Pharm Sci
+J Pharm Sci Technol
+J Pharm Technol
+J Pharmacobiodyn
+J Pharmacokinet Biopharm
+J Pharmacokinet Pharmacodyn
+J Pharmacol
+J Pharmacol Exp Ther
+J Pharmacol Methods
+J Pharmacol Sci
+J Pharmacol Toxicol Methods
+J Pharmacother
+J Phila Cty Dent Soc
+J Philipp Dent Assoc
+J Philipp Dev
+J Philipp Fed Priv Med Pract
+J Philipp Med Assoc
+J Philipp Stat
+J Philos
+J Photochem Photobiol B
+J Phycol
+J Phys B At Mol Opt Phys
+J Phys Chem
+J Phys Chem A Mol Spectrosc Kinet Environ Gen Theory
+J Phys Chem B Condens Matter Mater Surf Interfaces Biophys
+J Phys Chem B Mater Surf Interfaces Biophys
+J Phys Colloid Chem
+J Phys Condens Matter
+J Phys D Appl Phys
+J Phys G Nucl Part Phys
+J Phys [E]
+J Physical Soc Japan
+J Physicians Assoc AIDS Care
+J Physiol
+J Physiol (Paris)
+J Physiol Anthropol
+J Physiol Anthropol Appl Human Sci
+J Physiol Biochem
+J Physiol Paris
+J Physiol Pharmacol
+J Physiol Sci
+J Physiol Suppl (Paris)
+J Pierre Fauchard Acad
+J Pineal Res
+J Plant Biol
+J Plant Growth Regul
+J Plant Nutr
+J Plant Physiol
+J Plant Res
+J Plast Reconstr Aesthet Surg
+J Plast Reconstr Surg Nurs
+J Policy Anal Manage
+J Policy Hist
+J Polit
+J Polit Econ
+J Polit Philos
+J Polit Sci
+J Polym Sci A
+J Polym Sci [A1]
+J Polym Sci [B]
+J Polyn Soc
+J Pop Cult
+J Popul
+J Popul Assoc Korea
+J Popul Econ
+J Popul Res
+J Post Anesth Nurs
+J Post Keynes Econ
+J Postgrad Med
+J Pract Nurs
+J Prat Rev Gen Clin Ther
+J Presbyt Hist
+J Prev Dent
+J Prev Interv Community
+J Prev Med Pub Health
+J Prev Psychiatry
+J Prev Soc Med
+J Prim Prev
+J Prison Health
+J Prison Jail Health
+J Prod Liability
+J Prof Nurs
+J Prof Serv Mark
+J Proj Tech
+J Proj Tech Pers Assess
+J Prosthet Dent
+J Prosthodont
+J Protein Chem
+J Proteome Res
+J Protozool
+J Psicol
+J Psychedelic Drugs
+J Psychiatr Educ
+J Psychiatr Ment Health Nurs
+J Psychiatr Nurs
+J Psychiatr Nurs Ment Health Serv
+J Psychiatr Pract
+J Psychiatr Res
+J Psychiatr Soc Work
+J Psychiatr Treat Eval
+J Psychiatry Law
+J Psychiatry Neurosci
+J Psychoactive Drugs
+J Psychohist
+J Psychol
+J Psychol Human Sex
+J Psychol Judaism
+J Psychol Norm Pathol (Paris)
+J Psychol Theol
+J Psycholinguist Res
+J Psychopathol Behav Assess
+J Psychopharmacol
+J Psychosoc Nurs Ment Health Serv
+J Psychosoc Oncol
+J Psychosom Obstet Gynaecol
+J Psychosom Res
+J Psychother Pract Res
+J Public Econ
+J Public Health (Bangkok)
+J Public Health (Oxf)
+J Public Health Dent
+J Public Health Manag Pract
+J Public Health Med
+J Public Health Policy
+J Public Policy
+J Qual Assur
+J Qual Clin Pract
+J Quant Econ
+J Quant Spectrosc Radiat Transf
+J R Anthropol Inst
+J R Army Med Corps
+J R Asiat Soc GB Irel
+J R Astron Soc Can
+J R Aust Hist Soc
+J R Coll Gen Pract
+J R Coll Gen Pract Occas Pap
+J R Coll Physicians Edinb
+J R Coll Physicians Lond
+J R Coll Surg Edinb
+J R I State Dent Soc
+J R Inst Public Health
+J R Microsc Soc
+J R Nav Med Serv
+J R Sanit Inst
+J R Soc Antiq Irel
+J R Soc Arts
+J R Soc Health
+J R Soc Med
+J R Soc West Aust
+J R Stat Soc Ser A Stat Soc
+J R Stat Soc Ser C Appl Stat
+J R Stat Soc [Ser A]
+J Radiat Res (Tokyo)
+J Radiol
+J Radiol Electrol Arch Electr Medicale
+J Radiol Electrol Med Nucl
+J Radiol Prot
+J Recept Res
+J Recept Signal Transduct Res
+J Reconstr Microsurg
+J Reform Jud
+J Refract Corneal Surg
+J Refract Surg
+J Refug Stud
+J Reg Policy
+J Reg Sci
+J Rehabil
+J Rehabil Med
+J Rehabil R D
+J Rehabil Res Dev
+J Rehabil Res Dev Clin Suppl
+J Relig
+J Relig Afr
+J Relig Aging
+J Relig Ethics
+J Relig Gerontol
+J Relig Health
+J Relig Hist
+J Ren Nutr
+J Renin Angiotensin Aldosterone Syst
+J Reprod Dev
+J Reprod Fertil
+J Reprod Fertil Abstr Ser
+J Reprod Fertil Suppl
+J Reprod Immunol
+J Reprod Infant Psychol
+J Reprod Med
+J Res Adolesc
+J Res Educ Indian Med
+J Res Indian Med
+J Res Natl Inst Stand Technol
+J Res Pers
+J Res Read
+J Respir Dis
+J Reticuloendothel Soc
+J Rheumatol
+J Rheumatol Suppl
+J Risk Insur
+J Risk Uncertain
+J Robot Syst
+J Rocky Mt Analg Soc
+J Rocky Mt Mediev Renaiss Assoc
+J Rural Dev
+J Rural Health
+J Rural Stud
+J Rutgers Univ Libr
+J S Afr Logop Soc
+J S Afr Speech Hear Assoc
+J S Afr Vet Assoc
+J S Afr Vet Med Assoc
+J S C Med Assoc
+J SOGC
+J Safety Res
+J San Antonio Dent Soc
+J San Diego Hist
+J Sch Health
+J Sch Nurs
+J Sch Psychol
+J Sci Food Agric
+J Sci Ind Res (C)
+J Sci Ind Res (India)
+J Sci Instrum
+J Sci Med Lille
+J Sci Med Sport
+J Sci Soc Thailand
+J Sci Study Relig
+J Scott Labour Hist Soc
+J Seattle Dist Dent Soc
+J Seattle King Cty Dent Soc
+J Sediment Petrol
+J Sediment Res A Sediment Petrol Process
+J Semit Stud
+J Sens Stud
+J Sep Sci
+J Sex Educ
+J Sex Educ Ther
+J Sex Marital Ther
+J Sex Med
+J Sex Res
+J Shoulder Elbow Surg
+J Siam Soc
+J Singapore Paediatr Soc
+J Sleep Res
+J Small Anim Pract
+J Smooth Muscle Res
+J Soc Adm Pharm
+J Soc Am
+J Soc Arch
+J Soc Army Hist Res
+J Soc Behav Pers
+J Soc Biol
+J Soc Biol Struct
+J Soc Christ Ethics
+J Soc Cienc Med Lisb
+J Soc Clin Psychol
+J Soc Cosmet Chem
+J Soc Dev Afr
+J Soc Econ Stud
+J Soc Gynecol Investig
+J Soc Health Syst
+J Soc Hist
+J Soc Hyg
+J Soc Issues
+J Soc Occup Med
+J Soc Ocean
+J Soc Pediatr Nurs
+J Soc Pers Relat
+J Soc Philos
+J Soc Policy
+J Soc Polit Aff
+J Soc Polit Econ Stud
+J Soc Polit Stud
+J Soc Psychol
+J Soc Res Adm
+J Soc Serv Res
+J Soc Stat Paris
+J Soc Stud
+J Soc Welfare Law
+J Soc Work Hum Sex
+J Sociol (Melb)
+J Sociol Soc Welf
+J Sociol Stud
+J South Afr Stud
+J South Asian Middle East Stud
+J South Calif Dent Assistants Assoc
+J South Calif Dent Assoc
+J South Calif State Dent Hyg Assoc
+J South Hist
+J South Orthop Assoc
+J Southeast Asian Earth Sci
+J Southeast Asian Stud
+J Sov Natly
+J Soz Forsch
+J Spacecr Rockets
+J Spec Educ
+J Spec Pediatr Nurs
+J Speech Disord
+J Speech Hear Disord
+J Speech Hear Disord Monogr Suppl
+J Speech Hear Res
+J Speech Lang Hear Res
+J Spinal Cord Med
+J Spinal Disord
+J Spinal Disord Tech
+J Sport Hist
+J Sports Med
+J Sports Med Phys Fitness
+J Sports Sci
+J Sri Lanka Branch R Asiat Soc
+J State Gov
+J Sterile Serv Manage
+J Steroid Biochem
+J Steroid Biochem Mol Biol
+J Stomatol Belg
+J Stone Dis
+J Stored Prod Res
+J Strength Cond Res
+J Stroke Cerebrovasc Dis
+J Struct Biol
+J Struct Funct Genomics
+J Stud Alcohol
+J Stud Alcohol Suppl
+J Submicrosc Cytol
+J Submicrosc Cytol Pathol
+J Subst Abuse
+J Subst Abuse Treat
+J Support Oncol
+J Supramol Struct
+J Supramol Struct Cell Biochem
+J Supramol Struct Cell Biochem Suppl
+J Supramol Struct Suppl
+J Surg Oncol
+J Surg Oncol Suppl
+J Surg Orthop Adv
+J Surg Res
+J Sykepleien
+J Synchrotron Radiat
+J Technol Transf
+J Telemed Telecare
+J Tenn Dent Assoc
+J Tenn Med Assoc
+J Tenn State Dent Assoc
+J Texas Dent Hyg Assoc
+J Texture Stud
+J Thai Assoc Volunt Steriliz
+J Thanatol
+J Theol South Afr
+J Theor Biol
+J Theory Soc Behav
+J Therm Biol
+J Thorac Cardiovasc Surg
+J Thorac Imaging
+J Thorac Surg
+J Thought
+J Thromb Haemost
+J Thromb Thrombolysis
+J Tissue Cult Methods
+J Tissue Viability
+J Tn State Med Assoc
+J Tongji Med Univ
+J Toxicol Clin Exp
+J Toxicol Clin Toxicol
+J Toxicol Cutaneous Ocul Toxicol
+J Toxicol Environ Health
+J Toxicol Environ Health A
+J Toxicol Environ Health B Crit Rev
+J Toxicol Environ Health Suppl
+J Toxicol Sci
+J Toxicol Toxin Rev
+J Trace Elem Electrolytes Health Dis
+J Trace Elem Med Biol
+J Tradit Chin Med
+J Transcult Nurs
+J Transl Med
+J Transnatl Law Policy
+J Transpl Coord
+J Trauma
+J Trauma Dissociation
+J Trauma Nurs
+J Trauma Stress
+J Travel Med
+J Trop Geogr
+J Trop Med
+J Trop Med Hyg
+J Trop Pediatr
+J Trop Pediatr Afr Child Health
+J Trop Pediatr Environ Child Health
+J UOEH
+J Ultrasound Med
+J Ultrastruct Mol Struct Res
+J Ultrastruct Res
+J Urban Econ
+J Urban Health
+J Urban Hist
+J Urban Law
+J Urol
+J Urol (Paris)
+J Urol Medicale Chir
+J Urol Nephrol (Paris)
+J Urol Nurs
+J Vac Sci Technol A
+J Value Inq
+J Vasc Access
+J Vasc Interv Radiol
+J Vasc Nurs
+J Vasc Res
+J Vasc Surg
+J Vector Borne Dis
+J Vector Ecol
+J Vener Dis Inf
+J Vestib Res
+J Vet Dent
+J Vet Diagn Invest
+J Vet Intern Med
+J Vet Med A Physiol Pathol Clin Med
+J Vet Med B Infect Dis Vet Public Health
+J Vet Med Educ
+J Vet Med Sci
+J Vet Pharmacol Ther
+J Vet Sci
+J Viral Hepat
+J Virol
+J Virol Methods
+J Vis
+J Vis Commun Med
+J Vis Impair Blind
+J Vitaminol (Kyoto)
+J Vocat Behav
+J Voice
+J Volunt Action Res
+J Volunt Adm
+J W Va Hist Assoc
+J Warburg Courtauld Inst
+J Wash Acad Sci
+J Water Health
+J Water Pollut Control Fed
+J West
+J West Aust Nurses
+J West Soc Periodontol Periodontal Abstr
+J West Va Philo Soc
+J Wilderness Med
+J Wildl Dis
+J Wildl Manage
+J Wis Dent Assoc
+J Wis State Dent Soc
+J Women Aging
+J Womens Heal
+J Womens Health
+J Womens Health (Larchmt)
+J Womens Health Gend Based Med
+J Womens Hist
+J World Intellect Prop
+J Wound Care
+J Wound Ostomy Continence Nurs
+J Youth Adolesc
+J Zhejiang Univ Sci
+J Zhejiang Univ Sci B
+J Zoo Wildl Med
+J Zool
+JAAPA
+JACEP
+JAMA
+JAPCA
+JBR-BTR
+JCAH Perspect
+JEMS
+JFMA
+JFORL J Fr Otorhinolaryngol Audiophonol Chir Maxillofac
+JICA Newsl
+JK Pract
+JNMA J Nepal Med Assoc
+JOGN Nurs
+JOICFP News
+JOICFP Rev
+JONAS Healthc Law Ethics Regul
+JOP
+JPEN J Parenter Enteral Nutr
+JPO J Pract Orthod
+JSAC Grapevine
+JSLS
+Jaarb K Ned Bot Ver
+Jaarb Kankeronderz Kankerbestrijd Ned
+Jaarb Numaga
+Jabega
+Jahrb Albertus Univ Koenigsb
+Jahrb Antike Christentum
+Jahrb Brandenbg Landesgesch
+Jahrb Coburg Landesstift
+Jahrb Gesch
+Jahrb Gesch Osteur
+Jahrb Gesch Staat Wirtsch Ges Lateinam
+Jahrb Hist Forsch Bundesrepub Dtschl
+Jahrb Hist Ver Furst Liechtenstein
+Jahrb Inst Dtsch Gesch
+Jahrb Inst Gesch Med Robert Bosch Stift
+Jahrb Landeskd Niederosterr
+Jahrb Natl Okon Stat
+Jahrb Oberoesterr Musealver
+Jahrb Schlesisch Friedrich Wilhelms Univ Breslau
+Jahrb Sozialwiss
+Jahrb Univ Duesseld
+Jahrb Vereins Gesch Stadt Wien
+Jahrb Wirtschaftsgesch
+Jahresber Schweiz Akad Med Wiss
+Jam J
+Jamaican Nurse
+James Arthur Lect
+James Joyce Q
+Janasamkhya
+Janasamkhya Siksha Mukhapatra
+Janus
+Japan World Econ
+Japanese Econ Stud
+Jednota Annu Furdek
+Jerusalem Q
+Jeune Afr
+Jew Aff
+Jew Dig
+Jew J Sociol
+Jew Life
+Jew Mem Hosp Bull
+Jew Obs Middle East Rev
+Jew Q Rev
+Jew Soc Stud
+Ji Sheng Chong Xue Yu Ji Sheng Chong Bing Za Zhi
+Jian Kang Bao
+Jibiinkoka
+Jikeikai Med J
+Jikken Dobutsu
+Jikoketsu Yuketsu
+Jimlar Mutane
+Jing Ji Lun Wen
+Jinko Mondai Kenkyu
+Jinko Mondai Kenkyusho Nenpo
+Jinkogaku Kenkyu
+Jinrui Idengaku Zasshi
+Jinruigaku Zasshi
+John Marshall J Comput Inf Law
+John Marshall J Pract Proced
+John Marshall Law Rev
+Johns Hopkins Mag
+Johns Hopkins Med J
+Johns Hopkins Med J Suppl
+Johns Hopkins Med Lett Health After 50
+Johns Hopkins Nurses Alumni Mag
+Joint Bone Spine
+Jordan Dent J
+Jordemodern
+Jordmorbladet
+Josai Shika Daigaku Kiyo
+Josanpu Zasshi
+Journ Annu Diabetol Hotel Dieu
+Journal
+Journal Hist
+Journal Q
+Journalism Monogr
+Jpn Circ J
+Jpn Dent J
+Jpn Heart J
+Jpn Hosp
+Jpn J Antibiot
+Jpn J Appl Phys
+Jpn J Cancer Res
+Jpn J Clin Oncol
+Jpn J Crop Sci
+Jpn J Dermatol B
+Jpn J Exp Med
+Jpn J Gastroenterol
+Jpn J Genet
+Jpn J Hum Genet
+Jpn J Infect Dis
+Jpn J Med
+Jpn J Med Sci Biol
+Jpn J Microbiol
+Jpn J Obstet Gynecol
+Jpn J Ophthalmol
+Jpn J Pharmacol
+Jpn J Physiol
+Jpn J Psychiatry Neurol
+Jpn J Psychon Sci
+Jpn J Surg
+Jpn J Thorac Cardiovasc Surg
+Jpn J Tuberc
+Jpn J Tuberc Chest Dis
+Jpn J Vet Res
+Jpn Med J
+Jpn Med J (Natl Inst Health Jpn)
+Jpn Psychol Res
+Jpn Q
+Jpn Stud Hist Sci
+Jt Comm J Qual Improv
+Jt Comm J Qual Patient Saf
+Jt Comm J Qual Saf
+Jt Comm Perspect
+Jud Librariansh
+Judaism
+Judges J
+Judicature
+Jugosl Ginekol Opstet
+Jugosl Ginekol Perinatol
+Jugosl Pregl
+Julk Suom Naishammaslaak Ryhma
+Jurid Rev
+Jurimetrics
+Juris Dr
+Jurist
+Juristat
+Justus Liebigs Ann Chem
+Juv Fam Court J
+Jyske Saml
+K Krigsvetenskapakad Handlingar Tidskr
+K Rep
+KIPH Bull
+KY Law J
+KY Rep
+Kagakushi Kenkyu
+Kaibogaku Zasshi
+Kaiin Dayori Nippon Kontakuto Renzu Gakkai
+Kajok Kyehoek Nonjip
+Kaku Igaku
+Kampener Alm
+Kanagawa Shigaku
+Kangaroo
+Kango
+Kango Gijutsu
+Kango Kenkyu
+Kango Kyoiku
+Kango Kyoshitsu
+Kango Tenbo
+Kangogaku Zasshi
+Kanho Hakhoe Chi
+Kanhohak Tamgu
+Kans Geogr
+Kans Hist
+Kans Hist Q
+Kans Med
+Kans Nurse
+Kans Statut Annot Kans
+Kansai Daigaku Keizai Ronshu
+Kansenshogaku Zasshi
+Kanzo
+Kao Ku Jen Lei Hsueh Kan
+Kaohsiung J Med Sci
+Kardiol Pol
+Kardiologiia
+Kassenzahnarzt Colloq Med Dent
+Kathmandu Univ Med J (KUMJ)
+Katilolehti
+Katollik Taehak Uihakpu Nonmunjip
+Katunob
+Kazan Med Zh
+Keep Posted
+Keio Econ Stud
+Keio J Med
+Keisei Geka
+Kekkaku
+Kemi
+Kennedy Inst Ethics J
+Kenya Nurs J
+Kenya Stat Dig
+Key Report
+Keystone Folkl Q
+Khelmintologiia
+Khimiia Meditsina
+Khirurgiia (Mosk)
+Khirurgiia (Sofiia)
+Kidney
+Kidney Blood Press Res
+Kidney Int
+Kidney Int Suppl
+Kiel Geogr Schr
+Kieler Beitr Gesch Med Pharm
+Kinderarztl Prax
+Kinderkrankenschwester
+Kinetoplastid Biol Dis
+King Faisal Spec Hosp Med J
+Kiryat Sefer
+Kisaengchunghak Chapchi
+Kisechugaku Zasshi
+Kiserl Orvostud
+Kita Kanto Igaku
+Kitasato Arch Exp Med
+Kiva
+Kleio
+Klin Anasthesiol Intensivther
+Klin Khir
+Klin Lab Diagn
+Klin Lech Zlokach Novoobraz
+Klin Med (Mosk)
+Klin Med Osterr Z Wiss Prakt Med
+Klin Mikrobiol Infekc Lek
+Klin Monatsbl Augenheilkd
+Klin Oczna
+Klin Padiatr
+Klin Sygepleje
+Klin Wochenschr
+Knappschaftsarzt
+Knee
+Knee Surg Sports Traumatol Arthrosc
+Ko Hsueh Tung Pao
+Kobe Daigaku Igakubu Kiyo
+Kobe Ika Daigaku Kiyo
+Kobe J Med Sci
+Koinonia
+Koku Eisei Gakkai Zasshi
+Kokubyo Gakkai Zasshi
+Kokuritsu Iyakuhin Shokuhin Eisei Kenkyusho Hokoku
+Kokusaigaku revyu
+Kokyu To Junkan
+Kokyuki Shinryo
+Koln Med Beitr
+Kolner Z Soz Sozialpsychol (Aufl)
+Komun Mazur Warm
+Kongressbd Dtsch Ges Chir Kongr
+Konjunkturpolitik
+Korea (South)
+Korea J
+Korea J Popul Dev
+Korean J Biochem
+Korean J Biol Sci
+Korean J Gastroenterol
+Korean J Hepatol
+Korean J Intern Med
+Korean J Ophthalmol
+Korean J Parasitol
+Korean J Radiol
+Korot
+Kos
+Koshu Eisei In Kenkyu Hokoku
+Kosm Biol Aviakosm Med
+Kosm Biol Med
+Kosm Ser A Biol
+Kosmas
+Kotiseutu
+Krankengymnastik
+Krankenpfl J
+Krankenpfl Soins Infirm
+Krankenpflege (Frankf)
+Krankenschwester
+Krebsarzt
+Krebsforsch Krebsbekampf
+Kritiek
+Kroc Found Ser
+Kronika
+Kulak Burun Bogaz Ihtis Derg
+Kult Fiz
+Kult Spolecz
+Kult Tech
+Kulturpflanze
+Kumamoto Igakkai Zasshi
+Kumamoto Med J
+Kun Chong Xue Bao
+Kunst Orient
+Kunst Ther
+Kupr Spoud
+Kurinikaru Sutadi
+Kurume Med J
+Kwart Hist
+Kwart Hist Kult Mater
+Kwart Hist Nauki Tech
+Kwart Hist Ruchu Zaw
+Ky Bench Bar
+Ky Dent J
+Ky Folkl Rec
+Ky Hosp Mag
+Ky Med J
+Ky Nurse
+Kybernetik
+Kyklos
+Kyobu Geka
+Kyoto Daigaku Kekkaku Kenkyusho Kiyo
+Kyoto Daigaku Kokukagaku Kiyo
+Kyushu J Med Sci
+LA Law Rev
+LA Rep
+LARC Med
+LC GC
+LDA J
+LDI Issue Brief
+LJ Spec Rep
+LMT
+LOVTID K DAN A
+LTC Regul Risk Liabil Advis
+La Hist
+La Stud
+Lab Anim
+Lab Anim (NY)
+Lab Anim Care
+Lab Anim Sci
+Lab Chip
+Lab Delo
+Lab Hematol
+Lab Invest
+Lab Med
+Lab Pract
+Lab Res Methods Biol Med
+Lab World
+Labor Hist
+Labor Law J
+Labor Lawyer
+Laboratorio
+Labour
+Labour Cap Soc
+Labour Hist
+Labour Soc
+Lactation Rev
+Ladies Home J
+Laeknabladid
+Lahey Clin Bull
+Lahey Clin Found Bull
+Lait
+Lakartidningen
+Lamp
+Lampada
+Lancet
+Lancet Infect Dis
+Lancet Neurol
+Lancet Oncol
+Land Aalst
+Land Econ
+Land Water Law Rev
+Land use policy
+Landarzt
+Landsc Urban Plan
+Landscape
+Lang Commun
+Lang Speech
+Lang Speech Hear Serv Sch
+Langages
+Langenbecks Arch Chir
+Langenbecks Arch Chir Suppl II Verh Dtsch Ges Chir
+Langenbecks Arch Chir Suppl Kongressbd
+Langenbecks Arch Klin Chir Ver Dtsch Z Chir
+Langenbecks Arch Surg
+Langmuir
+Laryngol Rhinol Otol (Stuttg)
+Laryngorhinootologie
+Laryngoscope
+Laser Phys
+Lasers Med Sci
+Lasers Surg Med
+Lasers Surg Med Suppl
+Lat Am Perspect
+Lat Am Popul Hist Bull
+Lat Am Popul Hist News
+Lat Am Res Rev
+Late Imp China
+Lateinamerika
+Laterality
+Latomus
+Lattante
+Laund News
+Lav Ist Anat Istol Patol Univ Studi Perugia
+Lav Um
+Laval Med
+Law Contemp Probl
+Law Context
+Law File
+Law Hum Behav
+Law Hum Genome Rev
+Law Inequal
+Law Inst J
+Law Justice
+Law Lib
+Law Libr J
+Law Med Health Care
+Law Med News
+Law Ment Health
+Law Philos
+Law Policy
+Law Policy Int Bus
+Law Policy Q
+Law Psychol Rev
+Law Q Rev
+Law Rev
+Law Soc Inq
+Law Soc Rev
+Law Teach
+Laws Ill
+Laws P R Annot P R
+Laws State N M N M
+Laws State Or Or
+Leadersh Health Serv
+League Exch
+League Lines
+Learn Behav
+Learn Mem
+Learn Motiv
+Leban Pharm J
+Lebensversicher Med
+Leber Magen Darm
+Lect Sci Basis Med
+Leech
+Leeds Dent J
+Leg Aspects Med Pract
+Leg Ethics
+Leg Med
+Leg Med (Tokyo)
+Leg Med Annu
+Leg Med Q
+Leg Stud
+Leg Stud (Soc Leg Scholars)
+Legal Ref Serv Q
+Lege Artis Med
+Lek List
+Lek Obz
+Lek Pr
+Lek Veda Zahr
+Lek Wojsk
+Lemouzi
+Lens Eye Toxic Res
+Lens Res
+Leodium
+Leopoldina
+Lepr India
+Lepr Rev
+Lethaia
+Lett Appl Microbiol
+Lett Ford Found
+Lett Ital
+Leuk Lymphoma
+Leuk Res
+Leukemia
+Leuvense Bijdr
+Leveltari Kozl
+Leveltari Sz
+Lex Sci
+Lexis DC Code DC
+Leyte Samar Stud
+Lias
+Lib Life Fam
+Lib Sci Slant Doc
+Liberation
+Liberian Stud J
+Libr Assoc Rec
+Libr Bull Univ Lond
+Libr Chron
+Libr Chron Univ Tex Austin
+Libr Cult
+Libr Hist
+Libr J
+Libr Q
+Libr Resour Tech Serv
+Libr Rev (Lond)
+Libr Technol Rep
+Libr Trends
+Library (Lond)
+Lichenologist
+Liet Istor Metrast
+Liet Tsr Aukst Mokyklu Mokslo Darb
+Life
+Life Sci
+Life Sci Adv
+Life Sci Adv Exp Clin Endocrinol
+Life Sci Adv Plant Physiol
+Life Sci I
+Life Sci II
+Life Sci Space Res
+Life Support Biosph Sci
+Life Support Syst
+Life Threat Behav
+Lifelong Learn Adult Years
+Lifetime Data Anal
+Ligament
+Liguorian
+Lijec Vjesn
+Lik Sprava
+Lilith
+Lille Chir
+Lille Med
+Limburg
+Limnol Oceanogr
+Lin Chuang Er Bi Yan Hou Ke Za Zhi
+Lin Chuang Kan Tan Ping Tsa Chih
+Linacre Q
+Linc Her
+Linc Law Rev
+Lincoln Rev
+Linen Supply News
+Lingua Nostra
+Links
+Linnean
+Lipids
+Lipids Health Dis
+Lipp Mitt Gesch Landeskd
+Lippincott Health Promot Lett
+Lippincotts Case Manag
+Lippincotts Prim Care Pract
+Lishi Yanjiu
+Listener
+Listening
+Lit Discuss
+Lit Eildienst Roche
+Lit Hist
+Lit Med
+Literatura
+Litt Med Soc
+Lituanus
+Liver
+Liver Int
+Liver Transpl
+Liver Transpl Surg
+Liverp Classical Mon
+Liverp Law Rev
+Livre Estampe
+Livrustkammaren
+Llano Estac Heritage
+Lloydia
+Llull
+Local Hist
+Local Popul Stud
+Locus
+Log
+Logbuch
+Logoped Phoniatr Vocol
+Logos
+Logos (Santa Clara)
+Loma Linda Univ Dent Mag
+Lond Archaeol
+Lond Clin Med J
+Lond Hosp Gaz
+Lond J
+Long Isl Forum
+Long Island Hist J
+Long Range Plann
+Long Term Care (Don Mills)
+Long Term Care Health Serv Adm Q
+Long Term Care Q
+Longevita
+Los Alamos Sci
+Los Angel Lawyer
+Los Angeles Times
+Loss Grief Care
+Loteria (Panama)
+Lotta Tuberc
+Louv Med
+Louv Stud
+Lovelace Clin Rev
+Loyola Consum Law Rev
+Loyola Law Rev
+Loyola Los Angel Int Comp Law J
+Loyola Los Angel Law Rev
+Loyola Univ Chicago Law J
+Luft Raumfahrt
+Luminescence
+Lung
+Lung Cancer
+Lupus
+Luso-Braz Rev
+Lutheran Q
+Lutheran Theol J
+Lutte Cancer
+Luzif Amor
+Lychnos Lardomshist Samf Arsb
+Lymphat Res Biol
+Lymphokine Cytokine Res
+Lymphokine Res
+Lymphology
+Lyon Chir
+Lyon Med
+Lyon Mediterr Med Med Sud Est
+Lyon Pharm
+M B Pharm Bull
+MAAS J Islam Sci
+MAGMA
+MARHIA
+MCH News PAC
+MCN Am J Matern Child Nurs
+MD
+MD Comput
+MD Law Rev
+MGH News
+MGMA Connex
+MH
+MIMS Mag
+MITS Technol Rev
+MLM Rep
+MLN Bull
+MLO Med Lab Obs
+MMW Fortschr Med
+MMW Munch Med Wochenschr
+MMWR CDC Surveill Summ
+MMWR Morb Mortal Wkly Rep
+MMWR Recomm Rep
+MMWR Surveill Summ
+MNA Accent
+MPS
+MRS Bull
+MSDA J
+Ma Zui Xue Za Zhi
+Maandbl Oud Utrecht
+Maandschr Cent Bur Stat
+Maandschr Kindergeneeskd
+Maandstat Bevolking
+Maandstat Bevolking Volksgezond
+Maastrich J Eur Comp Law
+Macleans
+Macromol Biosci
+Macromolecules
+Mademoiselle
+Madj Persat Dokt Gigi Indones
+Madjalah Kedokt Indones
+Mag Albemarle Cty Hist
+Mag Antiq
+Mag Istor
+Mag Litt
+Maghreb Machrek
+Maghreb Med
+Maghreb Rev
+Magn Reson Annu
+Magn Reson Chem
+Magn Reson Imaging
+Magn Reson Imaging Clin N Am
+Magn Reson Med
+Magn Reson Med Sci
+Magn Reson Q
+Magnes Res
+Magnes Trace Elem
+Magnesium
+Magy Belorv Arch
+Magy Noorv Lapja
+Magy Onkol
+Magy Radiol
+Magy Seb
+Magy Traumatol Orthop Helyreallito Seb
+Magy Traumatol Ortop Kezseb Plasztikai Seb
+Magy Tud
+Maharashtra Med J
+Mahidol Popul Q Gaz
+Maia
+Maine Hist Soc Q
+Maine Law Rev
+Maine Nurse
+Maine Revis Statut Annot 1964 Maine
+Mainlines
+Majalah Demografi Indones
+Majalah Obstet Ginekol Indones
+Majallah Jamia Dandan Pazshki
+Majallat Albuhuth Waaldirasat Alarabiyah
+Majallat Aldiwan Alqawmi Lilusrah Waal Umran lbashari
+Majallat Ma had al Makhtutat al Arabiy
+Majallat Niqabat Attiba Alasnnan Alurduniyah
+Majallat Tibb Alasnan Alsuriyah
+Majallat Tibb Alfamm Alsuriyah
+Major Probl Clin Pediatr
+Major Probl Clin Surg
+Major Probl Intern Med
+Major Probl Obstet Gynecol
+Major Probl Pathol
+Mak Rounds Health Faith Ethics
+Makedon Med Pregl
+Mal Cardiovasc
+Malacologia
+Malaga
+Malar J
+Malar Wkly
+Malawi Med J
+Malawian Geogr
+Malay Econ Rev
+Malays Dent J
+Malays J Pathol
+Malays J Reprod Health
+Malays J Trop Geogr
+Male Nurses J
+Malloch Room Newsl
+Malpract Dig
+Mamm Genome
+Mammalia
+Man Hist
+Man India
+Man Med
+Man Ther
+Manag Care
+Manag Care Interface
+Manag Care Q
+Manag Care Strateg
+Manag Compliance Ser
+Manag Int Dev
+Manag Medicare Medicaid News
+Manage Focus
+Manage Rev
+Manage Sci
+Manage World
+Manch Med Gaz
+Manch Men
+Manchester Sch Econ Soc Stud
+Manedsskr Prakt Laegegern
+Manit Law J
+Manit Med Rev
+Mankind
+Mankind Q
+Manshur Atmajallat Dirasat Alkhalij Waal Jazirah Alarabiyah
+Manuf Chem Aerosol News
+Manuscripta
+Manuscripts (N Y)
+Manushi
+Mar Behav Physiol
+Mar Biotechnol (NY)
+Mar Chem
+Mar Corps Gaz
+Mar Ecol Prog Ser
+Mar Environ Res
+Mar Geol
+Mar Mirror
+Mar Pollut Bull
+Mar Rundsch
+Marbg Geogr Schr
+Marbg Schrift Medgesch
+Marga
+Margin
+Marginalia Dermatol
+Margins (Baltim)
+Mark Health Serv
+Markers
+Maroc Med
+Marquette Law Rev
+Marquette Med Rev
+Marriage
+Marriage Fam Living
+Marriage Fam Newsl
+Marriage Fam Rev
+Mars Chir
+Mars Med
+Marx Perspect
+Mass Gen Laws Annot Mass
+Mass Hist Soc Proc
+Mass Journal Ment Health
+Mass Law Rev
+Mass Nurse
+Mass Physician
+Mass Rep Mass Supreme Judic Court
+Mass Rev
+Mass Spectrom Rev
+Masterkey Ind Lore Hist
+Masui
+Matekon
+Mater Bevolkwiss
+Mater Cult
+Mater Des
+Mater Hist Bull
+Mater Manag Health Care
+Mater Med Greca
+Mater Med Nordmark
+Mater Med Pol
+Mater Perform
+Mater Pr Antropol
+Mater Res Bull
+Matern Child Health J
+Matern Child Nurs J
+Matern Infanc (Sao Paulo)
+Maternite
+Materwiss Werksttech
+Math Biosci
+Math Comput Model
+Math Comput Simul
+Math Med Biol
+Math Popul Stud
+Math Soc Sci
+Matrix
+Matrix Biol
+Matrix Suppl
+Maturitas
+Mawazo
+Maxwell Rev
+Mayan
+Mayo Clin Health Lett
+Mayo Clin Proc
+Mayo Clin Womens Healthsource
+Mazingira
+McCalls
+McGeorge Law Rev
+McGill J Educ
+McGill Law J
+McGill News
+McKinneys Consol Laws N Y Annot N Y State
+Mcgill Dent Rev
+Mcgill Med J
+Mcgill Queens Hannah Inst Stud Hist Med Health Soc
+Mcgraw Hills Med Health
+Mcgraw Hills Wash Rep Med Health
+Md Bar J
+Md Geneal Soc Bull
+Md Hist
+Md Hist Mag
+Md J Contemp Leg Issues
+Md J Int Law Trade
+Md Law Forum
+Md Med
+Md Med J
+Md Nurs News
+Md Nurse
+Md State Med J
+Mead Johnson Symp Perinat Dev Med
+Meander
+Meas Sci Technol
+Mech Ageing Dev
+Mech Dev
+Mech Eng
+Med Actual
+Med Aeronaut
+Med Aeronaut Spat
+Med Aff
+Med Afr Noire
+Med Ann Dist Columbia
+Med Annu
+Med Anthropol
+Med Anthropol Newsl
+Med Anthropol Q
+Med Arh
+Med Armees
+Med Art
+Med Arts Sci
+Med Aspects Hum Sex
+Med Audio Vis
+Med Bild Dienst
+Med Biol
+Med Biol Eng
+Med Biol Eng Comput
+Med Biol Illus
+Med Bohringer (Overseas)
+Med Br (Lond)
+Med Bull (Ann Arbor)
+Med Bull Harrisburg Polyclin Hosp
+Med Bull Istanbul Med Fac
+Med Bull St Louis Univ
+Med Bull U S Army Army 1st
+Med Bull U S Army Eur Command Med Div
+Med Bull US
+Med Bull US Army Eur
+Med Bull Uganda
+Med Bull Vet Adm
+Med Care
+Med Care Res Rev
+Med Care Rev
+Med Chem
+Med Chem Res
+Med Chir Dig
+Med Cir (Bogota)
+Med Cir Farm
+Med Cir Guerra
+Med Claims Manag
+Med Clin (Barc)
+Med Clin North Am
+Med Clin Sper
+Med Colon
+Med Confl Surviv
+Med Contact (Bussum)
+Med Contemp
+Med Counterpoint
+Med Cutan Ibero Lat Am
+Med Decis Making
+Med Dent J
+Med Deporte Trab
+Med Des Mater
+Med Desarrollo
+Med Device Technol
+Med Dig
+Med Dimens
+Med Dok
+Med Dosim
+Med Dosw
+Med Dosw Mikrobiol
+Med Econ
+Med Educ
+Med Egypte
+Med Electron
+Med Electron Biol Eng
+Med Electron Microsc
+Med Emory
+Med Eng Phys
+Med Esp
+Med Ethics
+Med Ethics (Burlingt, Mass)
+Med Ethics Pract Royal Pharm Soc G B
+Med Etika Bioet
+Med Exp Int J Exp Med
+Med Fis Rehabil
+Med Forum
+Med Fr (Bombay)
+Med Gen Fr
+Med Geogr Herit
+Med Ges
+Med Ges Gesch
+Med Ges Gesch Beih
+Med Gesch Kult
+Med Glas
+Med Glob Surviv
+Med Group Manage
+Med Group Manage J
+Med Gynaecol Androl Sociol
+Med Gynaecol Sociol
+Med Health
+Med Health Care Philos
+Med Health R I
+Med Herit
+Med Hist
+Med Hist (Barc)
+Med Hist Aust
+Med Hist Suppl
+Med Hoje
+Med Humanit
+Med Humanit Rev
+Med Hyg (Geneve)
+Med Hypotheses
+Med Illus
+Med Image Anal
+Med Immunol
+Med Infant
+Med Infant (Paris)
+Med Inform (Lond)
+Med Inform Internet Med
+Med Insight
+Med Instrum
+Med Int
+Med Int (Milano)
+Med Intensiva
+Med Interface
+Med Interna
+Med Interna (Bucur)
+Med Interne
+Med J Aust
+Med J Cairo Univ
+Med J Egypt Armed Forces
+Med J Islam Repub Iran
+Med J Malaya
+Med J Malaysia
+Med J Osaka Univ
+Med J Southwest
+Med J Zambia
+Med Klin
+Med Klin (Munich)
+Med Klin Suppl
+Med Klin [Klin]
+Med Klin [Prax]
+Med Lab
+Med Lab (Stuttg)
+Med Lab Sci
+Med Lab Technol
+Med Lat
+Med Lav
+Med Law
+Med Law Int
+Med Law Rev
+Med Leg Bull
+Med Leg Dommage Corpor
+Med Leg J
+Med Lett Drugs Ther
+Med Libre
+Med Maandbl
+Med Mal Infect
+Med Malpract Cost Containment J
+Med Manag Netw
+Med Mark Media
+Med Microbiol Immunol (Berl)
+Med Mol Morphol
+Med Monatsschr
+Med Monatsschr Pharm
+Med Monde
+Med Moral Newsl
+Med Mycol
+Med Netw Strategy Rep
+Med Newsl (Lond)
+Med Nov
+Med Nowozytna
+Med Off
+Med Oncol
+Med Oncol Tumor Pharmacother
+Med Opin Rev
+Med Oral
+Med Oral Patol Oral Cir Bucal
+Med Panam
+Med Parazitol (Mosk)
+Med Pediatr Oncol
+Med Pediatr Oncol Suppl
+Med Periskop Ingelheim
+Med Pharmacol Exp Int J Exp Med
+Med Phys
+Med Pr
+Med Pract
+Med Pract (Zaragoza)
+Med Pregl
+Med Press
+Med Princ Pract
+Med Probl Perform Art
+Med Proc (Johannesb)
+Med Prod Sales
+Med Prog
+Med Prog Technol
+Med Prom SSSR
+Med Psicosom
+Med Radiogr
+Med Radiogr Photogr
+Med Radiol
+Med Radiol (Mosk)
+Med Razgl
+Med Rec Ann
+Med Rec Health Care Inf J
+Med Rec News
+Med Ref Serv Q
+Med Res Eng
+Med Res Rev
+Med Reserve (Paris)
+Med Rev
+Med Sachverstand
+Med Sci
+Med Sci (Paris)
+Med Sci Law
+Med Sci Monit
+Med Sci Res
+Med Sci Sports
+Med Sci Sports Exerc
+Med Secoli
+Med Sect Proc
+Med Segur Trab (Madr)
+Med Self Care
+Med Serv
+Med Serv J Can
+Med Sestra
+Med Soc (Berkeley)
+Med Sport (Berl)
+Med Sport (Roma)
+Med Sport Sci
+Med Staff Couns
+Med Teach
+Med Tech (Stuttg)
+Med Techn Bull
+Med Tekh
+Med Thorac
+Med Times
+Med Today
+Med Toxicol
+Med Toxicol Adverse Drug Exp
+Med Tr Prom Ekol
+Med Tradic
+Med Trial Tech Q
+Med Trib Med News
+Med Trop (Madr)
+Med Trop (Mars)
+Med Ultrasound
+Med Usine Rev Hyg Ind Mal Prof
+Med Vet Entomol
+Med Vjesn (Osijek)
+Med War
+Med Waste Anal
+Med Welt
+Med Weter
+Med Wieku Rozwoj
+Med Womans J
+Med World
+Med World (Lond)
+Med World News
+Med Year
+Med Zh Uzb
+MedGenMed
+Medd Flyg Navalmed Namnd Statens Namnd For Flyg Navalmed Forsk Forsoksvererksamhet
+Medd Nor Farm Selsk
+Medd Sundhedsstyr Beredskabsafdelingen
+Meded K Acad Wet Lett Schone Kunsten Belg Kl Wet
+Meded Rijksuniv Gent Fak Landbouwkd Toegep Biol Wet
+Medi-Cal Policy Inst Issue Brief
+Media & Gend Monit
+Media Asia
+Media Dev
+Mediaev Philos Pol
+Mediaev Stud
+Mediators Inflamm
+Medicam Hist Soc
+Medicamenta (Madr)
+Medicamentum
+Medicamundi
+Medicare Brief
+Medicina
+Medicina (B Aires)
+Medicina (Firenze)
+Medicina (Kaunas)
+Medicina (Madr)
+Medicina (Mex)
+Medicinar
+Medicine
+Medicine (Baltimore)
+Medico
+Medico (Eur)
+Medico (Int)
+Medico (Porto)
+Medicographia
+Medicoleg News
+Medicus
+Mediev Archaeol
+Mediev Humanist
+Medievales
+Medika (Zagreb)
+Medinfo
+Medioev Romanzo
+Medioevo Lat
+Mediterr Med
+Mediterr Peoples
+Medizinhist J
+Medizinische
+Medizinrecht
+Medscape Womens Health
+Medsurg Nurs
+Meharri Dent
+Meikai Daigaku Shigaku Zasshi
+Meisheimer Entomol Ser
+Melanges Casa Velazquez
+Melanges Ec Fr Rome Moyen Age Temps Mod
+Melanoma Res
+Melb Hist J
+Melb Univ Law Rev
+Melita Theol
+Mellen Hist Med
+Mem Acad Chir (Paris)
+Mem Acad Cienc Lisb Cl Cienc
+Mem Acad R Med Belg
+Mem Acad Sci Inscr B-Lett Toulouse
+Mem Cognit
+Mem Col Nac (Mex)
+Mem Inst Butantan
+Mem Inst Oswaldo Cruz
+Mem Soc Agric Commer Sci Arts Marne
+Memai Heiko Igaku
+Membr Biochem
+Membr Cell Biol
+Membranes
+Memo Med Res Counc
+Memo Rep Nav Med Res Inst (US)
+Memory
+Memphis Med J
+Memphis Mid South Med J
+Memphis State Univ Law Rev
+Men's Reprod Health
+Mendel Newsl
+Menninger Perspect
+Mennon Q Rev
+Menopause
+Mens Ondernem
+Ment Disabil Law Rep
+Ment Health (Lond)
+Ment Health Care
+Ment Health Nurs
+Ment Health Serv Res
+Ment Health Soc
+Ment Health Stat Note
+Ment Health Today
+Ment Hosp
+Ment Hyg
+Ment Phys Disabil Law Rep
+Ment Retard
+Ment Retard Abstr
+Ment Retard Dev Disabil Res Rev
+Ment Retard Law
+Mentalis
+Mentalities
+Mercer Dent Soc Newsl
+Mercer Law Rev
+Merck Rep
+Merrill Palmer Q
+Merrill Palmer Q Behav Dev
+Mese Sanit
+Mesoamerica (Antigua Guatem)
+Met Ions Biol Syst
+Meta
+Metab Bone Dis Relat Res
+Metab Brain Dis
+Metab Eng
+Metab Ophthalmol
+Metab Pediatr Ophthalmol
+Metab Pediatr Syst Ophthalmol
+Metabolism
+Metabolomics
+Metamedicine
+Meteorit Planet Sci
+Meteoritics
+Methodist Hist
+Methodol Sci
+Methods
+Methods Achiev Exp Pathol
+Methods Biochem Anal
+Methods Cell Biol
+Methods Cell Sci
+Methods Enzymol
+Methods Find Exp Clin Pharmacol
+Methods Inf Med
+Methods Inf Med Suppl
+Methods Med Res
+Methods Mol Biol
+Methods Mol Med
+Mex Stud
+Mezzog Eur
+Mich Acad
+Mich Appeals Rep Mich Court Appeals
+Mich Compil Laws Annot Mich
+Mich Health Hosp
+Mich Hist
+Mich Hist Rev
+Mich Hosp
+Mich J Gend Law
+Mich Jew Hist
+Mich Law Rev
+Mich Med
+Mich Nurse
+Mich Q Rev
+Mich Statut Annot Mich
+Michies Annot Code Public Gen Laws Md Md
+Michies W Va Code Annot W Va
+Microb Cell Fact
+Microb Comp Genomics
+Microb Drug Resist
+Microb Ecol
+Microb Pathog
+Microb Releases
+Microbes Infect
+Microbiol Esp
+Microbiol Immunol
+Microbiol Mol Biol Rev
+Microbiol Parazitol Epidemiol (Bucur)
+Microbiol Res
+Microbiol Rev
+Microbiol Sci
+Microbiologia
+Microbiologica
+Microbiology
+Microbios
+Microchem J
+Microcirc Endothelium Lymphatics
+Microcirculation
+Microgravity Q
+Microgravity Sci Technol
+Micron
+Micronesica
+Microsc Acta
+Microsc Acta Suppl
+Microsc Electron Biol Celular
+Microsc Microanal
+Microsc Res Tech
+Microscope
+Microscopy
+Microsurgery
+Microvasc Res
+Mid Am
+Mid Am Rev Sociol
+MidCont J Archaeol
+Middle East Dent Oral Health
+Middle East Health
+Middle East J
+Middle East J Anaesthesiol
+Middle East J Anesthesiol
+Middle East Med J
+Middle East Rep
+Middle East Stud
+Midl Hist
+Midstream (N Y)
+Midway
+Midwest Alliance Nurs J
+Midwest Dent
+Midwest Med Ethics
+Midwest Q (Pittsb)
+Midwife Health Visit
+Midwife Health Visit Community Nurse
+Midwifery
+Midwifery Today Childbirth Educ
+Midwifery Today Int Midwife
+Midwives
+Midwives Chron
+Mie Med J
+Migr News
+Migr Teme
+Migr Today
+Migr World Mag
+Migration
+Mikrobiol Z
+Mikrobiol Zh
+Mikrobiologiia
+Mikrobiyol Bul
+Mikrochim Acta
+Mikrochim Ichnoanal Acta
+Mikroskopie
+Mil Aff
+Mil Law Rev
+Mil Med
+Mil Psychol
+Mil Rev
+Mil Surg
+Milbank Mem Fund Q
+Milbank Mem Fund Q Health Soc
+Milbank Q
+Milieux
+Milit Collect Hist
+Milit Hist Tex Southwest
+Militaerlaegen
+Militargeschichte
+Milw Hist
+Mind
+Miner Electrolyte Metab
+Mineral Mag
+Minerva
+Minerva Anestesiol
+Minerva Cardioangiol
+Minerva Cardioangiol Eur
+Minerva Chir
+Minerva Dermatol
+Minerva Dietol
+Minerva Dietol Gastroenterol
+Minerva Ecol Idroclimatol Fis Sanit
+Minerva Ecol Idroclimatol Fisiconucl
+Minerva Endocrinol
+Minerva Farm
+Minerva Fisiconucl
+Minerva Fisioter Radiobiol
+Minerva Gastroenterol
+Minerva Gastroenterol Dietol
+Minerva Ginecol
+Minerva Med
+Minerva Medicoleg
+Minerva Nefrol
+Minerva Neurochir
+Minerva Nipiol
+Minerva Nucl
+Minerva Oftalmol
+Minerva Ortognatod
+Minerva Ortop
+Minerva Otorinolaringol
+Minerva Pediatr
+Minerva Psichiatr
+Minerva Radiol
+Minerva Radiol Fisioter Radiobiol
+Minerva Stomatol
+Minerva Urol
+Minerva Urol Nefrol
+Mini Rev Med Chem
+Minim Invasive Neurosurg
+Minim Invasive Surg Nurs
+Minim Invasive Ther Allied Technol
+Minn Hist
+Minn Law Rev
+Minn Med
+Minn Nurs Accent
+Minn Pharm
+Minn Statut Annot Minn
+Minneap Dist Dent J
+Minor Nurse Newsl
+Minzoku Eisei
+Mirovaia Ekon Mezdunar Otnosheiia
+Misc Estud Arabes Hebraicos
+Mises Point Accouch Pediatre
+Miss Coll Law Rev
+Miss Dent Assoc J
+Miss Doct
+Miss Law J
+Miss Law Rev
+Miss Q
+Miss RN
+Miss Valley Med J
+Mission Hisp
+Mitochondrion
+Mitt Arbeitsmarkt Berufsforsch
+Mitt Dtsch Archaeol Inst Abt Kairo
+Mitt Dtsch Pharm Ges Pharm Ges DDR
+Mitt Forschungsbeitr Cusanus Ges
+Mitt Geb Lebensmittelunters Hyg
+Mitt Ges Salzburger Landeskd
+Mitt Krems Stadtarch
+Mitt Lebensmitteluntersuchung Hyg
+Mitt Med Abt Schweiz Unfallversicherunst
+Mitt Osterr Ges Tropenmed Parasitol
+Mitt Osterr Sanitatsverwalt
+Mittbl Osterr Ges Stat Inform
+Mitteldtsch Famkd
+Mittellat Jahrb
+Mmg
+Mnemosyne
+Mo Dent J
+Mo Hist Rev
+Mo Med
+Mo Nurse
+Mo Statut Annot Mo
+Mobius
+Mod Age
+Mod Asian Stud
+Mod China
+Mod Churchm
+Mod Concepts Cardiovasc Dis
+Mod Dent Pract
+Mod Healthc
+Mod Healthc (Short Term Care)
+Mod Hosp
+Mod Jud
+Mod Lang Notes
+Mod Law Rev
+Mod Med
+Mod Med Asia
+Mod Midwife
+Mod Nurs Home
+Mod Pathol
+Mod Philol
+Mod Probl Ophthalmol
+Mod Probl Paediatr
+Mod Probl Pharmacopsychiatry
+Mod Rheumatol
+Mod Schoolman
+Mod Treat
+Mod Trends Hum Reprod Physiol
+Mod Trends Immunol
+Mod Trends Med Virol
+Mod Trends Neurol
+Mod Trends Orthop
+Mod Trends Plast Surg
+Mod Trends Radiother
+Mod Trends Rheumatol
+Mod Trends Surg
+Mod Vet Pract
+Mohlomi
+Mol Aspects Med
+Mol Biochem Parasitol
+Mol Biol
+Mol Biol (Mosk 1972)
+Mol Biol (Mosk)
+Mol Biol Biochem Biophys
+Mol Biol Cell
+Mol Biol Evol
+Mol Biol Med
+Mol Biol Rep
+Mol Biotechnol
+Mol Biother
+Mol Cancer
+Mol Cancer Res
+Mol Cancer Ther
+Mol Carcinog
+Mol Cell
+Mol Cell Biochem
+Mol Cell Biol
+Mol Cell Biol Hum Dis Ser
+Mol Cell Biol Res Commun
+Mol Cell Endocrinol
+Mol Cell Neurosci
+Mol Cell Probes
+Mol Cell Proteomics
+Mol Cells
+Mol Chem Neuropathol
+Mol Diagn
+Mol Diagn Ther
+Mol Divers
+Mol Ecol
+Mol Ecol Notes
+Mol Endocrinol
+Mol Gen Genet
+Mol Gen Mikrobiol Virusol
+Mol Genet Genomics
+Mol Genet Med
+Mol Genet Metab
+Mol Hum Reprod
+Mol Imaging
+Mol Imaging Biol
+Mol Immunol
+Mol Interv
+Mol Mar Biol Biotechnol
+Mol Med
+Mol Med (Sofia)
+Mol Med Today
+Mol Membr Biol
+Mol Microbiol
+Mol Neurobiol
+Mol Nutr Food Res
+Mol Pain
+Mol Pathol
+Mol Pharm
+Mol Pharmacol
+Mol Phylogenet Evol
+Mol Phys
+Mol Plant Microbe Interact
+Mol Psychiatry
+Mol Reprod Dev
+Mol Syst Biol
+Mol Ther
+Mol Toxicol
+Mol Urol
+Mol Vis
+Moment
+Momentum
+Mon Bull Minist Health Public Health Lab Serv
+Mon Bull Stat U N Stat Off
+Mon Labor Rev
+Mon Not R Astron Soc
+Mon Rev
+Mon Vital Stat Rep
+Monaldi Arch Chest Dis
+Monash Bioeth Rev
+Monash Univ Law Rev
+Monastic Stud
+Monatsh Tierheilkd
+Monatsh Veterinarmed
+Monatskurse Arztl Fortbild [Bildbeil]
+Monatsschr Dtsch Zahnarzte Freie Zahnarzt
+Monatsschr Kinderheilkd
+Monatsschr Kriminol
+Monatsschr Ohrenheilkd Laryngorhinol
+Monatsschr Psychiatr Neurol
+Monatsschr Tuberkulosebekampf
+Monatsschr Unfallheilkd
+Monatsschr Unfallheilkd Versicher Versorg Verkehrsmed
+Monatsschr Unfallheilkd Versicherungsmed
+Mondai To Kenkyu
+Monday Dev
+Monde Alp Rhodan
+Monde Dent
+Monde Med
+Mondes Cult
+Mondes Dev
+Mondo Cin
+Mondo Odontostomatol
+Mondo Ortod
+Mong J Demogr
+Monist
+Monit Ostet Ginecol Endocrinol Metab
+Monit Ostet Ginecolog
+Monit Pharm Lab
+Monogr Allergy
+Monogr Am Assoc Ment Defic
+Monogr Am Assoc Ment Retard
+Monogr Atheroscler
+Monogr Clin Cytol
+Monogr Corp Cent Reg Poblac
+Monogr Dev Biol
+Monogr Endocrinol
+Monogr Gesamtgeb Neurol Psychiatr
+Monogr Gesamtgeb Psychiatr Psychiatry Ser
+Monogr Hum Genet
+Monogr Neoplast Dis Var Sites
+Monogr Neural Sci
+Monogr Oral Sci
+Monogr Paediatr
+Monogr Pathol
+Monogr Physiol Soc
+Monogr Popul Biol
+Monogr Quad Int Stor Med Sanita
+Monogr Ser World Health Organ
+Monogr Soc Res Child Dev
+Monogr Sov Med Sci
+Monogr Surg Sci
+Monogr Virol
+Monspel Hippocrates
+Mont Code Annot Mont
+Mont Law Rev
+Mont Wildl
+Montana
+Montfort
+Month
+Montp Med
+Montreal Med
+Monum Hist
+Monum Nippon
+Morb Mortal
+Morb Mortal Wkly Rep Surveill Summ
+Moreana
+Morfologiia
+Morphol Embryol (Bucur)
+Morphol Igazsagugyi Orv Sz
+Morphol Med
+Morphologie
+Mosq News
+Mother Jones
+Mothers Child
+Motor Control
+Mouth
+Mouv Soc
+Mov Disord
+Mov Operaio Soc
+Moyo
+Ms
+Mt Res Dev
+Mt Sinai J Med
+Mult Scler
+Multibody Syst Dyn
+Multis
+Multivariate Behav Res
+Munca Sanit
+Munch Med Wochenschr
+Mund Kiefer Gesichtschir
+Mundo Med
+Munger Afr Libr Notes
+Munnpleien
+Munstersche Beitr Gesch Theor Med
+Mus Geneve
+Mus Helv
+Muscle Biol
+Muscle Nerve
+Muscle Nerve Suppl
+Museon
+Music Lett
+Muslim World
+Muslim World (Hartford)
+Mutagenesis
+Mutat Res
+Mycol Res
+Mycologia
+Mycopathol Mycol Appl
+Mycopathologia
+Mycorrhiza
+Mycoses
+Mykosen
+Mykosen Suppl
+Mymensingh Med J
+N C Dent Gaz
+N C Dent J
+N C Folklor
+N C Hist Rev
+N C J Ment Health
+N C Med J
+N D Century Code N D
+N D Hist
+N D Law Rev
+N Engl Galaxy
+N Engl Hist Geneal Regist
+N Engl J Crim Civ Confin
+N Engl J Hum Serv
+N Engl J Med
+N Engl J Public Policy
+N Engl Reg Allergy Proc
+N H Revis Statut Annot N H
+N HC Perspect Community
+N J Healthc
+N J Hist
+N J Hist Comm Newsl
+N J J Pharm
+N J League Nurs News
+N J Med
+N J Nurse
+N J Statut Annot N J
+N M Dent J
+N M Hist Rev
+N M Law Rev
+N M Nurse
+N M Statut 1978 Annot N M
+N S Hist Rev
+N S Med Bull
+N S W Public Health Bull
+N Y Aff
+N Y Folk
+N Y Hist
+N Y J Dent
+N Y Law Forum
+N Y Law J
+N Y Law Sch J Hum Rights
+N Y Law Sch J Int Comp Law
+N Y Law School Hum Rights Annu
+N Y Med Coll News Notes
+N Y Med Q
+N Y Physician
+N Y State Bar J
+N Y State Dent J
+N Y State J Med
+N Y State Nurse
+N Y Suppl Second Ser
+N Y Times Mag
+N Y Univ J Dent
+N Y Univ J Int Law Polit
+N Y Univ J Legis Public Policy
+N Y Univ Law Rev
+N Z Bioeth J
+N Z Cartogr Geogr Inf Sys
+N Z Dent J
+N Z Fam Physician
+N Z Geog
+N Z Health Hospital
+N Z Hosp
+N Z J Fam Plann
+N Z J Geogr
+N Z J Hist
+N Z Law J
+N Z Law Rep
+N Z Libr
+N Z Med J
+N Z Nurs Forum
+N Z Nurs J
+N Z Popul Newsl
+N Z Popul Rev
+N Z Sch Dent Serv Gaz
+N Z Vet J
+NAACOG Newsl
+NAACOG Tech Bull
+NAACOGS Clin Issu Perinat Womens Health Nurs
+NABER Rep
+NACDL J
+NACUBO Bus Off
+NADA
+NADC MA United States Nav Air Dev Cen Johnsville Pa Aviat Med Accel Lab
+NADC-MR Rep
+NADL J
+NAHAM Access Manag J
+NAHAM Manage J
+NANR News
+NAPHT News
+NARESA Mongr
+NASA Contract Rep NASA CR
+NASPCP Newsl
+NATNEWS
+NBER Work Pap Ser
+NCBHR Commun
+NCCE News
+NCEHR Commun
+NCI Monogr
+NCP Bull
+NCSDHA Dent Hyg
+NCSL Legisbrief
+NDA J
+NEHW Health Watch
+NFAIS Newsl
+NFI Bull
+NFPA J
+NHPF Issue Brief
+NIAID AIDS Agenda
+NIDA Res Monogr
+NIH Consens Dev Conf Summ
+NIH Consens State Sci Statements
+NIH Consens Statement
+NIH Guide Grants Contracts
+NIHAE Bull
+NIPH Ann
+NITA
+NJPC Bull
+NJSNA Newsl
+NLN Conv Pap
+NLN Publ
+NLN Update
+NMR Biomed
+NP News
+NPG Forum Ser
+NPN Med
+NS Hist Q
+NT Learn Curve
+NTM
+NTP CERHR MON
+NY Dent J
+NY Folkl Q
+NY Hist Soc Q
+NY Law Sch Law Rev
+NY Med
+NY Times (Print)
+NYO Rep
+NYSSNTA J
+NZ J Psychol
+NZ Psychol
+Nachr Akad Wiss Gott Philol Hist Kl
+Nachr Inst Gesch Naturwiss Hamburg
+Nachrichtenbl Dtsch Ges Gesch Med Naturwiss Tech
+Nagasaki Igakkai Zasshi
+Nagoya J Med Sci
+Nagoya Med J
+Nahrung
+Naika
+Naika Hokan
+Nakadori Byoin Iho
+Nan Fang Yi Ke Da Xue Xue Bao
+Nano Lett
+Nanomedicine
+Nanotechnology
+Nanyang Wenti Yenchiu
+Nar Zdrav
+Narodonaselenie
+Naselenie
+Nasnewsletter
+Nasu Suteshon
+Nasza Przeslosc
+Nat Biotechnol
+Nat Cell Biol
+Nat Chem Biol
+Nat Clin Pract Cardiovasc Med
+Nat Clin Pract Gastroenterol Hepatol
+Nat Clin Pract Oncol
+Nat Clin Pract Urol
+Nat Genet
+Nat Hist
+Nat Immun
+Nat Immun Cell Growth Regul
+Nat Immunol
+Nat Law Forum
+Nat Mater
+Nat Med
+Nat Methods
+Nat Monspel Ser Bot
+Nat Neurosci
+Nat New Biol
+Nat Prod Lett
+Nat Prod Rep
+Nat Prod Res
+Nat Resour Forum
+Nat Resour J
+Nat Rev Cancer
+Nat Rev Drug Discov
+Nat Rev Genet
+Nat Rev Immunol
+Nat Rev Microbiol
+Nat Rev Mol Cell Biol
+Nat Rev Neurosci
+Nat Struct Biol
+Nat Struct Mol Biol
+Nat Syst
+Nat Toxins
+Nation
+Nationalokon Tidsskr
+Nations Bus
+Nations Health
+Natl AIDS Bull
+Natl Black Law J
+Natl Bur Econ Res Bull Aging Health
+Natl Cancer Inst Carcinog Tech Rep Ser
+Natl Cancer Inst Monogr
+Natl Cancer Inst Res Rep
+Natl Cathol Bioeth Q
+Natl Cathol Report
+Natl Civ Rev
+Natl Conf Dent Public Relat
+Natl Dent Assoc J
+Natl Eng
+Natl Fam Health Surv Bull
+Natl Fed Cathol Physicians Guild Newsl
+Natl Forum
+Natl Geogr Mag
+Natl Geogr Res
+Natl Hosp Health Care
+Natl Inst Anim Health Q (Tokyo)
+Natl Inst Drug Abuse Res Monogr Ser
+Natl Inst Econ Rev
+Natl Inst Health Consens Dev Conf Consens Statement
+Natl Inst Health Consens Dev Conf Summ
+Natl J (Wash)
+Natl Jew Law Rev
+Natl Jew Mon
+Natl Law J
+Natl Med Care Util Expend Surv B
+Natl Med Care Util Expend Surv C
+Natl Med J India
+Natl Med Leg J
+Natl Netw
+Natl Nosocomial Infect Study Rep
+Natl Obs
+Natl Pap
+Natl Popul News Bull
+Natl Rep Subacute Care
+Natl Rev
+Natl Saf News
+Natl Tax J
+Natl Toxicol Program Tech Rep Ser
+Natl Underwrit [Life Health]
+Natl Univ Law Rev
+Natl Vital Stat Rep
+Natl Widl
+Natl Womens Health Rep
+Natur Mus
+Natura
+Nature
+Naturwiss Rundsch
+Naturwissenschaften
+Nauchni Tr Med Acad Vulko Chervenkov
+Nauchni Tr Nauchnoizsled Stomatol Inst (Sofiia)
+Nauchni Tr Vissh Med Inst Sofiia
+Nauchnye Doki Vyss Shkoly Biol Nauki
+Naunyn Schmiedebergs Arch Exp Pathol Pharmakol
+Naunyn Schmiedebergs Arch Pharmacol
+Naunyn Schmiedebergs Arch Pharmakol
+Naunyn Schmiedebergs Arch Pharmakol Exp Pathol
+Nav Rev
+Navy Med
+Neb Law Rev
+Nebr Hist
+Nebr Med J
+Nebr Nurse
+Nebr State Med J
+Nebr Symp Motiv
+Ned Milit Geneeskd Tijdschr
+Ned Tandartsenbl
+Ned Tijdschr Geneeskd
+Ned Tijdschr Gerontol
+Ned Tijdschr Psychol
+Ned Tijdschr Tandheelkd
+Ned Tijdschr Verloskd Gynaecol
+Neerl Volksleven
+Nefrologia
+Negro Hist Bull
+Neirofiziologiia
+Neonatal Intensive Care
+Neonatal Netw
+Neoplasia
+Neoplasma
+Neopsichiatria
+Nepal Med Coll J
+Nepal Popul Dev J
+Nepegeszseguegy
+Nephrol Dial Transplant
+Nephrol News Issues
+Nephrol Nurs J
+Nephrol Nurse
+Nephrologie
+Nephrology (Carlton)
+Nephron
+Nephron Clin Pract
+Nephron Exp Nephrol
+Nephron Physiol
+Nerv Child
+Nerv Sist
+Nervenarzt
+Nestle Nutr Workshop Ser Clin Perform Programme
+Nestle Nutr Workshop Ser Pediatr Program
+Neth J Med
+Neth J Surg
+Netw Fr
+Netw Int Fertil Res Program
+Netw Res Triangle Park N C
+Network
+Neue Heimat
+Neue Jurist Wochenschr
+Neue Munch Beitr Gesch Med Naturwiss Medizinhist Reihe
+Neue Osterr Z Kinderheilkd
+Neue Polit Lit
+Neue Rundsch
+Neuere Med Wiss Quellen Stud
+Neues Jahrb Geol Palaontol Abh
+Neumol Cir Torax
+Neuphilol Mitt
+Neural Comput
+Neural Netw
+Neural Plast
+Neuro Endocrinol Lett
+Neuro-oncol
+NeuroRehabilitation
+NeuroRx
+Neurobehav Toxicol
+Neurobehav Toxicol Teratol
+Neurobiol Aging
+Neurobiol Dis
+Neurobiol Learn Mem
+Neurobiologia
+Neurobiology
+Neurobiology (Bp)
+Neurocase
+Neurochem Int
+Neurochem Pathol
+Neurochem Res
+Neurochirurgia (Stuttg)
+Neurochirurgie
+Neurocirugia
+Neurocirugia (Astur)
+Neurocomputing
+Neurocrit Care
+Neurodegeneration
+Neuroendocrinology
+Neuroepidemiology
+Neurofibromatosis
+Neurogastroenterol Motil
+Neurogenetics
+Neuroimage
+Neuroimaging Clin N Am
+Neuroimmunomodulation
+Neuroinformatics
+Neurol Clin
+Neurol Clin Neurophysiol
+Neurol Croat
+Neurol India
+Neurol Med Chir (Tokyo)
+Neurol Neurochir Pol
+Neurol Neurochir Psychiatr Pol
+Neurol Neurocir Psiquiatr
+Neurol Psihiatr Neurochir
+Neurol Psychiatr (Bucur)
+Neurol Psychiatr Ceskoslov
+Neurol Res
+Neurol Sci
+Neurologia
+Neurologija
+Neurologist
+Neurology
+Neuromolecular Med
+Neuromuscul Disord
+Neuron
+Neuron Glia Biol
+Neuroophthalmology
+Neuropadiatrie
+Neuropathol Appl Neurobiol
+Neuropathology
+Neuropatol Pol
+Neuropediatrics
+Neuropeptides
+Neuropharmacology
+Neurophysiol Clin
+Neurophysiology
+Neuropsichiatria
+Neuropsihijatrija
+Neuropsychiatr Enfance Adolesc
+Neuropsychiatry
+Neuropsychiatry Neuropsychol Behav Neurol
+Neuropsychobiology
+Neuropsychol Dev Cogn B Aging Neuropsychol Cogn
+Neuropsychol Rehabil
+Neuropsychol Rev
+Neuropsychologia
+Neuropsychology
+Neuropsychopharmacol Hung
+Neuropsychopharmacology
+Neuroradiology
+Neurorehabil Neural Repair
+Neuroreport
+Neurosci Behav Physiol
+Neurosci Biobehav Rev
+Neurosci Lett
+Neurosci Lett Suppl
+Neurosci News
+Neurosci Res
+Neurosci Res (N Y)
+Neurosci Res Commun
+Neurosci Res Program Bull
+Neurosci Res Suppl
+Neuroscience
+Neuroscientist
+Neurosignals
+Neurosurg Clin N Am
+Neurosurg Focus
+Neurosurg Rev
+Neurosurgery
+Neurotox Res
+Neurotoxicol Teratol
+Neurotoxicology
+Neurotoxins
+Neurourol Urodyn
+Neuss Jahrb
+Nev Hist Soc Q
+Nev Nurses Assoc Q Newsl
+Nev Revis Statut Nev
+Nev Rnformation
+Nevrone Period Neurol Psichiatr Sci Affin
+Nevropatol Psikhiatriia
+New Afr
+New Am Rev
+New Atlantis
+New Biol
+New Blackfriars
+New Cathol World
+New Dent
+New Dir Child Adolesc Dev
+New Dir Child Dev
+New Dir Ment Health Serv
+New Dir Youth Dev
+New Egypt J Med
+New Engl J Prison Law
+New Engl Law Rev
+New Engl Q
+New Era Nurs Image Int
+New Genet Soc
+New Ger Crit
+New Guard
+New Horiz
+New Humanist
+New Hung Q
+New Int
+New Istanbul Contrib Clin Sci
+New Law J
+New Lead
+New Left Rev
+New Lit Hist
+New Microbiol
+New Orleans Med Surg J
+New Perspect Q
+New Perspect Turk
+New Philipp
+New Philos
+New Physician
+New Phytol
+New Repub
+New Rev Bioeth
+New Scholasticism
+New Sci
+New Sci Sci J
+New Soc
+New South
+New Statesman
+New Theol Rev
+New Times
+New Univ Q
+New West
+New World
+New York
+New York Rev Books
+New Yorker
+New community
+Newborn Infant Nurs Rev
+Newcastle Med J
+Newfoundl Med Assoc J
+Newport
+Newport Hist
+News Bull Indian Dent Assoc
+News Lett Florence Nightingale Int Nurs Assoc
+News Lett Maine State Nurses Assoc
+News Nor
+News Notes Ohio Dent J
+News Physiol Sci
+News Views
+Newsette
+Newsl Am Acad Health Adm
+Newsl Am Acad Implant Dent
+Newsl Am Acad Pediatr
+Newsl Am Acad Psychiatry Law
+Newsl Am Assoc Tissue Banks
+Newsl Biomed Saf Stand
+Newsl Br Soc Hist Sci
+Newsl Can Soc Hist Med
+Newsl Fam Plan Assoc Hong Kong
+Newsl Fam Plan Int Assist
+Newsl Feminism Philos
+Newsl Group Use Psychol Hist
+Newsl Hist Anthropol
+Newsl Infant Feed Action Coalit
+Newsl Int Acad Periodontol
+Newsl Int Coll Dent India Sect
+Newsl Int Union Sci Study Popul
+Newsl Inter Afr Comm Tradit Pract Affect Health Women Child
+Newsl Ky Nurses Assoc
+Newsl Macro Syst Inst Resour Dev Demogr Health Surv
+Newsl Med Sci Hist Soc
+Newsl Scand Soc Forensic Odontol
+Newsl Sci Technol Human Values
+Newsl Soc Anc Med
+Newsl Soc Anc Med Pharm
+Newsl Soc Welf Hist Group
+Newsl Springfield Dent Soc
+Newsl Wis League Nurs
+Newsl Womens Glob Netw Reprod Rights
+Newsline People AIDS Coalit N Y
+Newswatch
+Newsweek
+Niagara Frontier
+Nicar Med
+Nicar Odontol
+Nice Hist
+Nice Med
+Nichibunken Newsl
+Nichidai Igaku Zasshi
+Nichidai Koko Kagaku
+Nicotine Tob Res
+Niederdtsch Mitt
+Niederhein Jahrb
+Niedersachs Arztebl
+Niedersachs Zahnarztebl
+Nieuwe Ned Bijdr Geschied Geneeskd Natuurwet
+Nieuwe West Indische Gids
+Niger Classics
+Niger Dent J
+Niger J Clin Pract
+Niger J Econ Soc Stud
+Niger J Med
+Niger J Paediatr
+Niger Mag
+Niger Med J
+Niger Med Pract
+Niger Nurse
+Niger Pop
+Niger Postgrad Med J
+Nihon Ago Kansetsu Gakkai Zasshi
+Nihon Arukoru Yakubutsu Igakkai Zasshi
+Nihon Eiyo Shokuryo Gakkai Shi
+Nihon Hansenbyo Gakkai Zasshi
+Nihon Hotetsu Shika Gakkai Zasshi
+Nihon Kango Kagakkaishi
+Nihon Kokyuki Gakkai Zasshi
+Nihon Kyobu Shikkan Gakkai Zasshi
+Nihon Rinsho Meneki Gakkai Kaishi
+Nihon Rinsho Shishubyo Danwakai Kaishi
+Nihon Shinkei Seishin Yakurigaku Zasshi
+Nihon Sutoma Rihabiriteshon Gakkaishi J Jpn Soc Stoma Rehabil
+Nihon Univ Comp Law
+Nihon Univ J Med
+Nihonshi Kenkyu
+Niigata Igakkai Zasshi
+Ninet Century
+Ninet Century Contexts
+Nineteenth Century Fict
+Nippon Byori Gakkai Kaishi
+Nippon Chokucho Komonbyo
+Nippon Daicho Komonbyo Gakkai Zasshi
+Nippon Densenbyo Gakkai Zasshi
+Nippon Eiseigaku Zasshi
+Nippon Funin Gakkai Zasshi
+Nippon Gan Chiryo Gakkai Shi
+Nippon Ganka Gakkai Zasshi
+Nippon Ganka Kiyo
+Nippon Geka Gakkai Zasshi
+Nippon Geka Hokan
+Nippon Heikatsukin Gakkai Zasshi
+Nippon Hifuka Gakkai Zasshi
+Nippon Hinyokika Gakkai Zasshi
+Nippon Hoigaku Zasshi
+Nippon Hoshasen Gijutsu Gakkai Zasshi
+Nippon Igaku Hoshasen Gakkai Zasshi
+Nippon Ika Daigaku Zasshi
+Nippon Ishigaku Zasshi
+Nippon Ishikai Zasshi
+Nippon Ishinkin Gakkai Zasshi
+Nippon Jibiinkoka Gakkai Kaiho
+Nippon Jinzo Gakkai Shi
+Nippon Juigaku Zasshi
+Nippon Ketsueki Gakkai Zasshi
+Nippon Koku Geka Gakkai Zasshi
+Nippon Koshu Eisei Zasshi
+Nippon Kyobu Geka Gakkai Zasshi
+Nippon Kyosei Shika Gakkai Zasshi
+Nippon Naibunpi Gakkai Zasshi
+Nippon Naika Gakkai Zasshi
+Nippon Rai Gakkai Zasshi
+Nippon Rinsho
+Nippon Ronen Igakkai Zasshi
+Nippon Saikingaku Zasshi
+Nippon Sanka Fujinka Gakkai Zasshi
+Nippon Seikeigeka Gakkai Zasshi
+Nippon Seirigaku Zasshi
+Nippon Shika Hyoron
+Nippon Shika Ishikai Zasshi
+Nippon Shika Zairyo Kikai Gakkai Zasshi
+Nippon Shishubyo Gakkai Kaishi
+Nippon Shokakibyo Gakkai Zasshi
+Nippon Shonika Gakkai Zasshi
+Nippon Suisan Gakkai Shi
+Nippon Yakurigaku Zasshi
+Nisshin Igaku Jpn J Med Prog
+Nitric Oxide
+No Shinkei Geka
+No To Hattatsu
+No To Shinkei
+Nobel Symp
+Noesis
+Noise Health
+Nomad People
+Nonlinear Dynamics Psychol Life Sci
+Nonlinearity Biol Toxicol Med
+Nonprofit Manag Leadersh
+Noordgouw
+Nor Geogr Tidsskr
+Nor Tannlaegeforen Tid
+Nor Teol Tidsskr
+Nord Hyg Tidskr
+Nord Hyg Tidskr Suppl
+Nord J Psychiatry
+Nord J Sov East Eur Stud
+Nord Med
+Nord Medicinhist Arsb
+Nord Psyk Medl
+Nord Psykiatr Tidsskr
+Nord Psykol
+Nord Sexol
+Nord Vet Med
+Norm Pathol Anat (Stuttg)
+Norois
+North Am Rev
+North Am Vet
+North Carol Centr Law J
+North Carol Law Rev
+North East Rep Second Ser
+North Hist
+North Ill Univ Law Rev
+North Irel Law Rep
+North Irel Leg Q
+North KY Law Rev
+North Ky State Law Forum
+North Scotl
+North Va Herit
+North West Rep Second Ser
+Northeast Afr Stud
+Northeast Folk
+Northeast Nev Hist Soc Q
+Northwest Dent
+Northwest Dent Res
+Northwest Med
+Northwest Ohio Q
+Northwest Rep
+Northwest Univ Law Rev
+Noseleutike
+Noshuyo Byori
+Nosokom Chron
+Not Farm
+Not Man Apart
+Not Polit
+Notas Poblacion
+Notatki Plockie
+Notes Queries
+Notes Rec R Soc Lond
+Notes Undergr
+Notre Dame J Law Ethics Public Policy
+Notre Dame Law Rev
+Notre Dame Lawyer
+Nourrisson
+Nous
+Nouv Com Int Cathol Infirm Assist Med Soc
+Nouv Ec
+Nouv J Chim
+Nouv Presse Med
+Nouv Repub Lett
+Nouv Rev 16e Siecle
+Nouv Rev Deux Mondes
+Nouv Rev Fr Hematol
+Nouv Rev Fr Hematol Blood Cells
+Nov Khir
+Nov Med Priborostr
+Nov Med Tekh
+Nov Novejsaja Istor
+Nova Acta Leopold
+Nova Acta Paracelsica
+Nova Acta Stomatol
+Nova Law J
+Nova Law Rev
+Novartis Found Symp
+Nowe Drogi
+Nowotwory
+Nucl Eng Des/Fusion
+Nucl Instrum Methods Phys Res A
+Nucl Instrum Methods Phys Res B
+Nucl Med (Stuttg)
+Nucl Med Biol
+Nucl Med Commun
+Nucl Med Rev Cent East Eur
+Nucl Phys A
+Nucl Recept
+Nucl Recept Signal
+Nucl Sci Eng
+Nucl Technol
+Nucl Tracks Radiat Meas
+Nucleic Acids Res
+Nucleic Acids Res Spec Publ
+Nucleic Acids Res Suppl
+Nucleic Acids Symp Ser
+Nucleonics
+Nucleosides Nucleotides
+Nucleosides Nucleotides Nucleic Acids
+Nucleus
+Nuestra Hist
+Nueva Enferm
+Nuevos Libros
+Nufusbil Derg
+Nuklearmedizin
+Numen
+Nuncius
+Nunt Radiol
+Nuova Antologia
+Nuova Riv Stor
+Nuova Vet
+Nuovi Ann Ig Microbiol
+Nuovo Cimento C
+Nurs Adm Q
+Nurs BC
+Nurs Care
+Nurs Careers
+Nurs Case Manag
+Nurs Clin North Am
+Nurs Crit Care
+Nurs Diagn
+Nurs Dig
+Nurs Dyn
+Nurs Econ
+Nurs Educ Microworld
+Nurs Educ Monogr
+Nurs Educ Perspect
+Nurs Elder
+Nurs Ethics
+Nurs Focus
+Nurs Forum
+Nurs Forum (Auckl)
+Nurs Health Care
+Nurs Health Care Perspect
+Nurs Health Sci
+Nurs Hist Rev
+Nurs Homes
+Nurs Homes Sr Citiz Care
+Nurs Inq
+Nurs J (Manila)
+Nurs J India
+Nurs J Singapore
+Nurs Law Ethics
+Nurs Law Regan Rep
+Nurs Leadersh
+Nurs Leadersh Forum
+Nurs Life
+Nurs Manag (Harrow)
+Nurs Manage
+Nurs Mirror
+Nurs Mirror Midwives J
+Nurs Montreal
+Nurs N Z
+Nurs News
+Nurs News (Meriden)
+Nurs Older People
+Nurs Outlook
+Nurs Pap
+Nurs Philos
+Nurs Pract
+Nurs Prax N Z
+Nurs Qual Connect
+Nurs Que
+Nurs RSA
+Nurs Res
+Nurs Res Conf
+Nurs Res Rep
+Nurs Rev
+Nurs Sci
+Nurs Sci Q
+Nurs Spectr (Fla Ed)
+Nurs Spectr (Gt Chic Ne Ill Nw Indiana Ed)
+Nurs Spectr (Gt Phila Tri State Ed)
+Nurs Spectr (N Engl Ed)
+Nurs Spectr (Wash D C)
+Nurs Staff Dev Insid
+Nurs Stand
+Nurs Stand Spec Suppl
+Nurs Success Today
+Nurs Times
+Nurs Times Nurs Homes
+Nurs World
+Nurse Anesth
+Nurse Author Ed
+Nurse Educ
+Nurse Educ Oppor Innov
+Nurse Educ Today
+Nurse Managers Bookshelf
+Nurse Pract
+Nurse Pract Forum
+Nurse Res
+Nurses Lamp
+Nursing
+Nursing (Brux)
+Nursing (Lond)
+Nursingconnections
+Nutr Abstr Rev
+Nutr Abstr Rev Ser Hum Exp
+Nutr Cancer
+Nutr Clin Care
+Nutr Clin Pract
+Nutr Dieta Eur Rev Nutr Diet
+Nutr Health
+Nutr Healthy Living
+Nutr Hist Notes
+Nutr Hosp
+Nutr J
+Nutr Life
+Nutr Metab
+Nutr Metab (Lond)
+Nutr Metab Cardiovasc Dis
+Nutr Neurosci
+Nutr Rep Int
+Nutr Res
+Nutr Rev
+Nutr Today
+Nutr Update
+Nutrition
+OCCUP Outlook Q
+OECD Obs
+OH
+OHMS Dig
+OMICS
+ONA J
+ONS News
+OR Manager
+OR Tech
+ORGYN
+ORINS Rep US At Energy Comm
+ORL Head Neck Nurs
+ORL J Otorhinolaryngol Relat Spec
+ORNL
+ORNL-NSIC Rep
+ORNL-TM Rep
+ORO Rep
+Ob Gyn News
+Ob/Gyn World
+Obes Res
+Obes Rev
+Obes Surg
+Obesity (Silver Spring)
+Objets Mondes
+Obs Diagn Econ
+Obstet Ginecol (Bucur)
+Obstet Ginecol Lat Am
+Obstet Gynecol
+Obstet Gynecol Annu
+Obstet Gynecol Clin North Am
+Obstet Gynecol Surv
+Obstet Prat
+Occas Newsl Lindsay Club
+Occas Pap Med Hist Aust
+Occas Pap R Coll Gen Pract
+Occup Environ Med
+Occup Health (Auckl)
+Occup Health (Lond)
+Occup Health Nurs
+Occup Health Nurse (Auckl)
+Occup Health Rev
+Occup Health Saf
+Occup Med
+Occup Med (Lond)
+Occup Ther Int
+Occup Ther Rehabil
+Oceania
+Ochanomizu Igaku Zasshi
+Ochsner Clin Rep
+Ocul Immunol Inflamm
+Ocul Surf
+Odonatologica
+Odont
+Odontes
+Odontiatriki
+Odontoestomatologia
+Odontoiatr Prat
+Odontoiatr Rev Iberoam Med Boca
+Odontol Am
+Odontol Atual
+Odontol Bull
+Odontol Capixaba
+Odontol Chil
+Odontol Clin
+Odontol Conserv
+Odontol Din
+Odontol Foren Tidskr
+Odontol Jalisc
+Odontol Peru
+Odontol Postgrado
+Odontol Pract
+Odontol Revy
+Odontol Revy Suppl
+Odontol Tidskr
+Odontol Urug
+Odontol [Mexico]
+Odontologia
+Odontologia (Lima)
+Odontologica
+Odontologie
+Odontologo
+Odontology
+Odontoprotesi
+Odontostomatol Implantoprotesi
+Odontostomatol Proodos
+Odontostomatol Trop
+Oecologia
+Oecon Pol
+Off J Can Assoc Crit Care Nurs
+Off Our Backs
+Off Publ Dent Hyg Assoc State N Y
+Offentl Gesundheitsdienst
+Offentl Gesundheitswes
+Office
+Oftalmol Zh
+Oftalmologia
+Ohio Dent J
+Ohio Hist
+Ohio J Sci
+Ohio Med
+Ohio North Univ Law Rev
+Ohio Nurses Rev
+Ohio State Law J
+Ohio State Med J
+Oil Paint Drug Report
+Okajimas Folia Anat Jpn
+Okla Nurse
+Okla Statut Annot Okla
+Oklahoma City Univ Law Rev
+Oklahoma Law Rev
+Old Engl Newsl
+Old Northwest
+Oldenburg Familienkd
+Oligonucleotides
+Omaly Anio
+Omega (Westport)
+Omni
+Omnia Med
+Omnia Med Suppl
+Omnia Med Ther
+Omnia Ther
+Omnia Ther Suppl
+Omvardaren
+Oncodev Biol Med
+Oncogene
+Oncogene Res
+Oncol Nurs Forum
+Oncol Rep
+Oncol Res
+Oncologia
+Oncologica
+Oncologist
+Oncology
+Oncology (Williston Park)
+Onderstepoort J Vet Res
+Ondontol Bonaer
+One One
+Onkologie
+Online J Curr Clin Trials
+Online J Issues Nurs
+Online J Knowl Synth Nurs
+Ons Amst
+Ons Heem
+Onsen Kagaku
+Ont Dent
+Ont Hist
+Ont Med Rev
+Ont Psychol
+Ontogenez
+Oostvlaam Zanten
+Oper Dent
+Oper Dent Suppl
+Oper Orthop Traumatol
+Oper Res
+Oper Res Q
+Oper Tech Sports Med
+Ophtalmologie
+Ophthal Plast Reconstr Surg
+Ophthalmic Epidemiol
+Ophthalmic Genet
+Ophthalmic Lit
+Ophthalmic Paediatr Genet
+Ophthalmic Physiol Opt
+Ophthalmic Res
+Ophthalmic Semin
+Ophthalmic Surg
+Ophthalmic Surg Lasers
+Ophthalmic Surg Lasers Imaging
+Ophthalmol Clin North Am
+Ophthalmologe
+Ophthalmologica
+Ophthalmology
+Opin Atty Gen State Md Md Atty Gen Off
+Opt Acta (Lond)
+Opt Commun
+Opt Express
+Opt J Rev Optom
+Opt Lett
+Optician
+Options Policy Pract
+Optom Clin
+Optom Vis Sci
+Optom Wkly
+Optometry
+Opusc Med
+Opusculum
+Or Revis Statut Or
+Oral Dis
+Oral Health
+Oral Health Prev Dent
+Oral Hist Rev
+Oral Hyg
+Oral Implantol
+Oral Microbiol Immunol
+Oral Oncol
+Oral Sci Rev
+Oral Surg Oral Diagn
+Oral Surg Oral Med Oral Pathol
+Oral Surg Oral Med Oral Pathol Oral Radiol Endod
+Orale Implantol
+Oralprophylaxe
+Orange Cty Dent Soc Bull
+Orbis
+Orbit
+Orchester
+Ordre Natl Chir Dent Cons Natl Bull Off
+Oreg Hist Q
+Oreg Nurse
+Oreg State Dent J
+Oregon Law Rev
+Org Biomol Chem
+Org Geochem
+Org Lett
+Org Prep Proced Int
+Organ
+Organ Behav Hum Decis Process
+Organ Behav Hum Perform
+Organ Dyn
+Organometallics
+Organon
+Oriens Extremus
+Oriente Crist
+Oriente Mod
+Orig Life
+Orig Life Evol Biosph
+Origins
+Orizz Ortop Odie Riabil
+Ornicar
+Orthod Craniofac Res
+Orthod Epitheorese
+Orthod Fr
+Orthod Rev
+Orthodontist
+Orthop Clin North Am
+Orthop Nurs
+Orthop Rev
+Orthop Surg
+Orthopade
+Orthopedics
+Ortodoncia
+Ortodontia
+Ortop Maxilar
+Ortop Travmatol Protez
+Orv Hetil
+Orvoskepzes
+Orvosok Lapja Nepeu
+Orvostort Kozl
+Osaka City Med J
+Osaka Daigaku Shigaku Zasshi
+Osaka Univ Law Rev
+Osgoode Hall Law J
+Osiris
+Osler Libr Newsl
+Osp Ital Chir
+Osp Maggiore
+Osp Psichiatr
+Osteoarthritis Cartilage
+Osteopath Hosp
+Osteopath Hosp Leadersh
+Osteopath Prof
+Osteoporos Int
+Osterr Apoth Ztg
+Osterr Arzteztg
+Osterr Dent Z
+Osterr Hebammenztg
+Osterr Krankenpflegez
+Osterr Schwesternztg
+Osterr Z Erforsch Bekampf Krebskr
+Osterr Z Onkol
+Osterr Z Stat Inform
+Osterr Z Stomatol
+Osterr Zahnarzteztg
+Osterr Zahnprothet
+Osterr Zahntech Handw
+Osterr Zahntech Handwerk
+Osterreichische Pflegezeitschrift
+Osteuropa
+Ostomy Wound Manage
+Otago Law Rev
+Otol Neurotol
+Otolaryngol Clin North Am
+Otolaryngol Head Neck Surg
+Otolaryngol Pol
+Otolaryngology
+Otorinolaringol Ital
+Otorinolaringologie
+Ottawa Law Rev
+Ou Daigaku Shigakushi
+Oudheidkd Meded Rijksmus Oudheiden Leiden
+Ouest Med
+Our Planet
+Outcomes Manag
+Outcomes Manag Nurs Pract
+Outlook Agric
+Outlook Bull South Dent Soc N J
+Outlook Mag
+Overijssel
+Overseas Postgrad Med J
+Oxf Bull Econ Stat
+Oxf Econ Pap
+Oxf Ger Stud
+Oxf J Leg Stud
+Oxf Med Sch Gaz
+Oxf Rev Reprod Biol
+Oxf Slav Pap
+Oxf Surv Eukaryot Genes
+P N G Med J
+P R Enferm
+P R Health Sci J
+P S Q
+PA Bar Assoc Q
+PA J
+PAN
+PAS Rep
+PCR Methods Appl
+PCR Rep
+PDA J Pharm Sci Technol
+PDM
+PFCA Rev
+PGRSA Q
+PHC4 FYI
+PI Perspect
+PIACT Pap
+PIACT Prod News
+PIDSA Abstr
+PLoS Biol
+PLoS Comput Biol
+PLoS Genet
+PLoS Med
+PLoS Pathog
+POPCEN Newsl
+POPIN Bull
+PP News
+PR J Public Health Trop Med
+PRB Rep
+PRN Forum
+PS (Wash DC)
+PSD Commun Newsl
+Pa Archaeol
+Pa Dent J (Harrisb)
+Pa Dist Cty Rep
+Pa Folklife
+Pa Health You
+Pa Herit
+Pa Hist
+Pa Mag Hist Biogr
+Pa Med
+Pa Med J
+Pa Mennon Herit
+Pa Nurse
+Pac AIDS Alert Bull
+Pac Aff
+Pac Health Dialog
+Pac Hist
+Pac Hist Rev
+Pac Law J
+Pac Med Surg
+Pac Northwest
+Pac Northwest Q
+Pac Rep Second Ser
+Pac Sociol Rev
+Pac Stud
+Pac Symp Biocomput
+Pac Viewp
+Pace Law Rev
+Pacifica
+Pacing Clin Electrophysiol
+Padiatr Grenzgeb
+Padiatr Padol
+Padiatr Padol Suppl
+Paedagog Hist
+Paediatr Anaesth
+Paediatr Danub
+Paediatr Drugs
+Paediatr Indones
+Paediatr Jpn
+Paediatr Nurs
+Paediatr Perinat Epidemiol
+Paediatr Respir Rev
+Paediatr Univ Tokyo
+Paediatrician
+Paedovita
+Pag Bull
+Pages Ohio Revis Code Annot Ohio
+Pagine Stor Med [Collana]
+Pahlavi Med J
+Pain
+Pain Headache
+Pain Manag Nurs
+Pain Med
+Pain Res Manag
+Pain Suppl
+Pak Dent Rev
+Pak Dev Rev
+Pak Econ Soc Rev
+Pak J Fam Plann
+Pak J Health
+Pak J Med Res
+Pak J Pharm Sci
+Pak J Psychol
+Pak J Sci
+Pak J Sci Ind Res
+Pak J Surg Gynaecol Obstet
+Pak J Zool
+Pak Nurs Health Rev
+Pak Pediatr J
+Pak Popul Rev
+Palacio
+Palaeogeogr Palaeoclimatol Palaeoecol
+Palaeontology
+Palaios
+Paleobiology
+Paleoceanography
+Paleopathol Newsl
+Palest Explor Q
+Palimpsest (Iowa City)
+Palliat Med
+Palliat Support Care
+Pammatone
+Pan Afr J
+Pan Am Health
+Pan Am Med Womans J
+Pancreas
+Pancreatology
+Panhand-Plains Hist Rev
+Panminerva Med
+Panoscope
+Panstwo Prawo
+Pap Bibliogr Soc Am
+Pap Bibliogr Soc Can
+Pap Br Sch Rome
+Pap Far East Hist
+Pap Fr Seventeenth Century Lit
+Pap Natl Conf Prof Nurses Physicians
+Pap Reg Sci
+Pap Reg Sci Assoc
+Pap Ser United Hosp Fund N Y
+Pap Slov Stud
+Papeles Poblac
+Paracelsus Arch Prakt Med
+Parade
+Paradentologie
+Paramed Int
+Parameters
+Paraplegia
+Parasite
+Parasite Immunol
+Parasitol Int
+Parasitol Res
+Parasitol Today
+Parasitology
+Parassitologia
+Parazitologiia
+Parents Mag
+Parents Mag (NY)
+Paris Med
+Parivar Ayojan
+Parking
+Parkinsonism Relat Disord
+Parliam Aff
+Parma Med
+Parodontol
+Parodontol Acad Rev
+Parodontol Stomatol (Nuova)
+Parodontologie
+Parodontopathies
+Paroi Arterielle
+Parola Passato
+Part A News
+Part B News
+Part Fibre Toxicol
+Part J
+Partis Rev
+Parttort Kozl
+Passato Presente
+Passau Schr Psychologiegesch
+Past Present
+Pastoral Psychol
+Pathobiol Annu
+Pathobiology
+Pathol Annu
+Pathol Biol
+Pathol Biol (Paris)
+Pathol Eur
+Pathol Gen
+Pathol Immunopathol Res
+Pathol Int
+Pathol Microbiol (Basel)
+Pathol Oncol Res
+Pathol Res Pract
+Pathol Vet
+Pathologe
+Pathologica
+Pathologist
+Pathology
+Pathology (Phila)
+Pathophysiol Haemost Thromb
+Pathophysiology
+Pathways
+Patient Acc
+Patient Care
+Patient Care Manag
+Patient Couns Health Educ
+Patient Educ Couns
+Patient Educ Newsl
+Patient Focus Care
+Patient Focus Care Satisf
+Patma Banasirakan Handes
+Patna J Med
+Patol Clin Ostet Ginecol
+Patol Fiziol Eksp Ter
+Patol Pol
+Patologia (Mex)
+Pattern Recognit
+Patterns Prejudice
+Pavlov J Biol Sci
+Pavlov J High Nerv Act
+Pawlow Z Hohere Nerventatigkeit
+Peasant Stud
+Pediatr AIDS HIV Infect
+Pediatr Adolesc Gynecol
+Pediatr Akus Ginekol
+Pediatr Alert
+Pediatr Allergy Immunol
+Pediatr Am
+Pediatr Ann
+Pediatr Blood Cancer
+Pediatr Cardiol
+Pediatr Case Rev
+Pediatr Cerrahi Derg
+Pediatr Clin India
+Pediatr Clin North Am
+Pediatr Crit Care Med
+Pediatr Dent
+Pediatr Dermatol
+Pediatr Dev Pathol
+Pediatr Diabetes
+Pediatr Emerg Care
+Pediatr Endocrinol Rev
+Pediatr Hematol Oncol
+Pediatr Infect Dis
+Pediatr Infect Dis J
+Pediatr Int
+Pediatr Listy
+Pediatr Med Chir
+Pediatr Nephrol
+Pediatr Neurol
+Pediatr Neurosci
+Pediatr Neurosurg
+Pediatr News
+Pediatr Nurs
+Pediatr Panam
+Pediatr Pathol
+Pediatr Pathol Lab Med
+Pediatr Pathol Mol Med
+Pediatr Pharmacol (New York)
+Pediatr Phys Ther
+Pediatr Pol
+Pediatr Prat
+Pediatr Pulmonol
+Pediatr Pulmonol Suppl
+Pediatr Radiol
+Pediatr Rehabil
+Pediatr Res
+Pediatr Rev
+Pediatr Surg Int
+Pediatr Transplant
+Pediatria (Bucur)
+Pediatria (Napoli)
+Pediatria (Rio)
+Pediatria (Santiago)
+Pediatrician
+Pediatrics
+Pediatrie
+Pediatrie (Bucur)
+Pediatriia
+Pedod Fr
+Pelican News
+Penal Code Annot State Calif Calif
+Penelope
+Penn Dent J (Phila)
+Penn Med
+Pensee
+Pensee Hommes
+Pensiero Polit
+People
+People (Chicago)
+People Count
+People Place
+People Planet
+Pepperdine Law Rev
+Pept Res
+Peptide Protein Rev
+Peptides
+Percept Mot Skills
+Percept Psychophys
+Perception
+Perf Latinoam
+Perform Improv Advis
+Perfusion
+Perinat Care
+Perinatol Reprod Hum
+Period Biol
+Period Counc Am Mil Past
+Periodontal Abstr
+Periodontal Case Rep
+Periodontal Clin Investig
+Periodontics
+Periodontol 2000
+Perioper Nurs Q
+Perit Dial Int
+Perkins J
+Perm Found Med Bull
+Permian Hist Annu
+Pers Adm
+Pers Derecho
+Pers Guid J
+Pers Individ Dif
+Pers J
+Pers Relatsh
+Pers Soc Psychol Bull
+Pers Soc Psychol Rev
+Personalist
+Personnel
+Persoonia
+Perspect Accredit
+Perspect Addict Nurs
+Perspect Am Hist
+Perspect Biol Med
+Perspect Dev Neurobiol
+Perspect Health
+Perspect Healthc Risk Manage
+Perspect Infirm
+Perspect Int Planif Fam
+Perspect Internation Plan Fam
+Perspect Med Virol
+Perspect Medicaid Manage
+Perspect Medicaid Medicare Manage
+Perspect Mex Am Stud
+Perspect Nephrol Hypertens
+Perspect Pediatr Pathol
+Perspect Psychiatr Care
+Perspect Respir Nurs
+Perspect Sci Christ Faith
+Perspect Sex Reprod Health
+Perspect Soc Work
+Perspect Vasc Surg Endovasc Ther
+Perspectivas
+Perspectives
+Perspectives (Montclair)
+Perspectives Prof
+Pesqui Odontol Bras
+Pesqui Planej Econ
+Pest Manag Sci
+Pestic Biochem Physiol
+Pestic Monit J
+Petermanns Geogr Mitt
+Pflege
+Pflege Aktuell
+Pflege Z
+Pflugers Arch
+Pflugers Arch Gesamte Physiol Menschen Tiere
+Pharm Acta Helv
+Pharm Beih
+Pharm Biol
+Pharm Biotechnol
+Pharm Bull
+Pharm Dev Technol
+Pharm Fr
+Pharm Hist
+Pharm Hist (Lond)
+Pharm Hist Aust
+Pharm Ind
+Pharm Manage Comb Am J Pharm
+Pharm Pract Manag Q
+Pharm Prax
+Pharm Res
+Pharm Rundsch
+Pharm Times
+Pharm Unserer Zeit
+Pharm Update
+Pharm Weekbl
+Pharm Weekbl Sci
+Pharm World Sci
+Pharm Zentralhalle Dtschl
+Pharmacoeconomics
+Pharmacoepidemiol Drug Saf
+Pharmacogenet Genomics
+Pharmacogenetics
+Pharmacogenomics
+Pharmacogenomics J
+Pharmacol Biochem Behav
+Pharmacol Physicians
+Pharmacol Rep
+Pharmacol Res
+Pharmacol Res Commun
+Pharmacol Rev
+Pharmacol Ther
+Pharmacol Ther Dent
+Pharmacol Ther [B]
+Pharmacol Toxicol
+Pharmacology
+Pharmacopsychiatria
+Pharmacopsychiatry
+Pharmacotherapy
+Pharmakopsychiatr Neuropsychopharmakol
+Pharmakotherapia
+Pharmatherapeutica
+Pharmazie
+Pharmaziehist Bibliogr
+Pharmaziehist Forsch
+Pharmeuropa Bio
+Pharmeuropa Spec Issue Biol
+Pharmindex
+Pharos Alpha Omega Alpha Honor Med Soc
+Phi Delta Kappan
+Phi Kappa Phi J
+Phila Med
+Philipp Dev
+Philipp Econ J
+Philipp Geogr J
+Philipp J Cancer
+Philipp J Cardiol
+Philipp J Intern Med
+Philipp J Ment Health
+Philipp J Nurs
+Philipp J Nutr
+Philipp J Obstet Gynecol
+Philipp J Ophthalmol
+Philipp J Ophthalmol Otolaryngol
+Philipp J Pediatr
+Philipp J Public Adm
+Philipp J Surg
+Philipp J Surg Spec
+Philipp J Surg Surg Spec
+Philipp Law J
+Philipp Popul J
+Philipp Popul Newsl
+Philipp Q Cult Soc
+Philipp Rev Econ Bus
+Philipp Soc Sci Humanit Rev
+Philipp Sociol Rev
+Philipp Stud
+Phillip J
+Phillip J Restaur Zahnmed
+Philobiblon
+Philologus
+Philos Context
+Philos East West
+Philos Exch
+Philos Forum
+Philos J
+Philos Nat
+Philos Phenomenol Res
+Philos Public Aff
+Philos Public Policy Q
+Philos Q
+Philos Res Analysis
+Philos Res Arch
+Philos Rev
+Philos Sci
+Philos Soc Sci
+Philos Stud
+Philos Theol
+Philos Trans Phys Sci Eng
+Philos Trans R Soc Lond A
+Philos Trans R Soc Lond B Biol Sci
+Philos Transact A Math Phys Eng Sci
+Philosophia (Mendoza)
+Philosophy
+Phlebologie
+Phlebolymphology
+Phonetica
+Phosphorus Sulfur Silicon Relat Elem
+Photochem Photobiol
+Photochem Photobiol Sci
+Photodermatol
+Photodermatol Photoimmunol Photomed
+Photomed Laser Surg
+Photonics Spectra
+Photophysiology
+Photosynth Res
+Photosynthetica
+Phronesis
+Phycologia
+Phylon
+Phys Biol
+Phys Chem Chem Phys
+Phys East
+Phys Lett A
+Phys Lett B
+Phys Med
+Phys Med Biol
+Phys Med Rehabil Clin N Am
+Phys Occup Ther Geriatr
+Phys Occup Ther Pediatr
+Phys Rep
+Phys Rev A
+Phys Rev A Gen Phys
+Phys Rev B Condens Matter
+Phys Rev C Nucl Phys
+Phys Rev D Part Fields
+Phys Rev E Stat Nonlin Soft Matter Phys
+Phys Rev E Stat Phys Plasmas Fluids Relat Interdiscip Topics
+Phys Rev Lett
+Phys Sportsmed
+Phys Ther
+Phys Ther Rev
+Phys Today
+Physica A
+Physica D
+Physician Assist
+Physician Assist Health Pract
+Physician Exec
+Physician Perform Paym Rep
+Physician Relat Update
+Physicians Manage
+Physicians World
+Physiol Behav
+Physiol Biochem Zool
+Physiol Bohemoslov
+Physiol Chem Phys
+Physiol Chem Phys Med NMR
+Physiol Comp Ocol Int J Comp Physiol Ecol
+Physiol Entomol
+Physiol Genomics
+Physiol Meas
+Physiol Pharmacol Physicians
+Physiol Plant
+Physiol Res
+Physiol Rev
+Physiol Rev Suppl
+Physiol Teach
+Physiol Veg
+Physiol Zool
+Physiologie
+Physiologist
+Physiology (Bethesda)
+Physiother Can
+Physiother Res Int
+Physiother Theory Pract
+Physiotherapy
+Physis
+Physis Riv Int Stor Sci
+Phytochem Anal
+Phytochemistry
+Phytomedicine
+Phyton
+Phyton (Buenos Aires)
+Phytopathology
+Phytother Res
+Picturescope
+Pie
+Pieleg Polozna
+Pigment Cell Res
+Pinheiros Ter
+Pioneer Am Soc Trans
+Pisani
+Pituitary
+Placenta
+Placenta Suppl
+Plains Anthropol
+Plan Arb
+Plan Parent Chall
+Plan Parent Eur
+Plan Parent Rev
+Plan Parent World Popul Wash Memo
+Planej Agora
+Planet Rep
+Planet Space Sci
+Planned Parent (India)
+Plant Biol (Stuttg)
+Plant Breed Rev
+Plant Cell
+Plant Cell Environ
+Plant Cell Physiol
+Plant Cell Rep
+Plant Cell Tissue Organ Cult
+Plant Foods Hum Nutr
+Plant Genet Resour Newsl
+Plant Growth Regul
+Plant J
+Plant Methods
+Plant Mol Biol
+Plant Mol Biol Rep
+Plant Physiol
+Plant Physiol Biochem
+Plant Prod Sci
+Plant Sci
+Plant Soil
+Planta
+Planta Med
+Plasmid
+Plast Reconstr Surg
+Plast Surg Nurs
+Platelets
+Platon (Athens)
+Plucne Bolesti
+Plucne Bolesti Tuberk
+Plugger
+Plural Soc
+Plzen Lek Sb
+Pneumoftiziologia
+Pneumologia
+Pneumologie
+Pneumonol Alergol Pol
+Pneumonol Phymatiologike Epitheor
+Pneumonol Pol
+Pneumonologie
+Poblac Desarro
+Poblacion
+Pogon Sahoe Yongu
+Pol Am Stud
+Pol Arch Med Wewn
+Pol Arch Weter
+Pol Ecol Stud
+Pol J Chem
+Pol J Microbiol
+Pol J Occup Med
+Pol J Occup Med Environ Health
+Pol J Pathol
+Pol J Pharmacol
+Pol J Pharmacol Pharm
+Pol J Vet Sci
+Pol Med J
+Pol Med Sci Hist Bull
+Pol Merkuriusz Lek
+Pol Perspect
+Pol Popul Rev
+Pol Przegl Chir
+Pol Przegl Radiol
+Pol Przegl Radiol Med Nukl
+Pol Rev
+Pol Tyg Lek
+Pol Tyg Lek (Wars)
+Pol West Aff
+Polar Biol
+Polar Rec (Gr Brit)
+Polar Res
+Polarforschung
+Policlinico [Chir]
+Policlinico [Med]
+Policlinico [Prat]
+Policy Anal
+Policy Anal Brief H Ser
+Policy Anal Brief W Ser
+Policy Brief Commonw Fund
+Policy Brief George Wash Univ Cent Health Serv Res Policy
+Policy Brief UCLA Cent Health Policy Res
+Policy Polit
+Policy Polit Nurs Pract
+Policy Rev
+Policy Sci
+Policy Statement R Coll Gen Pract
+Policy Stud J
+Policy Stud Rev
+Polim Med
+Polio news
+Polis
+Polit Aff
+Polit Afr
+Polit Ekon
+Polit Etrang
+Polit Geogr
+Polit Int
+Polit Mein
+Polit Q
+Polit Res Q
+Polit Sci
+Polit Sci Q
+Polit Soc
+Polit Soc (Madrid)
+Polit Stud
+Polit Theory
+Polit Today
+Polit Vierteljahresschr
+Polit Zeitgesch
+Politico (Pavia)
+Politics (Syd)
+Politics Life Sciences
+Politiq Popul
+Polity
+Polymer Prepr
+Pommern
+Ponte
+Pop Gov
+Pop Sahel
+Pope Speaks
+Popline
+Popnet
+Popul
+Popul Avenir
+Popul Briefs
+Popul Bull
+Popul Bull ECWA
+Popul Bull ESCWA
+Popul Bull U N Econ Comm West Asia
+Popul Bull UN
+Popul Chron
+Popul Commun Tech Doc
+Popul Concern News
+Popul Data Inf Serv
+Popul Desenvolv
+Popul Dev Rev
+Popul Dyn Q
+Popul Educ Asia Ocean Newsl
+Popul Educ Asia Pac Newsl Forum
+Popul Educ Interchange
+Popul Environ
+Popul Environ Psychol Newsl
+Popul Famille
+Popul Forum
+Popul Geogr
+Popul Headl
+Popul Health Metr
+Popul Index
+Popul Manag
+Popul Newsl
+Popul Notes
+Popul Policy Compend
+Popul Rep
+Popul Rep A
+Popul Rep B
+Popul Rep C
+Popul Rep D
+Popul Rep E
+Popul Rep F
+Popul Rep G
+Popul Rep H
+Popul Rep I
+Popul Rep J
+Popul Rep K
+Popul Rep L
+Popul Rep M
+Popul Rep Spec Top Monogr
+Popul Res
+Popul Res Abstr
+Popul Res Bull
+Popul Res Leads
+Popul Res Policy Rev
+Popul Rev
+Popul Sci
+Popul Soc (Paris)
+Popul Stud (Camb)
+Popul Stud (NY)
+Popul Today
+Popul Trends
+Popul Trends Public Policy
+Popul Zpr
+Populasi
+Population (NY)
+Population (Paris)
+Port Med
+Portland Clin Bull
+Posit Aware
+Posit Dir News
+Posit Health News
+Posit Living
+Posit Outlook
+Post Hist
+Post Sov Geogr
+Post Sov Geogr Econ
+Postep Chir
+Postep Neurol Neurochir Psychiatr
+Postep Reumatol
+Postep Wiedzy Med
+Postepy Biochem
+Postepy Hig Med Dosw
+Postepy Hig Med Dosw (Online)
+Postgrad Dent
+Postgrad Med
+Postgrad Med J
+Postgrad Semin Am Urol Assoc North Cent
+Postharvest Biol Technol
+Poult Sci
+Poumon
+Poumon Coeur
+Pr Dejin Prir Ved
+Pr Lodz Tow Nauk [IV]
+Prac Lek
+Prac Studi Geograf
+Pract Dent Monogr
+Pract Dig
+Pract Farm
+Pract Gastroenterol
+Pract Lawyer
+Pract Midwife
+Pract Odontol
+Pract Otorhinolaryngol (Basel)
+Pract Periodontics Aesthet Dent
+Pract Proced Aesthet Dent
+Pract Psychol Physicians
+Practitioner
+Prague Med Rep
+Prairie Rose
+Prakt Anaesth
+Prakt Arzt
+Prakt Kieferorthop
+Prakt Tierarzt
+Prakt Zubn Lek
+Prax Kinderpsychol Kinderpsychiatr
+Prax Kinderpsychol Kinderpsychiatr Beih
+Prax Klin Pneumol
+Prax Kur
+Prax Pneumol
+Prax Psychother
+Prax Psychother Psychosom
+Praxis
+Precambrian Res
+Precis Anal Trav
+Preconf Papers Natl Dent Health Conf (U S)
+Pregon
+Prehosp Emerg Care
+Prehospital Disaster Med
+Prenat Diagn
+Prenat Neonatal Med
+Prensa Med Argent
+Prensa Med Mex
+Prensa Pediatr Rev Am Puericu Pediatr
+Prep Biochem
+Prep Biochem Biotechnol
+Pres Stud Q
+Presbyt St Lukes Hosp Med Bull
+Prescr J
+Prescrire Int
+Presence (Camb)
+Presence Afr
+Presence Normande
+Present Tense
+Presse Med
+Presse Therm Clim
+Prev Assist Dent
+Prev Cardiol
+Prev Chronic Dis
+Prev Hum Serv
+Prev Med
+Prev Sci
+Prev Stomatol
+Prev Vet Med
+Prevent
+Previdenza Soc
+Pride Inst J Long Term Home Health Care
+Priest
+Prikl Biokhim Mikrobiol
+Prilozi
+Prim Care
+Prim Care Companion J Clin Psychiatry
+Prim Dent Care
+Prim Sens Neuron
+Primary Cardiol
+Primates
+Primates Med
+Princ Viana
+Princess Takamatsu Symp
+Princet Hist
+Princet J Bioeth
+Princeton Univ Libr Chron
+Print Q
+Prir Clovek Zdravje
+Prism
+Priv Pact
+Pro Fam Inf
+Pro Fono
+Pro Infirm
+Pro Re Nata
+Probate Law J
+Probate Prop
+Probe
+Probe (Adelaide)
+Probe (Lond)
+Probl Actuels Endocrinol Nutr
+Probl Actuels Otorhinolaryngol
+Probl Am Lat
+Probl Cardiol
+Probl Desarro
+Probl Econ
+Probl Econ Transit
+Probl Endokrinol (Mosk)
+Probl Endokrinol Gormonoter
+Probl Gematol Pereliv Krovi
+Probl Geogr
+Probl Hematol Blood Transfus
+Probl Istor Demogr SSSR
+Probl Khig
+Probl Kosm Biol
+Probl Med Wieku Rozwoj
+Probl Oncol
+Probl Onkol
+Probl Rodziny
+Probl Sev
+Probl Sotsialnoi Gig Istor Med
+Probl Sotsialnoi Gig Zdravookhranenniiai Istor Med
+Probl Symp Aktuell Ther
+Probl Ter
+Probl Tuberculoza
+Probl Tuberk
+Probl Tuberk Bolezn Legk
+Probl Vet Med
+Probl Virol
+Proc (Bayl Univ Med Cent)
+Proc AMIA Annu Fall Symp
+Proc AMIA Symp
+Proc Acad Polit Sci
+Proc Am Antiq Soc
+Proc Am Assoc Cancer Res
+Proc Am Cathol Philos Assoc
+Proc Am Philos Soc
+Proc Am Thorac Soc
+Proc Anim Care Panel
+Proc Annu Clin Spinal Cord Inj Conf
+Proc Annu Conf Res Med Educ
+Proc Annu Manage Conf Am Dent Assoc
+Proc Annu Meet Am Assoc Cancer Res
+Proc Annu Meet Am Psychopathol Assoc
+Proc Annu Meet Med Sect Am Counc Life Insur
+Proc Annu Meet Med Sect Am Life Conv
+Proc Annu Meet Med Sect Am Life Insur Assoc
+Proc Annu Meet U S Anim Health Assoc
+Proc Annu Meet West Soc Fr Hist
+Proc Annu Symp Comput Appl Med Care
+Proc Annu Symp Eugen Soc
+Proc Assoc Am Physicians
+Proc Aust Assoc Neurol
+Proc Biol Sci
+Proc Biol Soc Wash
+Proc Bot Soc Br Isles
+Proc Br Paedod Soc
+Proc Br Soc Dent Maxillofac Radiol
+Proc Camb Philol Soc
+Proc Can Cancer Conf
+Proc Cardiff Med Soc
+Proc Chem Soc
+Proc Chin Acad Med Sci Peking Union Med Coll
+Proc Clin Dial Transplant Forum
+Proc Conf Oral Cancer
+Proc Consort Revolut Eur
+Proc Dorset Nat Hist Archeol Soc
+Proc Entomol Soc Wash
+Proc Eur Dial Transplant Assoc
+Proc Eur Dial Transplant Assoc Eur Ren Assoc
+Proc Eur Prosthodontic Assoc
+Proc Finn Dent Soc
+Proc Found Orthod Res
+Proc Geol Assoc
+Proc Health Policy Forum
+Proc Huguenot Soc Lond
+Proc IEEE Comput Soc Bioinform Conf
+Proc IEEE Comput Syst Bioinform Conf
+Proc Indiana Acad Sci
+Proc Inst Mech Eng G J Aerosp Eng
+Proc Inst Mech Eng [H]
+Proc Inst Med Chic
+Proc Int Acad Oral Pathol
+Proc Int Conf Intell Syst Mol Biol
+Proc Int Congr Endocrinol
+Proc Jpn Acad
+Proc K Ned Akad Wet C
+Proc Mine Med Off Assoc
+Proc Mine Med Off Assoc SA
+Proc NIPR Symp Antarct Meteorites
+Proc Natl Acad Sci U S A
+Proc Natl Cancer Conf
+Proc Natl Conf Methadone Treat
+Proc Natl Sci Counc Repub China B
+Proc Nurs Theory Conf
+Proc Nutr Soc
+Proc Nutr Soc India
+Proc Pap Annu Conf Calif Mosq Control Assoc
+Proc Pap Ga Assoc Hist
+Proc R Coll Physicians Edinb
+Proc R Inst G B
+Proc R Ir Acad C Archaeol Celt Stud Hist Linguist Lit
+Proc R Ir Acad [B]
+Proc R Soc Edinb [Biol]
+Proc R Soc Edinb [Nat Environ]
+Proc R Soc Lond B Biol Sci
+Proc R Soc Med
+Proc Rudolf Virchow Med Soc City N Y
+Proc S C Hist Assoc
+Proc Soc Exp Biol Med
+Proc Soc Study Fertil
+Proc Staff Meet Honol Clin
+Proc Staff Meet Pethah Tiqva Isr Beilinson Hosp
+Proc State Secr Manage Conf Am Dent Assoc
+Proc U S Nav Inst
+Proc Univ Otago Med Sch
+Proc Veterans Adm Spinal Cord Inj Conf
+Proc Virchow Pirquet Med Soc
+Proc Wesley Hist Soc
+Proc West Pharmacol Soc
+Proc Wkly Semin Neurol
+Process Biochem
+Prod Pharm
+Prod Probl Pharm
+Prof Care Mother Child
+Prof Dev Ser (Chic Ill)
+Prof Ethics
+Prof Ethics Rep
+Prof Flashes
+Prof Geogr
+Prof Inferm
+Prof Nurs Home
+Prof Nurse
+Prof Psychol
+Prof Psychol Res Pr
+Prof Saf
+Prof Sanit Manage
+Profamilia
+Profile Med Pract
+Profiles Healthc Mark
+Profiles Hosp Mark
+Prog AIDS Pathol
+Prog Allergy
+Prog Arch
+Prog At Med
+Prog Behav Modif
+Prog Biochem Pharmacol
+Prog Biocybern
+Prog Biometeorol
+Prog Biophys Biophys Chem
+Prog Biophys Mol Biol
+Prog Brain Res
+Prog Cardiovasc Dis
+Prog Cardiovasc Nurs
+Prog Cell Cycle Res
+Prog Chem Fats Other Lipids
+Prog Chem Toxicol
+Prog Clin Biol Res
+Prog Clin Cancer
+Prog Clin Immunol
+Prog Clin Parasitol
+Prog Clin Pathol
+Prog Colloid Polym Sci
+Prog Drug Res
+Prog Exp Pers Psychopathol Res
+Prog Exp Pers Res
+Prog Exp Tumor Res
+Prog Food Nutr Sci
+Prog Growth Factor Res
+Prog Gynecol
+Prog Hematol
+Prog Hemost Thromb
+Prog Histochem Cytochem
+Prog Hum Geogr
+Prog Hum Reprod Res
+Prog Immunobiol Stand
+Prog Ind Microbiol
+Prog Lipid Res
+Prog Liver Dis
+Prog Med
+Prog Med Chem
+Prog Med Genet
+Prog Med Virol
+Prog Mol Subcell Biol
+Prog Neurobiol
+Prog Neurol Psychiatry
+Prog Neuropsychopharmacol
+Prog Neuropsychopharmacol Biol Psychiatry
+Prog Nucl Med
+Prog Nucleic Acid Res Mol Biol
+Prog Odontoiatr
+Prog Odontostomatol
+Prog Orthod
+Prog Pediatr Cardiol
+Prog Pediatr Surg
+Prog Phys Ther
+Prog Plann
+Prog Psychobiol Physiol Psychol
+Prog Psychother
+Prog Radiat Ther
+Prog Rep Health Dev South Afr
+Prog Retin Eye Res
+Prog Stereochem
+Prog Surf Sci
+Prog Surg
+Prog Transplant
+Prog Urol
+Prog Vet Microbiol Immunol
+Progr Med
+Progr Med (Napoli)
+Progr Med (Paris)
+Progr Pediatr Puericult
+Progr Ter Clin
+Program Notes Assoc Univ Programs Health Adm
+Program Q Asia Found
+Progress Nurse
+Progressive
+Proj Inf Perspect
+Projet
+Prologue J Natl Arch
+Prometeo (Milan)
+Promot Dent
+Promot Educ
+Promot Health
+Prophyl Antivenerienne
+Prophyl Sanit Morale
+Prose Stud
+Prospects
+Prostaglandins
+Prostaglandins Leukot Essent Fatty Acids
+Prostaglandins Leukot Med
+Prostaglandins Med
+Prostaglandins Other Lipid Mediat
+Prostate
+Prostate Cancer Prostatic Dis
+Prostate Suppl
+Prosthet Orthot Int
+Prot Hum Subj
+Protein Eng
+Protein Eng Des Sel
+Protein Expr Purif
+Protein J
+Protein Pept Lett
+Protein Profile
+Protein Sci
+Protein Seq Data Anal
+Proteins
+Proteome Sci
+Proteomics
+Protes Dent
+Protet Stomatol
+Proteus
+Protist
+Protoplasma
+Prov Med
+Provence Hist
+Provider
+Prz Geogr
+Prz Orient
+Prz Zach
+Przegl Dermatol
+Przegl Epidemiol
+Przegl Hist
+Przegl Lek
+Przegl Pol
+Przeszlosc Demograf Pol
+Psihoterapija
+Psikholog Zh
+Psyche (Stuttg)
+Psyche Rev Int Sci Homme Psychanal
+Psychiatr Ann
+Psychiatr Bull R Coll Psychiatr
+Psychiatr Clin (Basel)
+Psychiatr Clin North Am
+Psychiatr Commun
+Psychiatr Danub
+Psychiatr Dev
+Psychiatr Enfant
+Psychiatr Forum
+Psychiatr Genet
+Psychiatr Hosp
+Psychiatr Hung
+Psychiatr J Univ Ott
+Psychiatr Med
+Psychiatr Med Update Mass Gen Hosp Rev Physicians
+Psychiatr Neurol (Basel)
+Psychiatr Neurol Med Psychol (Leipz)
+Psychiatr Neurol Med Psychol Beih
+Psychiatr Neurol Neurochir
+Psychiatr News
+Psychiatr Opin
+Psychiatr Pol
+Psychiatr Prax
+Psychiatr Q
+Psychiatr Q Suppl
+Psychiatr Rehabil J
+Psychiatr Res Rep Am Psychiatr Assoc
+Psychiatr Serv
+Psychiatry
+Psychiatry Clin Neurosci
+Psychiatry Dig
+Psychiatry Med
+Psychiatry Res
+Psychoanal Psychoanal Rev
+Psychoanal Q
+Psychoanal Rev
+Psychoanal Soc Sci
+Psychoanal Study Child
+Psychodyn Couns
+Psychohist Rev
+Psychol Abstr
+Psychol Addict Behav
+Psychol Aging
+Psychol Assess
+Psychol Belg
+Psychol Bull
+Psychol Dev Soc J
+Psychol Forsch
+Psychol Health
+Psychol Issues
+Psychol Med
+Psychol Med (Paris)
+Psychol Med Monogr Suppl
+Psychol Methods
+Psychol Monogr
+Psychol Neuropsychiatr Vieil
+Psychol Prax
+Psychol Psychother
+Psychol Public Policy Law
+Psychol Rec
+Psychol Rep
+Psychol Res
+Psychol Rev
+Psychol Rundsch
+Psychol Sch
+Psychol Sci
+Psychol Stud (Mysore)
+Psychol Stud Popul Fam Plan
+Psychol Today
+Psychol Women Q
+Psychologist
+Psychology
+Psychometrika
+Psychon Bull Rev
+Psychon Sci
+Psychoneuroendocrinology
+Psychooncology
+Psychopathology
+Psychopharmacol Bull
+Psychopharmacol Commun
+Psychopharmacol Ser
+Psychopharmacol Serv Cent Bull
+Psychopharmacologia
+Psychopharmacologie
+Psychopharmacology (Berl)
+Psychopharmacology Suppl
+Psychophysiology
+Psychosom Med
+Psychosomatics
+Psychother Med Psychol (Stuttg)
+Psychother Psychosom
+Psychother Psychosom Med Psychol
+Psychotherapy
+Pszichol Tanulman
+Publ Am Inst Hist Pharm
+Publ B Aires Cent Investig
+Publ Child Dev Univ Calif
+Publ Cour Eur Droits Homme Sr A Arrets Decis
+Publ Group Adv Psychiatry
+Publ Inst Antitubers Francisco Moragas
+Publ Inst Pasteur Guyane Fr Inini
+Publ Mus Farm
+Publ Psychol
+Publ Public Health Univ Calif
+Publ Soc Hist Archeol Limbg
+Publ Soc Lit Sci
+Publ Soc Savante Alsace Reg Est
+Publ Thoresby Soc
+Public Adm
+Public Adm Dev
+Public Adm Rev
+Public Aff Q
+Public Aff Rep
+Public Choice
+Public Citiz
+Public Finan Q
+Public Financ
+Public Health
+Public Health Monogr
+Public Health Nurs
+Public Health Nutr
+Public Health Pap
+Public Health Rep
+Public Health Rev
+Public Hist
+Public Interest
+Public Law
+Public Law Forum
+Public Opin Q
+Public Pers Manage
+Public Policy
+Public Relat J
+Public Sect Contract Rep
+Public Sector Health Care Risk Manage
+Public Underst Sci
+Public Welf
+Public health tech monogr US
+Publius
+Pula
+Pulaski Cty Hist Rev
+Pulm Pharmacol
+Pulm Pharmacol Ther
+Pulse
+Punjab Med J
+Purch Adm
+Purdons Pa Statut Annot Pa
+Pure Appl Chem
+Purinergic Signal
+Q Bull Northwest Univ Med Sch
+Q Bull S Afr Libr
+Q Bull Sea View Hosp
+Q Demogr Bull
+Q Dent Rev
+Q J Adm
+Q J Crude Drug Res
+Q J Econ
+Q J Exp Physiol
+Q J Exp Physiol Cogn Med Sci
+Q J Exp Psychol
+Q J Exp Psychol (Colchester)
+Q J Exp Psychol A
+Q J Exp Psychol B
+Q J Libr Cong
+Q J Med
+Q J Microsc Sci
+Q J Nucl Med
+Q J Nucl Med Mol Imaging
+Q J Soc Aff
+Q J Speech
+Q J Stud Alcohol
+Q Med Rev
+Q Natl Dent Assoc
+Q Popul Bull
+Q Prog Rep United States Air Force Radiat Lab Univ Chic
+Q Rev
+Q Rev Allergy Appl Immunol
+Q Rev Biol
+Q Rev Biophys
+Q Rev DC Nurses Assoc
+Q Rev Drug Metab Drug Interact
+Q Rev Econ Bus
+Q Rev Econ Finance
+Q Rev Med
+Q Rev Pediatr
+Q Rev Surg
+Q Rev Surg Obstet Gynecol
+QA Brief
+QA Rev
+QJM
+QRB Qual Rev Bull
+QRC Advis
+Qadmoniot
+Qld Nurse
+Qld Nurses J
+Quad Antibiot
+Quad Azione Soc
+Quad Clin Ostet Ginecol
+Quad Criminol Clin
+Quad Int Stor Med Sanita
+Quad Merceol
+Quad Nutr
+Quad Radiol
+Quad Raggruppamento Toscoumbroemiliano Stor Med
+Quad Sclavo Diagn
+Quad Stor
+Quad Stor Med Sci
+Quad Stor Univ Padova
+Quaerendo
+Quaker Hist
+Qual Assur
+Qual Assur Health Care
+Qual Assur Util Rev
+Qual Connect
+Qual Health Care
+Qual Health Res
+Qual Lett Healthc Lead
+Qual Life Cardiovasc Care
+Qual Life Res
+Qual Manag Health Care
+Qual Quant
+Qual Saf Health Care
+Qual Sociol
+Queen's Q
+Queens Gazette
+Queens Law J
+Queens Nurs J
+Quellen Forsch Ital Arch Bibl
+Quellen Stud Gesch Pharm
+Quercy Rech
+Quest
+Quim Nova
+Quinnipiac Health Law J
+Quintessence Dent Technol
+Quintessence Int
+Quintessence Int [Fr]
+Quintessencia
+Quintessencia Protese Lab
+Quintessenz
+Quintessenz Impulse
+Quintessenz J
+Quintessenz Zahntech
+Quintessenza
+Quinto Cent
+Quipu
+R Can Dent Corps Q
+R I Dent J
+R I Hist
+R I Med
+R I Med J
+R Inst Public Health Hyg J
+R S Rep
+R Sitios
+R Soc Health J
+RANF Rev
+RCM Midwives
+RCM Midwives J
+RCN Nurs Stand
+RDH
+RF
+RF Illus
+RGO
+RISO Rep
+RN
+RN (For Managers)
+RN Ida
+RNA
+RNABC News
+RNAO News
+RURDS Rev Urb Reg Dev Stud
+Race Cl
+Rad Med Fak Zagrebu
+Radiat Data Rep
+Radiat Eff
+Radiat Environ Biophys
+Radiat Meas
+Radiat Med
+Radiat Oncol Investig
+Radiat Prot Dosimetry
+Radiat Res
+Radiat Res Suppl
+Radiats Biol Radioecol
+Radic Am
+Radic Hist Rev
+Radiobiol Radioter Fis Med
+Radiobiol Radiother (Berl)
+Radiobiologiia
+Radiocarbon
+Radiogr Today
+Radiographics
+Radiography
+Radioisotopes
+Radiol
+Radiol Austriaca
+Radiol Clin
+Radiol Clin (Basel)
+Radiol Clin Biol
+Radiol Clin North Am
+Radiol Diagn (Berl)
+Radiol Health Data Rep
+Radiol Interam
+Radiol Manage
+Radiol Med (Torino)
+Radiol Prat
+Radiol Technol
+Radiologe
+Radiologia
+Radiology
+Radioprotection
+Radioter Radiobiol Fis Medica
+Radiother Oncol
+Rajasthan Med J
+Ramparts
+Rand J Econ
+Rapid Commun Mass Spectrom
+Raspr Grada Povij Znan
+Rass Arch Stato
+Rass Clin Sci
+Rass Clin Ter
+Rass Dermatol Sifilogr
+Rass Fisiopatol Clin Ter
+Rass Giuliana Med
+Rass Int Clin Ter
+Rass Int Stomatol Prat
+Rass Ital Chir Med
+Rass Ital Gastroenterol
+Rass Ital Sociol
+Rass Lett Ital
+Rass Med Ind Ig Lav
+Rass Med Sarda
+Rass Med Sper
+Rass Medica
+Rass Neurol Veg
+Rass Neuropsichiatr
+Rass Odontotec
+Rass Penititenziaria Crim
+Rass Stor Risorgim
+Rass Studi Psichiatr
+Rass Trimest Odontoiatr
+Rasy Nar Sovrem Etn Ras Probl
+Ratio Juris
+Ration Drug Ther
+Raumforsch Raumordn
+Ravintsara
+Rays
+Reach Out
+Read Dig
+Real Clin
+Real Property Probate Trust J
+Reanim Organes Artif
+Reason
+Reason Pap
+Rec Assoc Bar City New York
+Rec Bucks
+Rec Chem Prog
+Rec Columb Hist Soc
+Rec Med Vet Ec Alfort
+Recent Adv Biol Psychiatry
+Recent Adv Clin Nucl Med
+Recent Adv Nurs
+Recent Adv Stud Cardiac Struct Metab
+Recent Dev Alcohol
+Recent Prog Horm Res
+Recent Results Cancer Res
+Recenti Prog Med
+Recept Signal Transduct
+Receptor
+Receptors Channels
+Rech Amerindien Que
+Rech Econ Louvain
+Rech Geogr Strasbg
+Rech Soc
+Rech Sociogr
+Rech Soins Infirm
+Rech fem
+Recher Fed Groupes Etud Recher Inst
+Recherche
+Recomb DNA Tech Bull
+Reconstr Surg Traumatol
+Reconstructionist
+Record (Washington)
+Recruit Retain
+Recruit Retent Rep
+Recruit Retent Restruct Rep
+Red River Val Hist Rev
+Redbook
+Redox Rep
+Ref Gynecol Obstet
+Ref Serv Rev
+Reflect Nurs Leadersh
+Reflections
+Reform J
+Refract Corneal Surg
+Refu Vet
+Refuat Hapeh Vehashinayim
+Refuat Hashinayim
+Refug Rep
+Reg Anaesth
+Reg Anesth
+Reg Anesth Pain Med
+Reg Dev Dialogue
+Reg Immunol
+Reg Sci Perspect
+Reg Sci Urban Econ
+Reg Stud
+Regan Rep Nurs Law
+Regards Actual
+Regensb Jahrb Arztl Fortbild
+Regist Ky Hist Soc
+Regist Nurse
+Regul Aff J
+Regul Anal Med Waste
+Regul Pept
+Regul Pept Suppl
+Regul Toxicol Pharmacol
+Regulation
+Rehab Manag
+Rehabil Couns Bull
+Rehabil Fyz Lek
+Rehabil Lit
+Rehabil Nurs
+Rehabil Psychol
+Rehabil Rec
+Rehabilitation
+Rehabilitation (Bonn)
+Rehabilitation (Stuttg)
+Reihe Med Forsch
+Rein Foie
+Rejuvenation Res
+Relac Int
+Relig Educ
+Relig Humanism
+Relig Life
+Relig Stud Rev
+Rem Actual
+Ren Fail
+Ren Kou Xue Kan
+Ren Physiol
+Ren Physiol Biochem
+Renaiss Mod Stud
+Renaiss Q
+Renaiss Reform
+Rend Accad Naz XL
+Rend Ist Sup Sanit
+Rend Lincei Sci Fis Nat
+Rendezvous (Buffalo)
+Renkou Yanjiu
+Renkou Yu Jingji
+Rep Am Univ Field Staff
+Rep Can Def Res Chem Lab
+Rep Can Def Res Kings Lab
+Rep Carcinog
+Rep Cases Argued Determ Supreme Court N J N J Supreme Court
+Rep Cases Decided Appell Div Supreme Court State NY NY State Supreme Court Appell Div
+Rep Cases Decided Supreme Court State Ga Ga Supreme Court
+Rep Cases Determ Court Appeal State Calif
+Rep Civ Aeromed Res Inst US
+Rep Comm Accredit Rehabil Facil
+Rep Congr Eur Orthod Soc
+Rep Del Nurses Assoc
+Rep Group Adv Psychiatry
+Rep Health Soc Subj (Lond)
+Rep Inst Hum Values
+Rep Inst Philos Public Policy
+Rep Int Dev Res Cent Can
+Rep Med Guidel Outcomes Res
+Rep Natl Forum Hosp Health Aff
+Rep Nav Med Neuropsychiatr Res Unit
+Rep No NAEC ACEL United States Aerosp Crew Equip Lab Phila
+Rep Popul Fam Plann
+Rep Proc Scott Soc Hist Med
+Rep Public Health Med Subj (Lond)
+Rep Rheum Dis
+Rep Sel Cases Decided Courts State N Y Court Appeals Appell Div Supreme Court
+Rep US Army Med Res Lab
+Rep US Army Med Res Nutr Lab Denver
+Rep US Nav Med Res Lab
+Rep US Nav Submar Med Cent
+Rep US Navy Exp Diving Unit
+Report Hum Reprod Law
+Represent Res Soc Psychol
+Representations (Berkeley)
+ReproWatch (Youth Ed)
+Reprod Abstr Ser
+Reprod Biol
+Reprod Biol Endocrinol
+Reprod Biomed Online
+Reprod Domest Anim
+Reprod Fertil Dev
+Reprod Freedom News
+Reprod Genet Eng
+Reprod Health
+Reprod Health Matters
+Reprod Nutr Dev
+Reprod Suppl
+Reprod Toxicol
+Reproduccion
+Reproduction
+Reprowatch
+Repura
+Res Agenda Brief
+Res Aging
+Res Clin Stud Headache
+Res Commun Chem Pathol Pharmacol
+Res Commun Inst Ferment
+Res Commun Mol Pathol Pharmacol
+Res Commun Psychol Psychiatr Behav
+Res Commun Subst Abuse
+Res Community Ment Health
+Res Dev Disabil
+Res Dev Tech Rep
+Res Econ Anthropol
+Res Econ Hist
+Res Exp Med (Berl)
+Res Front Fertil Regul
+Res Health Econ
+Res Hum Cap Dev
+Res Hum Dev
+Res Immunol
+Res Initiat Treat Action
+Res Ipsa Loquitur
+Res Manage
+Res Microbiol
+Res Nurs Health
+Res Pap Hist Med Assoc
+Res Popul Ecol (Kyoto)
+Res Popul Econ
+Res Prog Org Biol Med Chem
+Res Prostaglandins
+Res Publ Assoc Res Nerv Ment Dis
+Res Publica
+Res Publica Litt
+Res Q
+Res Q Exerc Sport
+Res Rep Health Eff Inst
+Res Rep Nav Med Res Inst (US)
+Res Rep U S Nav Med Field Res Lab
+Res Rep U S Nav Sch Aviat Med
+Res Reprod
+Res Resour Rep
+Res Rural Sociol Dev
+Res Serv Med
+Res Soc Work Pract
+Res Sociol Educ Social
+Res Sociol Health Care
+Res Sports Med
+Res Stat Note
+Res Stat Note Health Care Financ Adm Off Policy Plan Res
+Res Steroids (Amst)
+Res Stud
+Res Theory Nurs Pract
+Res Today
+Res Urban Econ
+Res Vet Sci
+Res Virol
+Research
+Reseaux Ciephum
+Resen Clin Cient
+Resena Hist
+Resid Staff Physician
+Residue Rev
+Resour Biomed Res Educ
+Resour Fem Res
+Respir Care
+Respir Care Clin N Am
+Respir Manage
+Respir Med
+Respir Physiol
+Respir Physiol Neurobiol
+Respir Res
+Respir Ther
+Respiration
+Respirology
+Responsa Meridiana
+Responsive Community
+Restaurants Inst
+Restor Neurol Neurosci
+Restor Q
+Restorative Dent
+Results Probl Cell Differ
+Resume
+Resuscitation
+Retina
+Retrovirology
+Reumatismo
+Reumatizam
+Reumatologia
+Rev ABPAPAL
+Rev ADM
+Rev Acad Arabe Damas
+Rev Acad Pol Sci
+Rev Actual Estomatol Esp
+Rev Actual Odontoestomatol Esp
+Rev Adm Munic
+Rev Afr Polit Econ
+Rev Agrup Odontol Cap Fed
+Rev Alerg
+Rev Alerg Mex
+Rev Allem
+Rev Allergy
+Rev Alsace
+Rev Am Hist
+Rev Annu Soc Odontostomatol Nordest
+Rev Arch Bibl Mus
+Rev Archeol Centre France
+Rev Archeol Mod Archeol Gen
+Rev Argent Alerg
+Rev Argent Cardiol
+Rev Argent Cir
+Rev Argent Endocrinol Metab
+Rev Argent Implantol Estomatol
+Rev Argent Microbiol
+Rev Argent Neurol Psiquiatr
+Rev Argent Reumatol
+Rev Argent Tuberc Enferm Pulm
+Rev Argent Urol
+Rev Argent Urol Nefrol
+Rev Arh
+Rev Asoc Argent Microbiol
+Rev Asoc Med Argent
+Rev Asoc Mex Enferm
+Rev Asoc Odontol Argent
+Rev Asoc Odontol Costa Rica
+Rev Asoc Prof Hosp Nac Odontol
+Rev Assoc Med Bras
+Rev Assoc Med Minas Gerais
+Rev Assoc Med Rio Grande Do Sul
+Rev Assoc Paul Cir Dent
+Rev Assoc Paul Cir Dent Reg Aracatuba
+Rev Assyriol Archeol Orient
+Rev Ateneo Argent Odontol
+Rev Ateneo Catedra Tec Oper Dent
+Rev Atheroscler
+Rev Atheroscler (Paris)
+Rev Auvergne
+Rev Avranchin Pays Granville
+Rev Belg Homoeopath
+Rev Belg Pathol Med Exp
+Rev Belge Archeol Hist Art
+Rev Belge Geogr
+Rev Belge Hist Milit
+Rev Belge Med Dent
+Rev Belge Philol Hist
+Rev Belge Stomatol
+Rev Bibl Nac Jose Marti
+Rev Bibl Natl
+Rev Biol Oral
+Rev Biol Trop
+Rev Black Polit Econ
+Rev Bras Anestesiol
+Rev Bras Biol
+Rev Bras Cir
+Rev Bras Enferm
+Rev Bras Estat
+Rev Bras Estud Popul
+Rev Bras Gastroenterol
+Rev Bras Geogr
+Rev Bras Leprol
+Rev Bras Malariol Doencas Trop
+Rev Bras Med
+Rev Bras Odontol
+Rev Bras Odontol Mil
+Rev Bras Oftalmol
+Rev Bras Otorinolaringol
+Rev Bras Otorrinolaringol (Engl Ed)
+Rev Bras Parasitol Vet
+Rev Bras Pesqui Med Biol
+Rev Bras Psiquiatr
+Rev Bras Tuberc Doencas Torac
+Rev CEPAE
+Rev Can Biol
+Rev Can Biol Exp
+Rev Can Etud Dev
+Rev Cardiovasc Med
+Rev Catarinense Odontol
+Rev Cent Am Odontol
+Rev Cent Estud Demograficos
+Rev Cent Etud Pays Est
+Rev Centro Cienc Biomed Univ Fed Uberlandia
+Rev Centroam Econ
+Rev Chil Hig Med Prev
+Rev Chil Hist Geogr
+Rev Chil Nutr
+Rev Chil Obstet Ginecol
+Rev Chil Odontoestomatol
+Rev Chil Pediatr
+Rev Chilena Infectol
+Rev Chir
+Rev Chir Oncol Radiol O R L Oftalmol Stomatol Chir
+Rev Chir Oncol Radiol O R L Oftalmol Stomatol Otorinolaringol
+Rev Chir Oncol Radiol O R L Oftalmol Stomatol Ser Oftalmol
+Rev Chir Oncol Radiol O R L Oftalmol Stomatol Ser Stomatol
+Rev Chir Orthop Reparatrice Appar Mot
+Rev Cinc Biomed
+Rev Cir (Mex)
+Rev Cir Urug
+Rev Circ Argent Odontol
+Rev Circ Odontol Cordoba
+Rev Circ Odontol Sur
+Rev Circul Ondontol Ros
+Rev Clin Basic Pharm
+Rev Clin Esp
+Rev Clin Exp Hematol
+Rev Clin Gerontol
+Rev Clin Inst Matern Lisb
+Rev Clin Sao Paulo
+Rev Col Estomatol Guatem
+Rev Col Med Cir Guatem
+Rev Col Med Guatem
+Rev Col Nac Enferm
+Rev Colomb Obstet Ginecol
+Rev Colomb Pediatr Pueric
+Rev Comminges
+Rev Coree
+Rev Corps Sante Armees Terre Mer Air
+Rev Corps Sante Mil
+Rev Costarric Cienc Med
+Rev Cubana Cardiol
+Rev Cubana Enferm
+Rev Cubana Estomatol
+Rev Cubana Hig Epidemiol
+Rev Cubana Med
+Rev Cubana Med Trop
+Rev Cubana Pediatr
+Rev Cuhana Adm Salud
+Rev Cult Vozes
+Rev Cytol Clin
+Rev Czech Med
+Rev Dent (San Salv)
+Rev Dent (St Domingo)
+Rev Dent Chile
+Rev Dent Liban
+Rev Deux Mondes
+Rev Dialoctol Tradiciones Pop (Madrid)
+Rev Droit
+Rev Dromoise
+Rev Drug Metab Drug Interact
+Rev East Med Sci
+Rev Econ
+Rev Econ Cond Italy
+Rev Econ Dev
+Rev Econ Nordeste
+Rev Econ Polit
+Rev Econ Soc
+Rev Econ Stat
+Rev Econ Stud
+Rev Econ Sud Ouest
+Rev Ecuat Hig Med Trop
+Rev Ecuat Med Cienc Biol
+Rev Ecuat Pediatr
+Rev Ecuat Pediatr Pueric
+Rev Egyptol
+Rev Electroencephalogr Neurophysiol Clin
+Rev Elev Med Vet Pays Trop
+Rev Endocr Metab Disord
+Rev Enferm
+Rev Enferm (Lisboa)
+Rev Enferm Nov Dimens
+Rev Enseign Philos
+Rev Enseign Super
+Rev Environ Contam Toxicol
+Rev Environ Health
+Rev Epidemiol Med Soc Sante Publique
+Rev Epidemiol Sante Publique
+Rev Esc Enferm USP
+Rev Esc Odontol Tucuman
+Rev Esp Anestesiol
+Rev Esp Anestesiol Reanim
+Rev Esp Antropol Am
+Rev Esp Cardiol
+Rev Esp Doc Cient
+Rev Esp Endodoncia
+Rev Esp Enferm Apar Dig
+Rev Esp Enferm Apar Dig Nutr
+Rev Esp Enferm Dig
+Rev Esp Estomatol
+Rev Esp Fisiol
+Rev Esp Geriatr Gerontol
+Rev Esp Invest Sociol
+Rev Esp Med Nucl
+Rev Esp Obstet Ginecol
+Rev Esp Oncol
+Rev Esp Otoneurooftalmol Neurocir
+Rev Esp Paleontol
+Rev Esp Parad
+Rev Esp Pediatr
+Rev Esp Quimioter
+Rev Esp Reum Enferm Osteoartic
+Rev Esp Reumatol
+Rev Esp Salud Publica
+Rev Esp Tuberc
+Rev Est
+Rev Estad
+Rev Estud Extremenos
+Rev Estud Vida Local
+Rev Ethnol Que
+Rev Etud Byz
+Rev Etud Comp Est Ouest
+Rev Etud Grec
+Rev Etud Ital
+Rev Etud Juives
+Rev Etud Lat
+Rev Etud Slaves
+Rev Etud Sud Est Eur
+Rev Eur Dermatol MST
+Rev Eur Estud Latinoam Caribe
+Rev Eur Etud Clin Biol
+Rev Eur Migr Int
+Rev Eur Odontoestomatol
+Rev FOA
+Rev Fac Cien Med Univ Nac Cordoba
+Rev Fac Cienc Med
+Rev Fac Cienc Med Cordoba
+Rev Fac Farm Odontol Araraquara
+Rev Fac Farm Odontol Ribeiro Preto
+Rev Fac Farm Univ Cent Venez
+Rev Fac Med Tucuman
+Rev Fac Med Univ Nac Auton Mex
+Rev Fac Med Univ Nac Colomb
+Rev Fac Odontol Aracatuba
+Rev Fac Odontol P Alegre
+Rev Fac Odontol Pernambuco
+Rev Fac Odontol Ribeiro Preto
+Rev Fac Odontol Sao Jose Dos Campos
+Rev Fac Odontol Sao Paulo
+Rev Fac Odontol Tucuman
+Rev Fac Odontol Univ Antioq
+Rev Fac Odontol Univ Chile
+Rev Fac Odontol Univ Fed Bahia
+Rev Fac Odontol Univ Nac (Cordoba)
+Rev Fac Odontol Univ Nac Colomb
+Rev Faculdade Odontol FZL
+Rev Faculdade Odontol Lins
+Rev Farm Bioquim Univ Sao Paulo
+Rev Farm Odontol
+Rev Fed Am Health Syst
+Rev Fed Am Hosp
+Rev Fed Odontol Colomb
+Rev Fed Odontol Ecuat
+Rev Fom Soc
+Rev Fr Aff Soc
+Rev Fr Allergol
+Rev Fr Endocrinol Clin
+Rev Fr Endod
+Rev Fr Etud Am
+Rev Fr Etud Clin Biol
+Rev Fr Gerontol
+Rev Fr Gynecol Obstet
+Rev Fr Hist Livre
+Rev Fr Hist Outre Mer
+Rev Fr Mal Respir
+Rev Fr Odontostomatol
+Rev Fr Prothes Dent
+Rev Fr Prothese Dent
+Rev Fr Psychanal
+Rev Fr Sci Polit
+Rev Fr Sociol
+Rev Fr Transfus
+Rev Fr Transfus Hemobiol
+Rev Fr Transfus Immunohematol
+Rev Fuerzas Armadas Venez
+Rev Gastroenterol
+Rev Gastroenterol Disord
+Rev Gastroenterol Mex
+Rev Gastroenterol Peru
+Rev Gaucha Enferm
+Rev Gaucha Odontol
+Rev Gen Droit
+Rev Gen Mar
+Rev Gen Sci Pures Appl Bull Assoc Fr Av Sci
+Rev Geogr Alp
+Rev Geogr Est
+Rev Geogr Inst Panam Geogr Hist
+Rev Geogr Lyon
+Rev Geogr Montr
+Rev Geogr Pyren Sud Ouest
+Rev Gerontol Expr Fr
+Rev Ginecol Obstet (Sao Paulo)
+Rev Goiana Med
+Rev Guatem Estomatol
+Rev Gynecol Obstet
+Rev Haute Auvergne
+Rev Hematol
+Rev Hist (Arg)
+Rev Hist (Brazil)
+Rev Hist (Costa Rica)
+Rev Hist (Paris)
+Rev Hist Am Argent
+Rev Hist Am Fr
+Rev Hist Armees
+Rev Hist Art Dent
+Rev Hist Bordeaux Dep Gironde
+Rev Hist Canaria
+Rev Hist Demogr
+Rev Hist Deuxieme Guerre Mond
+Rev Hist Eccles
+Rev Hist Econ Soc
+Rev Hist Eglise Fr
+Rev Hist Jeronimo Zurita
+Rev Hist Litt Fr
+Rev Hist Maghreb
+Rev Hist Med Heb
+Rev Hist Mil
+Rev Hist Mod Contemp
+Rev Hist Naval
+Rev Hist Pharm (Paris)
+Rev Hist Philos Relig
+Rev Hist Psicol
+Rev Hist Relig
+Rev Hist Sci (Paris)
+Rev Hist Sci [Paris]
+Rev Hist Textes
+Rev Hosp Clin Fac Med Sao Paulo
+Rev Hosp Nino (Lima)
+Rev Hosp Ninos Alejandro Mann
+Rev Hyg Med Soc
+Rev Hyg Prof
+Rev Iber Endocrinol
+Rev Iber Parasitol
+Rev Iberoam Micol
+Rev Iberoam Ortod
+Rev Ibys
+Rev Ig Bacteriol Virusol Parazitol Epidemiol Pneumoftiziol Bacteriol Virusol Parazitol Epidemiol
+Rev Ig Bacteriol Virusol Parazitol Epidemiol Pneumoftiziol Pneumoftiziol
+Rev Ig Med Muncii Med Soc Bacteriol Virusol Parazitol Epidemiol Pneumoftiziol Pneumoftiziol
+Rev Immunogenet
+Rev Immunol (Paris)
+Rev Immunol Ther Antimicrob
+Rev Indias
+Rev Inf Legis
+Rev Infect Dis
+Rev Infirm
+Rev Infirm Assist Soc
+Rev Infirm Infirm Aux Que
+Rev Infirm [Inf]
+Rev Inform Med Ter
+Rev Inst Adolfo Lutz
+Rev Inst Antibiot (Recife)
+Rev Inst Eur
+Rev Inst Hist Geogr Bras
+Rev Inst Hyg Mines (Hasselt)
+Rev Inst Med Trop Sao Paulo
+Rev Inst Nac Cancerol (Mex)
+Rev Inst Napoleon
+Rev Inst Salubr Enferm Trop
+Rev Inst Sociol
+Rev Int Comm Jurists
+Rev Int Criminol Police Tech
+Rev Int Hepatol
+Rev Int Hist Mil
+Rev Int Hist Psychanal
+Rev Int Hist Psychiatr
+Rev Int Pediatr
+Rev Int Philos
+Rev Int Serv Sante Armees
+Rev Int Serv Sante Forces Armees
+Rev Int Sociol
+Rev Int Trach
+Rev Int Trach Pathol Ocul Trop Subtrop
+Rev Int Trach Pathol Ocul Trop Subtrop Sante Publique
+Rev Interam Planif
+Rev Interam Radiol
+Rev Invest (Guadalajara)
+Rev Invest Clin
+Rev Invest Salud Publica
+Rev Islenha
+Rev Istor
+Rev Jurid Univ P R
+Rev Kuba Med Trop Parasitol
+Rev Lang Rom
+Rev Laryngol Otol Rhinol (Bord)
+Rev Lat Am Enfermagem
+Rev Latinoam Anat Patol
+Rev Latinoam Cir Plast
+Rev Latinoam Microbiol
+Rev Latinoam Microbiol Parasitol (Mex)
+Rev Latinoam Patol
+Rev Latinoam Perinatol
+Rev Latinoam Psicol
+Rev Law Soc Change
+Rev Louvre Mus Fr
+Rev Lyon Med
+Rev Mal Respir
+Rev Mar
+Rev Med (Mex)
+Rev Med (Paris)
+Rev Med Aeron
+Rev Med Aeron Braz
+Rev Med Aeronaut
+Rev Med Alpes Fr
+Rev Med Brux
+Rev Med Chil
+Rev Med Chir Mal Foie
+Rev Med Chir Soc Med Nat Iasi
+Rev Med Cienc Afines
+Rev Med Cordoba
+Rev Med Costa Rica
+Rev Med Cubana
+Rev Med Hondur
+Rev Med Hosp Cent Empl
+Rev Med Hosp Gen (Mex)
+Rev Med Hosp Obrero
+Rev Med Hyg Outre Mer
+Rev Med IPSS
+Rev Med Inst Mex Seguro Soc
+Rev Med Int Photo Cinema Telev
+Rev Med Interna Neurol Psihiatr Neurochir Dermatovenerol Med Interna
+Rev Med Interna Neurol Psihiatr Neurochir Dermatovenerol Neurol Psihiatr Neurochir
+Rev Med Interne
+Rev Med Liege
+Rev Med Louvain
+Rev Med Mil
+Rev Med Miniere
+Rev Med Moamb
+Rev Med Moyen Orient
+Rev Med Nancy
+Rev Med Normandes
+Rev Med Panama
+Rev Med Peru
+Rev Med Picardie
+Rev Med Psychosom
+Rev Med Psychosom Psychol Med
+Rev Med Suisse
+Rev Med Suisse Romande
+Rev Med Toulouse
+Rev Med Tours
+Rev Med Univ Navarra
+Rev Med Valparaiso
+Rev Med Vet (B Aires)
+Rev Med Vet (Toulouse)
+Rev Med Virol
+Rev Medica Hosp Esp
+Rev Medica Minas
+Rev Mediterr Sci Med
+Rev Metaphys
+Rev Metaphys Morale
+Rev Mex Anal Conducta
+Rev Mex Cienc Polit Soc
+Rev Mex Cir Ginecol Cancer
+Rev Mex Sociol
+Rev Mex Urol
+Rev Mus Nac
+Rev Museo Fac Odontol B Aires
+Rev Neurol
+Rev Neurol (Paris)
+Rev Neurol Dis
+Rev Neuropsiquiatr
+Rev Neuropsychiatr Infant
+Rev Neurosci
+Rev Nord
+Rev Nouv
+Rev Numis
+Rev OIM Sobre Migr Am Lat
+Rev Obstet Ginecol Venez
+Rev Occident Musulman Mediterr
+Rev Occidente
+Rev Oculomot Res
+Rev Odontoestomatol
+Rev Odontoimplantol
+Rev Odontol (Cordoba)
+Rev Odontol (La Paz)
+Rev Odontol Circ Odontol Parag
+Rev Odontol Concepcion
+Rev Odontol Costa Rica
+Rev Odontol Ecuat
+Rev Odontol P R
+Rev Odontol P R (Santurce)
+Rev Odontol Parana
+Rev Odontol St Catarina
+Rev Odontol UNESP
+Rev Odontol UNICID
+Rev Odontol Univ Sao Paulo
+Rev Odontostomatol
+Rev Odontostomatol (Paris)
+Rev Odontostomatol Midi Fr
+Rev Odontostomatol Nordest
+Rev Ordem Med
+Rev Orthop Chir Appar Mot
+Rev Orthop Dento Faciale
+Rev Ortop Traumatol Ed Lat Am
+Rev Oto Neuro Oftalmol Cir Neurol Sud Am
+Rev Otoneuroophtalmol
+Rev Otorrinolaringol
+Rev Palaeobot Palynol
+Rev Palais Decouv
+Rev Palud Med Trop
+Rev Panam Salud Publica
+Rev Pathol Comp
+Rev Pathol Gen Physiol Clin
+Rev Patient Focus Care Assoc
+Rev Pau Bearn
+Rev Paul Endodontia
+Rev Paul Enferm
+Rev Paul Hosp
+Rev Paul Med
+Rev Paul Odontol
+Rev Pediatr
+Rev Pediatr Obstet Ginecol Obstet Ginecol
+Rev Pediatr Obstet Ginecol Pediatr
+Rev Pernambucana Odontol
+Rev Peru Pediatr
+Rev Peru Poblac
+Rev Philol Litt Hist Anc
+Rev Philos Louv
+Rev Physiol Biochem Pharmacol
+Rev Planeac Desarro
+Rev Pneumol Clin
+Rev Pol Acad Sci
+Rev Polit
+Rev Port Cardiol
+Rev Port Cir Cardiotorac Vasc
+Rev Port Estomatol Cir Maxilofac
+Rev Port Farm
+Rev Port Hist
+Rev Port Med Mil
+Rev Port Pediatr
+Rev Port Pediatr Pueric
+Rev Port Pneumol
+Rev Prat
+Rev Psicoanal
+Rev Psiquiatr Psicol Med Eur Am Lat
+Rev Psychol Appl
+Rev Publ Nav
+Rev Public Data Use
+Rev Pure Appl Pharmacol Sci
+Rev Quest Sci
+Rev Quim Farm
+Rev Quir Esp
+Rev Radic Polit Econ
+Rev Reg Aracatuba Assoc Paul Cir Dent
+Rev Reg Stud
+Rev Relig
+Rev Relig Res
+Rev Reprod
+Rev Rev Interam
+Rev Rhum
+Rev Rhum Ed Fr
+Rev Rhum Engl Ed
+Rev Rhum Mal Osteoartic
+Rev Roche
+Rev Rom Bioet
+Rev Rom Stat
+Rev Rouergue
+Rev Roum Endocrinol
+Rev Roum Hist
+Rev Roum Inframicrobiol
+Rev Roum Med
+Rev Roum Med Intern
+Rev Roum Morphol Embryol
+Rev Roum Morphol Physiol
+Rev Roum Neurol
+Rev Roum Neurol Psychiatr
+Rev Roum Physiol
+Rev Roum Virol
+Rev SESDA
+Rev Saintonge Aunis
+Rev Salud Publica (Bogota)
+Rev Sanid Asist Soc
+Rev Sanid Hig Publica (Madr)
+Rev Sanid Mil Peru
+Rev Sanid Milit
+Rev Sanid Milit Argent
+Rev Sanid Polic
+Rev Saude Publica
+Rev Sci Edu
+Rev Sci Hum
+Rev Sci Instrum
+Rev Sci Med
+Rev Sci Morales Polit
+Rev Sci Philos Theol
+Rev Sci Soc Fr Est
+Rev Sci Tech
+Rev Sci Tech Ser Sci Hum
+Rev Serv Nac Salud
+Rev Serv Nac Tubers
+Rev Soc Argent Biol
+Rev Soc Biom Hum
+Rev Soc Bolivar Venez
+Rev Soc Bras Hist Cienc
+Rev Soc Bras Med Trop
+Rev Soc Colomb Endocrinol
+Rev Soc Colomb Ortod
+Rev Soc Colomb Pediatr Pueric
+Rev Soc Econ
+Rev Soc Fr Hist Art Dent
+Rev Soc Fr Hist Hop
+Rev Soc Ginecol Obstet
+Rev Soc Mex Hist Nat
+Rev Soc Odontol La Plata
+Rev Soc Peru Endocrinol
+Rev Soc Policy
+Rev Soc Savantes Haute Normandie
+Rev Soc Venez Hist Med
+Rev Social Law
+Rev Sociol
+Rev Stat
+Rev Stat Appl
+Rev Stiint Med
+Rev Stomatol Chir Maxillofac
+Rev Stomatoodontol Nord Fr
+Rev Suisse Zool
+Rev Surg
+Rev Synth
+Rev Tarn
+Rev Trav Acad Sci Morales Polit C R Seances
+Rev Tuberc
+Rev Tuberc Pneumol (Paris)
+Rev Tuberc Urug
+Rev Tunis Etud Popul
+Rev Tunis Geogr
+Rev Tunis Sci Soc
+Rev Univ
+Rev Univ Brux
+Rev Univ Nac Cordoba
+Rev Univ Ottawa
+Rev Urol(mexico)
+Rev Urug Psicoanal
+Rev Venez Sanid Asist Soc
+Rev Venez Urol
+Rev Viernes Med
+Rev Vivarais
+Revis Biol Celular
+Revis Statut La
+Revmatologiia (Mosk)
+Revolution
+Revue Geriatr
+Revue Stomatol
+Rhein Mus Philol
+Rhein Vierteljahrsbl
+Rheinische Lebensbilder
+Rheinisches Arztebl
+Rheum Dis Clin North Am
+Rheumatism
+Rheumatol Balneol Allergol
+Rheumatol Int
+Rheumatol Phys Med
+Rheumatol Rehabil
+Rheumatology
+Rheumatology (Oxford)
+Rhinol Suppl
+Rhinology
+Rhod Nurse
+Rhodesiana
+Rhumatologie
+Ric Clin Lab
+Ric Sci
+Ric Sci 2 Ser Pt 1 Riv
+Ric Sci 2 Ser Pt 2 Rend [B]
+Richmond Cty Hist
+Riforma Med
+Rig
+Rinascimento
+Rinsho Biseibutshu Jinsoku Shindan Kenkyukai Shi
+Rinsho Byori
+Rinsho Eiyo
+Rinsho Fujinka Sanka
+Rinsho Ganka
+Rinsho Geka
+Rinsho Hoshasen
+Rinsho Ketsueki
+Rinsho Kyobu Geka
+Rinsho Naika Shonika Intern Med
+Rinsho Shika
+Rinsho Shinkeigaku
+Rinsho Shishubyo Danwakai Kaishi
+Rinsho Shokakibyogaku
+Ripon Forum
+Risk
+Risk Anal
+Risk Issues Health Saf
+Risk Manag Can Health Care
+Risk Manage
+Rittenhouse
+Riv Anat Patol Oncol
+Riv Biol
+Riv Chir Med
+Riv Chir Pediatr
+Riv Clin Pediatr
+Riv Crit Clin Med
+Riv Crit Stor Filos
+Riv Emoter Immunoematol
+Riv Estet
+Riv Eur Sci Med Farmacol
+Riv Filol Istruz Classica
+Riv Filos
+Riv Gastroenterol
+Riv Geogr Ital
+Riv Inferm
+Riv Infort Mal Prof
+Riv Int Psicol Ipn
+Riv Int Sci Sociali
+Riv Ist Sieroter Ital
+Riv Ist Vaccinogeno Consorzi Prov Antituberc
+Riv Istochim Norm Patol
+Riv Ital Econ Demogr Stat
+Riv Ital Ginecol
+Riv Ital Ig
+Riv Ital Med Leg
+Riv Ital Odontoiatr Infant
+Riv Ital Odontotec
+Riv Ital Pediatr
+Riv Ital Stomatol
+Riv Ital Trac Patol Ocul Esotica
+Riv Malariol
+Riv Maritt
+Riv Med Aeronaut
+Riv Med Aeronaut Spaz
+Riv Meteorol Aeronaut
+Riv Mil
+Riv Neurobiol
+Riv Neurol
+Riv Odontoiatr Amici Brugg
+Riv Odontostomatol Implantoprotesi
+Riv Ostet Ginecol
+Riv Ostet Ginecol Prat
+Riv Ostet Ginecol Prat Med Perinat
+Riv Otoneurooftalmol
+Riv Parassitol
+Riv Patol Clin
+Riv Patol Clin Sper
+Riv Patol Nerv Ment
+Riv Pediatr Sicil
+Riv Psicoanal
+Riv Psicol
+Riv Psicol Scr
+Riv Psicol Soc Arch Ital Psicol Gen
+Riv Radiol
+Riv Rosmin Filos Cult
+Riv Sicil Tuberc
+Riv Sper Freniatr Med Leg Alien Ment
+Riv Stor Calabr
+Riv Stor Chiesa Ital
+Riv Stor Filos
+Riv Stor Ital
+Riv Stor Med
+Riv Stor Sci
+Riv Stor Sci Mediche Nat
+Riv Studi Class
+Riv Studi Orient
+Riv Tuberc Mal Appar Respir
+Rob Auton Syst
+Rochester Hist
+Rocky Mt Med J
+Rocky Mt Soc Sci J
+Rocz Akad Med Bialymst
+Rocz Akad Med Bialymst Supl
+Rocz Akad Med Im Juliana Marchlewskiego Bialymst
+Rocz Akad Med Im Juliana Marchlewskiego Bialymst Suppl
+Rocz Filoz
+Rocz Humanist
+Rocz Panstw Zakl Hig
+Rocz Pomor Akad Med
+Rodo Kagaku
+Rofo
+Rogerian Nurs Sci News
+Roll Stone
+Rom Hist Mitt
+Rom J Endocrinol
+Rom J Gastroenterol
+Rom J Gerontol Geriatr
+Rom J Intern Med
+Rom J Morphol Embryol
+Rom J Neurol Psychiatry
+Rom J Physiol
+Rom J Virol
+Rom Med Rev
+Roman Notes
+Romania
+Romantisme
+Ronenbyo
+Rontgen Laborator
+Rontgenblatter
+Rontgendiagnostik Ergeb
+Rontgeneur Radiodiagn Clin Eur
+Rontgenpraxis
+Rooilijn
+Roshni
+Ross Fiziol Zh Im I M Sechenova
+Ross Gastroenterol Zh
+Ross Med Zh
+Rostrum
+Roum Arch Microbiol Immunol
+Round Table
+Rozhl Chir
+Rozhl Tuberk
+Rozpr Wydz Nauk Med
+Ruch Praw Ekon Socjol
+Rudolstaedter Heimath
+Rum Med Rev
+Rumah Tangga Kesehatan
+Ruperto Carola
+Rural Afr
+Rural Cond Trends
+Rural Demogr
+Rural Policy Brief
+Rural Reconstr Forum
+Rural Remote Health
+Rural Sociol
+Russ Coiles Health Trends
+Russ J Immunol
+Russ J Plant Physiol
+Russ Rev
+Russ Soc Sci Rev
+Rutgers Camden Law J
+Rutgers Comput Technol Law J
+Rutgers J Comput Technol Law
+Rutgers Law J
+Rutgers Law Rev
+Rutland Rec
+Ryan Advis Health Serv Gov Boards
+Ryoikibetsu Shokogun Shirizu
+Ryumachi
+S A Nurs J
+S Afr Cancer Bull
+S Afr Dent J
+S Afr Geogr J
+S Afr Hist J
+S Afr J Afr Aff
+S Afr J Anim Sci
+S Afr J Clin Sci
+S Afr J Commun Disord
+S Afr J Econ
+S Afr J Econ Hist
+S Afr J Hum Rights
+S Afr J Lab Clin Med
+S Afr J Med Sci
+S Afr J Obstet Gynaecol
+S Afr J Psychol
+S Afr J Sci
+S Afr J Surg
+S Afr Labour Bull
+S Afr Law J
+S Afr Law Rep
+S Afr Med J
+S Afr Nursing J
+S Afr Sociol Rev
+S Afr Tydskr Sosiol
+S Afr Tydskr Strafr Kriminol
+S C Dent J
+S C Hist Illus
+S C Hist Mag
+S C Law Rev
+S C Nurs
+S C Nurse
+S D Codif Laws S D
+S D Farm Home Res
+S D Hist Collect
+S D J Med
+S D Law Rev
+S D Med
+S D Nurse
+S TA NU
+SA J Contin Med Educ
+SA Nurs J
+SAAD Dig
+SAAS Bull Biochem Biotechnol
+SADJ
+SAFE J
+SAIS Rev
+SAR QSAR Environ Res
+SAfAIDS News
+SBC
+SC Rep
+SC Trodent
+SCADA J
+SCHA J
+SCI Nurs
+SCN News
+SCNA Newsl
+SD J Med Pharm
+SDA Dent
+SECOLAS Ann
+SHSTF
+SIAM J Appl Math
+SIECUS Rep
+SMU Law Rev
+SOLAIAT
+SPUMS J
+SPVN
+SRA J
+SSO Schweiz Monatsschr Zahnheilkd
+ST Vincents Hosp Med Bull
+STD Bull
+STEP Perspect
+STNS J Trauma Nurs
+Saalburg Jahrb
+Saarl Arztebl
+Sabouraudia
+Sac Explor
+Sacitra Ayurveda
+Saeculum
+Safe Mother
+Sage
+Saguenay Med
+Sairaanh Vuosik
+Sairaanhoitaja
+Sairaanhoitajalehti
+Saishin Igaku
+Saisons Alsace
+Saline Systems
+Salisbury Med Bull
+Salmagundi
+Salubritas
+Salud Bucal
+Salud Publica Mex
+Salud Reprod Soc
+Salus
+Salzburger Beitr Paracelsusforsch
+Same Day Surg
+Samid Aliqtisadi
+Samiska
+Samml Zwangl Abh Geb Psychiatr Neurol
+Sample Regist Bull
+Samtiden
+San Diego Law Rev
+San Fernando Val Dent Soc Bull
+San Gabriel Val Dent Soc Bull
+San Jose Stud
+Sanfujinka Chiryo
+Sanfujinka No Jissai
+Sanfujinka No Shinpo
+Sang
+Sangre (Barc)
+Sangue
+Sangyo Eiseigaku Zasshi
+Sangyo Igaku
+Sanita Pubblica
+Sankhya
+Sankhya Ser B
+Sanop Misaengmul Hakhoe Chi
+Santa Clara Comput High Technol Law J
+Santa Clara Law Rev
+Santa Clara Lawyer
+Sante
+Sante Ment Que
+Sante Publique
+Sante Publique (Bucur)
+Sante Que
+Sante Salud
+Santiago
+Sao Paulo Med J
+Sapienza
+Sapporo Igaku Zasshi
+Sarcoidosis
+Sarcoidosis Vasc Diffuse Lung Dis
+Sarvekshana
+Sask Hist
+Sask Law Rev
+Saturday Rev
+Saturday Rev Sci
+Saturday Rev World
+Saudi Med J
+Savanna
+Sb Arch Pr
+Sb Lek
+Sb Nar Muz Pr Rada C Lit Hist
+Sb Nar Muz Praze Rada A Hist
+Sb Nar Muz Praze Rada B
+Sb Pathofysiol Traveni Vyz Gastroenterol Bohema
+Sb Pr Filos Fak Brnenske Univ Rada Hist
+Sb Tr Azerbaidzhanskii Gos Meditsinskii Inst N Narimanova
+Sb Ved Pr Lek Fak Karlovy Univerzity Hradci Kralove
+Sb Ved Pr Lek Fak Karlovy Univerzity Hradci Kralove Suppl
+Sbs Ces Geogr Spol
+Scalpel (Brux)
+Scalpel Tongs
+Scan Electron Microsc
+Scand Actuar J
+Scand Audiol
+Scand Audiol Suppl
+Scand Cardiovasc J
+Scand Cardiovasc J Suppl
+Scand Econ Hist Rev
+Scand J Caring Sci
+Scand J Clin Lab Invest
+Scand J Clin Lab Invest Suppl
+Scand J Dent Res
+Scand J Dev Altern
+Scand J Econ
+Scand J Gastroenterol
+Scand J Gastroenterol Suppl
+Scand J Haematol
+Scand J Haematol Suppl
+Scand J Hist
+Scand J Immunol
+Scand J Immunol Suppl
+Scand J Infect Dis
+Scand J Infect Dis Suppl
+Scand J Med Sci Sports
+Scand J Occup Ther
+Scand J Plast Reconstr Surg
+Scand J Plast Reconstr Surg Hand Surg
+Scand J Plast Reconstr Surg Hand Surg Suppl
+Scand J Plast Reconstr Surg Suppl
+Scand J Prim Health Care
+Scand J Prim Health Care Suppl
+Scand J Psychol
+Scand J Public Health
+Scand J Public Health Suppl
+Scand J Rehabil Med
+Scand J Rehabil Med Suppl
+Scand J Respir Dis
+Scand J Respir Dis Suppl
+Scand J Rheumatol
+Scand J Rheumatol Suppl
+Scand J Soc Med
+Scand J Soc Med Suppl
+Scand J Soc Welf
+Scand J Surg
+Scand J Thorac Cardiovasc Surg
+Scand J Thorac Cardiovasc Surg Suppl
+Scand J Urol Nephrol
+Scand J Urol Nephrol Suppl
+Scand J Work Environ Health
+Scand Popul Stud
+Scand Stud
+Scand Stud Law
+Scandia
+Scanning
+Scanning Microsc
+Scanning Microsc Suppl
+Scanodont
+Scapel Quill
+Sch Couns
+Sch Dent Serv Gaz N Z
+Sch Health Rev
+Sch Inq Nurs Pract
+Sch Psychol Dig
+Sch Psychol Int
+Sch Sci Math
+Sch Sci Rev
+Schaffhauser Beitr Vaterl Gesch
+Scheidewege
+Schiff Zeit
+Schizophr Bull
+Schizophr Res
+Schmerz
+Scholast Update
+School Nurse News
+School Psych Rev
+Schr Gesch Kult Alten Orients
+Schriftenr Geb Off Gesundheitswes
+Schriftenr Ges Freunde Med Hochsch Hannover
+Schriftenr Gesch Versamml Dtsch Naturforsch Arzte
+Schriftenr Neurol
+Schriftenr Ver Wasser Boden Lufthyg
+Schriftenr Zentralbl Arbeitsmed Arbeitsschutz Prophyl
+Schriftenr Zentralbl Arbeitsmed Arbeitsschutz Prophyl Ergonomie
+Schweiz Apoth Ztg
+Schweiz Arch Neurol Neurochir Psychiatr
+Schweiz Arch Neurol Psychiatr
+Schweiz Arch Tierheilkd
+Schweiz Arzteztg
+Schweiz Med Wochenschr
+Schweiz Med Wochenschr Suppl
+Schweiz Monatsh
+Schweiz Monatsschr Zahnmed
+Schweiz Rundsch Med Prax
+Schweiz Z Gesch
+Schweiz Z Gynakol Geburtshilfe
+Schweiz Z Homoopath
+Schweiz Z Med Traumatol
+Schweiz Z Pathol Bakteriol
+Schweiz Z Psychol Anwend
+Schweiz Z Soziol
+Schweiz Z Sportmed
+Schweiz Z Tuberc Pneumonol
+Schweiz Z Tuberk
+Schweiz Z Volkswirtsch Stat
+Schwest Rev
+Sci Act
+Sci Aging Knowledge Environ
+Sci Aliments
+Sci Am
+Sci Avenir
+Sci Basis Med Annu Rev
+Sci Bio J
+Sci Can
+Sci China B
+Sci China C Life Sci
+Sci Christ Belief
+Sci Commun
+Sci Context
+Sci Cult
+Sci Cult (Lond)
+Sci Dig
+Sci Educ
+Sci Educ Bull
+Sci Educ J
+Sci Eng Ethics
+Sci Forum
+Sci Genet
+Sci Geol Mem
+Sci Gov Rep
+Sci Hortic (Amsterdam)
+Sci Justice
+Sci Med (Phila)
+Sci Med Ital
+Sci Med Man
+Sci New Guinea
+Sci News
+Sci People
+Sci Pharm
+Sci Proc Cardiff Med Soc
+Sci Prog
+Sci Prog (New Haven)
+Sci Public Policy
+Sci Rech Odontostomatol
+Sci Rep Ist Super Sanita
+Sci Rep Res Inst Tohoku Univ [Med]
+Sci Rep Tohoku Univ Ser 7
+Sci STKE
+Sci Sin
+Sci Sin [B]
+Sci Soc
+Sci Soc Sante
+Sci Sports
+Sci Stud
+Sci Teach
+Sci Tech Perspect
+Sci Technol Human Values
+Sci Total Environ
+Science
+Science (80- )
+Sciences (New York)
+Scientia
+ScientificWorldJournal
+Scientist
+Scientometrics
+Scott Edu Rev
+Scott Geogr Mag
+Scott Hist Rev
+Scott J Crim Justice Stud
+Scott J Polit Econ
+Scott J Sociol
+Scott Med J
+Scr Ethnol
+Scr Med (Brno)
+Scr Minora
+Screening
+Scriptorium
+Sdc Mag
+Se Pu
+Sea View Hosp Bull
+Seagull Theatre Q
+Seaprap Res Rep
+Seara Med
+Search Medford N J
+Seattle Univ Law Rev
+Second Messengers Phosphoproteins
+Second Opin
+Second Opin (Chic)
+Second Opin Health Care Issues
+Sedimentology
+Seeds
+Sefarad
+Seibutsu Kankyo Chosetsu
+Seikagaku
+Seikatsu Eisei
+Seikei Geka
+Seiroka Kango Daigaku Kiyo
+Seishin Igaku
+Seishin Igaku Kenkyusho Gyosekishu
+Seishin Shinkeigaku Zasshi
+Seitai Kagaku
+Seizure
+Sel Cancer Ther
+Sel Odontol (Sao Paulo)
+Self
+Sem Epidemiol
+Sem Hop
+Sem Hop Inf
+Sem Hop Paris
+Sem Hop Ther
+Sem Hop Ther Paris
+Sem Med
+Sem Med Mex
+Sem Med Prof Med Soc
+Sem Medicale Med Soc
+Sem Ther
+Semin Adolesc Med
+Semin Anesth
+Semin Arthritis Rheum
+Semin Arthroplasty
+Semin Cancer Biol
+Semin Cardiothorac Vasc Anesth
+Semin Cell Biol
+Semin Cell Dev Biol
+Semin Clin Neuropsychiatry
+Semin Cutan Med Surg
+Semin Dent Hyg
+Semin Dermatol
+Semin Diagn Pathol
+Semin Dial
+Semin Drug Treat
+Semin Ensen Odontopediatr
+Semin Fam Med
+Semin Fetal Neonatal Med
+Semin Gastrointest Dis
+Semin Hear
+Semin Hematol
+Semin Immunol
+Semin Int
+Semin Interv Cardiol
+Semin Laparosc Surg
+Semin Liver Dis
+Semin Musculoskelet Radiol
+Semin Neonatol
+Semin Nephrol
+Semin Neurol
+Semin Nucl Med
+Semin Nurse Manag
+Semin Oncol
+Semin Oncol Nurs
+Semin Ophthalmol
+Semin Orthod
+Semin Pediatr Infect Dis
+Semin Pediatr Neurol
+Semin Pediatr Surg
+Semin Perinatol
+Semin Perioper Nurs
+Semin Psychiatry
+Semin Radiat Oncol
+Semin Rep Merck Sharp Dohme
+Semin Reprod Endocrinol
+Semin Reprod Med
+Semin Respir Crit Care Med
+Semin Respir Infect
+Semin Roentgenol
+Semin Speech Lang
+Semin Surg Oncol
+Semin Thorac Cardiovasc Surg
+Semin Thorac Cardiovasc Surg Pediatr Card Surg Annu
+Semin Thromb Hemost
+Semin Ultrasound CT MR
+Semin Urol
+Semin Urol Oncol
+Semin Vasc Med
+Semin Vasc Surg
+Semin Vet Med Surg (Small Anim)
+Semina
+Seminar
+Senckenb Biol
+Senefiance
+Sens Actuators A Phys
+Sens Actuators B Chem
+Sens Processes
+Sens Syst
+Sentinel Event Alert
+Seoul J Med
+Ser Filol Ros
+Ser Haematol
+Ser Libr
+Ser Nurs Adm
+Ser Paedopsychiatr
+Serv Migr
+Servir
+Sess Laws Guam
+Sess Laws Hawaii Passed State Legis Hawaii
+Sess Laws Kans Kans
+Sess Laws State Minn Minn
+Sessualita
+Sessuologia
+Seton Hall Law Rev
+Seton Hall Legis J
+Settim Med
+Seven Days
+Seventeenth Century
+Sewage Ind Waste
+Sewage Work J
+Sex Abuse
+Sex Disabil
+Sex Health
+Sex Health Exch
+Sex Marital Ther
+Sex Med Today
+Sex Planeam Fam
+Sex Roles
+Sex Transm Dis
+Sex Transm Infect
+Sex Wkly Plus
+Shakai Keizai Shigaku
+Shakespeare Stud
+Shandong Yi Kan
+Shanghai Kou Qiang Yi Xue
+Shendet Pop
+Sheng Li Ke Xue Jin Zhan
+Sheng Li Xue Bao
+Sheng Wu Gong Cheng Xue Bao
+Sheng Wu Hua Xue Yu Sheng Wu Wu Li Xue Bao (Shanghai)
+Sheng Wu Yi Xue Gong Cheng Xue Za Zhi
+Shengzhi Yu Biyun
+Shi Yan Sheng Wu Xue Bao
+Shigaku
+Shigaku Zasshi
+Shika Igaku
+Shika Kiso Igakkai Zasshi
+Shika Rikogaku Zasshi
+Shika Zairyo Kikai
+Shikai Tenbo
+Shikwa Gakuho
+Shimane J Med Sci
+Shinkei Kenkyu No Shimpo
+Shinrigaku Kenkyu
+Shinryo
+Shiso
+Shiyo
+Shma
+Shock
+Shokubutsu Kenkyu Zasshi
+Shokuhin Eiseigaku Zasshi
+Shoni Shikagaku Zasshi
+Shonika
+Shonika Kiyo
+Showa Igakkai Zasshi
+Showa Shigakkai Zasshi
+Shui sheng sheng wu hsueh bao
+Shujutsu
+Si Yu Yan
+Sichuan Da Xue Xue Bao
+Sichuan Da Xue Xue Bao Yi Xue Ban
+Sichuan Yi Xue Yuan Xue Bao
+Sicil Sanit
+Sicilia Med
+SidAlerte
+Sidahora
+Sieccan J
+Sierra
+Sight Sav Rev
+Sightsav Rev
+Sightsaving
+Sigmund Freud House Bull
+Sign
+Signature
+Signs (Chic)
+Siirtolaisuus
+Simon Wiesenthal Cent Annu
+Simul Games
+Simul Gaming
+Sinai Hosp J (Balt)
+Singap J Trop Geogr
+Singap Libr
+Singap Stat Bull
+Singapore Dent J
+Singapore Fam Physician
+Singapore J Obstet Gynecol
+Singapore Med J
+Singapore Public Health Bull
+Singmul Hakhoe Chi
+Sintesi Medica
+Sir Lanka J Popul Stud
+Siriraj Hosp Gaz
+Sist Nerv
+Sist Urbani
+Sistole
+Sitzungsber Heidelb Akad Wiss Math Naturwiss Kl
+Sixt Century J
+Sjukskoterskan
+Skand Arch Physiol
+Skeletal Radiol
+Skeptic
+Skin (Los Angeles)
+Skin Pharmacol
+Skin Pharmacol Appl Skin Physiol
+Skin Pharmacol Physiol
+Skin Res Technol
+Skin Therapy Lett
+Skinmed
+Skull Base
+Sky Telescope
+Slaski Kwat Hist Sabotka
+Slav East Eur Rev
+Slavery Abol
+Slavic Rev
+Sleep
+Sleep Breath
+Sleep Med
+Sleep Med Rev
+Sleep Res Online
+Slezsky Sb
+Sloan Manage Rev
+Slov Arch
+Slov Lek
+Slov Prehl
+Slown Biogr Pol Med XX Wieku
+Smith Coll Stud Soc Work
+Smith-Hurd Ill Annot Statut Ill
+Smithson Ann Flight
+Smithson Stud Hist Technol
+Smithsonian
+Snakeroot Extr
+Soap Perfum Cosmet (Lond)
+Soc Action
+Soc Afr SIDA
+Soc Anc Med Rev
+Soc Anim
+Soc Appl Bacteriol Symp Ser
+Soc Behav
+Soc Behav Pers
+Soc Biol
+Soc Biol Hum Aff
+Soc Casework
+Soc Change
+Soc Choice Welfare
+Soc Cult
+Soc Dev
+Soc Dev Issues
+Soc Econ Adm (Lond)
+Soc Econ Stud
+Soc Forces
+Soc Gen Physiol Ser
+Soc Hist
+Soc Hist Med
+Soc Indic Res
+Soc Ital Dermatol Sifilogr Sezioni Interprov Soc Ital Dermatol Sifilogr
+Soc Justice
+Soc Justice Res
+Soc Justice Rev
+Soc Labour Bull
+Soc Leg Stud
+Soc Malawi J
+Soc Mar Q
+Soc Mark Forum
+Soc Mark Update
+Soc Med Tidskr
+Soc Nat Resour
+Soc Networks
+Soc Nurs Hist Gaz
+Soc Philos Policy
+Soc Policy
+Soc Policy Rep
+Soc Prax
+Soc Probl
+Soc Psychiatry
+Soc Psychiatry Psychiatr Epidemiol
+Soc Psychol
+Soc Psychol Q
+Soc Rehabil Rec
+Soc Res (New York)
+Soc Responsib Journal Law Med
+Soc Sci
+Soc Sci (Kans)
+Soc Sci China
+Soc Sci Comput Rev
+Soc Sci Hist
+Soc Sci Inf (Paris)
+Soc Sci Inf Stud
+Soc Sci J
+Soc Sci Med
+Soc Sci Med [A]
+Soc Sci Med [B]
+Soc Sci Med [C]
+Soc Sci Med [D]
+Soc Sci Med [E]
+Soc Sci Med [F]
+Soc Sci Med [Med Anthropol]
+Soc Sci Med [Med Econ]
+Soc Sci Med [Med Geogr]
+Soc Sci Med [Med Psychol Med Sociol]
+Soc Sci New Lett
+Soc Sci Q
+Soc Sci Res
+Soc Secur Bull
+Soc Secur Bull Annu Stat Suppl
+Soc Serv Rev
+Soc Soc Hist Med Bull (Lond)
+Soc Stor
+Soc Stud (Maynooth)
+Soc Stud Sci
+Soc Theory Pract
+Soc Thought
+Soc Welfare (India)
+Soc Wetenschap
+Soc Work
+Soc Work Educ
+Soc Work Educ Dev Newsl
+Soc Work Groups
+Soc Work Health Care
+Soc Work Res
+Soc Work Res Abstr
+Social Rev
+Social Revolut
+Socialisme
+Socialmed Tidskr Skriftser
+Societas
+Society
+Socioecon Issues Health
+Socioecon Plann Sci
+Sociol Anal
+Sociol Bull
+Sociol Cas
+Sociol Diritto
+Sociol Educ
+Sociol Focus
+Sociol Gids
+Sociol Health Illn
+Sociol Inq
+Sociol Lav
+Sociol Methodol
+Sociol Methods Res
+Sociol Neerl
+Sociol Perspect
+Sociol Q
+Sociol Relig
+Sociol Res
+Sociol Rev
+Sociol Rev [Monogr]
+Sociol Rom
+Sociol Ruralis
+Sociol Sela
+Sociol Soc
+Sociol Soc Res
+Sociol Spectr
+Sociol Sport J
+Sociol Symp
+Sociol Trav
+Sociol Work Occup
+Sociologia
+Sociologus
+Sociology
+Sociometry
+Soemmering Forsch
+Softw Healthc
+Softw Law J
+Sogo Igaku
+Sogo Kango
+Sogo Rinsho
+Soil Biol Biochem
+Soil Sci
+Soil Sci Soc Am J
+Soins
+Soins Cardiol
+Soins Chir
+Soins Chir Gen Spec
+Soins Form Pedagog Encadr
+Soins Gerontol
+Soins Gynecol Obstet Pueric
+Soins Gynecol Obstet Pueric Pediatr
+Soins Pathol Trop
+Soins Pediatr Pueric
+Soins Psychiatr
+Sojourn
+Sojourners
+Solicit J
+Solid State Nucl Magn Reson
+Somat Cell Mol Genet
+Somatic Cell Genet
+Somatosens Mot Res
+Somatosens Res
+Somerset Archaeol Net Hist
+Sonde
+Sonderb Strahlenther Onkol
+Sonderb Z Strahlenther Onkol
+Soobshch Akad Nauk Gruz Ssr
+Soproden
+Sos Laaketiet Aikak
+Sosiologia
+Sotahist Aikak
+Sotilaslaak Aikak
+Sotsiol Issled
+Soud Lek
+Soundings
+Source Notes Hist Art
+South Afr J Demogr
+South Afr J Geol
+South Afr Polit Econ Mon
+South Am Indian Stud
+South Asia Bull
+South Asia Res
+South Asian Rev
+South Atl Q
+South Calif Interdiscip Law J
+South Calif Law Rev
+South Calif Q
+South Calif Rev Law Womens Stud
+South East Report Second Ser
+South Econ J
+South Eur Soc Polit
+South Expos
+South Folk Q
+South Hist
+South Hosp
+South Ill Univ Law J
+South Indian Stud
+South J Educ Res
+South J Optom
+South J Philos
+South Med
+South Med J
+South Pac Bull
+South Report Second Ser Cases Argued Determ Courts Ala Fla La Miss
+South Stud
+South Tex Law J
+South Tex Law Rev
+South Univ Law Rev
+Southampt Med J
+Southeast Asian J Soc Sci
+Southeast Asian J Surg
+Southeast Asian J Trop Med Public Health
+Southeast Eur
+Southeast Eur Ser
+Southeast Geogr
+Southwest Hist Q
+Southwest J Anthropol
+Southwest J Philos
+Southwest Law J
+Southwest Med
+Southwest Rev
+Southwest Univ Law Rev
+Sov Anthropol Archeol
+Sov Arkh
+Sov Econ
+Sov Educ
+Sov Etnogr
+Sov Genet
+Sov Geogr
+Sov Gos Pravo
+Sov J Bioorg Chem
+Sov J Dev Biol
+Sov J Ecol
+Sov Jew Aff
+Sov Law Gov
+Sov Med
+Sov Rev
+Sov Soc
+Sov Stud
+Sov Stud Hist
+Sov Stud Philos
+Sov Union
+Sov Varchebnyii Sb
+Sov Zdravookhr
+Sov Zdravookhr Kirg
+Sovrem Probl Onkol
+Sovrem Probl Tuberk
+Sowjetwiss Naturwiss Beitr
+Soz Praventivmed
+Sozialgesch Med
+Sozialmed Padagog Jugendkd
+Space Commun
+Space Life Sci
+Space Med Med Eng (Beijing)
+Space Policy
+Space Sci Rev
+Space Technol
+Space World
+Spaceflight (Lond)
+Spagna Contemp
+Span J Psychol
+Spat Cogn Comput
+Spat Vis
+Spec Care Dentist
+Spec Collect
+Spec Educ
+Spec Educ Forward Trends
+Spec Law Dig Health Care (Mon)
+Spec Law Dig Health Care Law
+Spec Libr
+Spec Publ Am Philos Soc
+Spec Rep Ser Indian Counc Med Res
+Spec Rep Ser Med Res Counc (G B)
+Spec Top Endocrinol Metab
+Specif Eng
+Spectator
+Spectrochim Acta A Mol Biomol Spectrosc
+Spectrochim Acta [A]
+Spectrum
+Speculations Sci Technol
+Speculum
+Speech Pathol Ther
+Sperimentale
+Spieg Hist
+Spinal Cord
+Spine
+Spine J
+Spinner
+Spirales
+Spis Blg Akad Nauk
+Spitalul
+Splice Life
+Spore
+Sportarzt Ver Sportmed
+Sports Biomech
+Sports Med
+Sportverletz Sportschaden
+Spraw Czynnosci Posiedz Lodz Tow Nauk
+Springer Semin Immunopathol
+Springer Ser Health Care Soc
+Springfielder
+Sr Nurse
+Sr Scholast
+Sri Lanka J Soc Sci
+Sri Lanka Popul Dig
+Srp Arh Celok Lek
+St Anthony Messenger
+St Barnabas Hosp Med Bull
+St Bartholomews Hosp J
+St Johns Law Rev
+St Louis Univ Law J
+St Louis Univ Public Law Rev
+St Luc Med
+St Lukes Hosp Gaz (Guardamangia)
+St Lukes J Theol
+St Marys Hosp Gaz
+St Marys Law J
+St Thomas Law Rev
+St Tomas J Med
+St Tomas Nurs J
+St Vladmirs Theol Q
+Stadion
+Stadion (Koln)
+Stadtehygiene
+Staff Pap Int Monet Fund
+Stain Technol
+Stal
+Stand News
+Stanford Fr Rev
+Stanford J Int Law
+Stanford Law Pol Rev
+Stanford Law Rev
+Stanford Med Bull
+Stanovnistvo
+Stat
+Stat Appl Genet Mol Biol
+Stat Bull Metrop Insur Co
+Stat Bull Metrop Life Found
+Stat Bull Metropol Life Insur Co
+Stat Etud Midi Pyren
+Stat Inf Bull Afr
+Stat J UN Econ Comm Eur
+Stat Med
+Stat Methods Med Res
+Stat Navy Med
+Stat Neerl
+Stat News Pol
+Stat Notes Health Plann
+Stat Notes Jpn
+Stat Report
+Stat Sci
+Stat Szle
+Stat Tidskr
+State Black Am
+State Coverage Initiat Issue Brief
+State Demogr
+State Gov
+State Health Care Am
+State Health Legis Rep
+State Legis
+State Nurs Legis Q
+State Reprod Health Monit
+Stateco
+States Health
+Statist Efterret
+Statist Medd Ser Be Befolk Val
+Statistica
+Statistician
+Statut Calif Dig Meas Calif
+Stem Cells
+Stem Cells Dev
+Stereotact Funct Neurosurg
+Sterile World
+Sterkiana
+Sterne Weltraum
+Steroidologia
+Steroids
+Steroids Lipids Res
+Stetson Law Rev
+Stimmen Zeit
+Stoma (Heidelb)
+Stoma (Lisb)
+Stoma (Thessaloniki)
+Stomatol Chron (Athenai)
+Stomatol DDR
+Stomatol Glas Srb
+Stomatol Mediterr
+Stomatol Vestn
+Stomatol Vjesn
+Stomatol Zpr
+Stomatologia (Athenai)
+Stomatologia (Bucur)
+Stomatologica (Genova)
+Stomatologie
+Stomatologiia (Mosk)
+Stomatologiia (Sofiia)
+Stomatologija
+Stor Contemp
+Stor Crit Psicol
+Stor Lomb
+Stor Med Pop
+Stor Urbana
+Strabismus
+Strada Maestra
+Strahlenschutz Forsch Prax
+Strahlenther Onkol
+Strahlentherapie
+Strahlentherapie [Sonderb]
+Strasb Med
+Strateg Healthc Excell
+Strategy Tactics
+Stratigr Geol Correl
+Strenna Stor Bolognese
+Stress
+Stress Med
+Stroke
+Structure
+Stud Am Renaiss
+Stud Anc Med
+Stud Bibliogr
+Stud Burke Time
+Stud Cartesiana
+Stud Cercet Endocrinol
+Stud Cercet Fiziol
+Stud Cercet Inframicrobiol
+Stud Cercet Med Interna
+Stud Cercet Neurol
+Stud Cercet Virusol
+Stud Christ Ethics
+Stud Chur Hist
+Stud Cl
+Stud Cl Orient
+Stud Comeniana Hist
+Stud Comp Int Dev
+Stud Demogr
+Stud Eighteenth Cent Cult
+Stud Emigrazione
+Stud Engl Lit
+Stud Eur Thought
+Stud Fam Plann
+Stud Filoz
+Stud Gen (Berl)
+Stud Geogr
+Stud Gesch Krankenhauswesens
+Stud Hastings Cent
+Stud Health Technol Inform
+Stud Hist
+Stud Hist Biol
+Stud Hist Gandensia
+Stud Hist Med
+Stud Hist Med Sci
+Stud Hist Philos Biol Biomed Sci
+Stud Hist Philos Sci
+Stud Hum Ecol
+Stud Islam
+Stud Labour Hist
+Stud Lawyer
+Stud Leibnitiana
+Stud Mater Dziej Nauk Pol Ser B
+Stud Mater Dziejow Nauk Pol
+Stud Med
+Stud Med Popul Subj
+Stud Mediev Cult
+Stud Mitt Gesch Benediktinerorden
+Stud Mycol
+Stud Neophilol
+Stud Neuroanat
+Stud Papyrol
+Stud Philol
+Stud Philos Med
+Stud Profertility Ser
+Stud Psychol (Bratisl)
+Stud Relig
+Stud Renaissance
+Stud Romanticism
+Stud Rosenthaliana
+Stud Russ Econ Dev
+Stud Scott Lit
+Stud Soc Sci West Ga Coll
+Stud Sov Thought
+Stud Stor
+Stud Surf Sci Catal
+Stud Third World Soc
+Stud Tokugawa Inst
+Stud Voltaire 18th Century
+Studenterraad Med
+Studi Emigr
+Studi Filol Ital
+Studi Fr
+Studi Ital Filol Cl
+Studi Sassar
+Studi Sociol
+Studi Veneziani
+Studia
+Studia Demogr
+Studies
+Studime Hist
+Studium
+Study Encounter
+Study Sess Can Cathol Hist Assoc
+Subacute Care
+Subcell Biochem
+Subsid Med
+Subst Abus
+Subst Alcohol Actions Misuse
+Subst Use Misuse
+Sucasnist
+Sud Med Chir
+Sud Med Ekspert
+Sudan J Popul Stud
+Sudan Med J
+Sudan Notes Rec
+Suddtsch Optikerztg
+Sudhoffs Arch Gesch Med Naturwiss
+Sudhoffs Arch Vierteljahrsschr Gesch Med Naturwiss Pharm Math
+Sudhoffs Arch Z Wissenschaftsgesch
+Sudhoffs Arch Z Wissenschaftsgesch Beih
+Sudosteuropa
+Suffield Tech Note Can Suffield Exp Stn Ralston Alta
+Suffield Tech Pap Can Suffield Exp Stn Ralston Alta
+Suffolk Transnatl Law R
+Suffolk Univ Law Rev
+Suicide
+Suicide Life Threat Behav
+Summ Shute Inst
+Sun
+Sunday Times
+Sunday Times Magazine
+Suom Apteenkkaril
+Suom Hammaslaak Toim
+Suom Hammaslaakarilehti
+Suom Laakaril
+Suom Tiedeakat Toim A5 Medica Anthropol
+Superv Manage
+Superv Nurse
+Suppl Clin Neurophysiol
+Suppl Eur J Neurosci
+Suppl Int J Gynecol Obstet
+Suppl J Med Oncol Tumor Pharmacother
+Suppl Public Health Rep
+Suppl Thromb Haemost
+Suppl Tumori
+Support Care Cancer
+Supreme Court Rev
+Surg Annu
+Surg Bus
+Surg Circ Lett U S Army Far East Command Med Sect
+Surg Clin North Am
+Surg Endosc
+Surg Forum
+Surg Gastroenterol
+Surg Gynecol Obstet
+Surg Infect (Larchmt)
+Surg Innov
+Surg Laparosc Endosc
+Surg Laparosc Endosc Percutan Tech
+Surg Neurol
+Surg Oncol
+Surg Oncol Clin N Am
+Surg Radiol Anat
+Surg Rounds
+Surg Technol
+Surg Technol Int
+Surg Today
+Surgeon
+Surgery
+Surv Biol Prog
+Surv Immunol Res
+Surv Methodol
+Surv Ophthalmol
+Surv Synth Pathol Res
+Surv World Obstet Gynecol (Jpn)
+Surveill Soc
+Survey (Lond)
+Surviv News (Atlanta Ga)
+Survival (Lond)
+SusPop News
+Suvr Med (Sofiia)
+Sveikatos Apsauga
+Sven Farm Tidskr
+Sven Geogr Arsb
+Sven Juristtidn
+Sven Lakartidn
+Sven Linnesallskap Arsskr
+Sven Med Tidskr
+Sven Tandlak Tidskr
+Sven Tidskr
+Sver Tandlakarforb Tidn
+Swasth Hind
+Swed Am Hist Q
+Swed Dent J
+Swed Dent J Suppl
+Swed Pioneer Hist Q
+Swiss Biotech
+Swiss Dent
+Swiss Med Wkly
+Swiss Surg
+Swiss Surg Suppl
+Syd Law Rev
+Sydn Univ Med J
+Sydsven Medicinhist Sallsk Arsskr
+Sydsven Medicinhist Sallsk Arsskr Suppl
+Syesis
+Sygeplejersken
+Sykepl Fag
+Sykepleien
+Symb Oslo
+Symbiosis
+Symp Fundam Cancer Res
+Symp Oral Sens Percept
+Symp Pharmacol Ther Toxicol Group
+Symp Ser Soc Appl Microbiol
+Symp Soc Dev Biol
+Symp Soc Exp Biol
+Symp Swed Nutr Found
+Synapse
+Synlett
+Synth Philos
+Synthese
+Synthesis (Mass)
+Syracuse Law Rev
+Syst Appl Microbiol
+Syst Biol
+Syst Entomol
+Syst Parasitol
+Syst Res
+Syst Zool
+Szazadok
+Szczecin Tow Nauk Wydz Nauk Lek
+Szemeszet
+TB HIV
+TDR News
+TEC Bull (Online)
+TIC
+TID Rep
+TIT J Life Sci
+TMJ Update
+TVZ
+Tablet
+Taehan Chikkwa Uisa Hyophoe Chi
+Taehan Kan Hakhoe Chi
+Taehan Kanho
+Taehan Kanho Hakhoe Chi
+Taehan Naekwa Hakhoe Chapchi
+Taehan Oekwa Hakhoe Chapchi
+Taehan Pinyogikwa Hakhoe Chapchi
+Taehan Sanbuinkwa Hakhoe Chapchi
+Taehan Uihak Hyophoe Chi
+Taiwan Yi Xue Hui Za Zhi
+Takamine Kenkyusho Nenpo
+Talanta
+Tampa Bay Hist
+Tandlaegebladet
+Tandlaegernes Tidsskr
+Tandlakartidningen
+Tandteknikern
+Tani Girisim Radyol
+Tannlaeknabladid
+Tanpakushitsu Kakusan Koso
+Tanzan Notes Rec
+Tar Heel Nurse
+Targeted Diagn Ther
+Taxon
+TeLindes Oper Gynecol Updates
+Teach Coll Rec
+Teach In
+Teach Learn Med
+Teach Notes Popul
+Teach Philos
+Teach Polit Sci
+Teach Psychol
+Teach Sociol
+Tech Belge Prothese Dent
+Tech Bull Dep Army
+Tech Bull Regist Med Technol
+Tech Bull United States Veterans Admin
+Tech Coloproctol
+Tech Doc Rep ARL TDR
+Tech Doc Rep Arct Aeromed Lab US
+Tech Doc Rep SAMTDR USAF Sch Aerosp Med
+Tech Doc Rep U S Air Force Syst Command Electron Syst Div
+Tech Eau Assainissement
+Tech Hand Up Extrem Surg
+Tech Hosp Med Soc Sanit
+Tech Man US Army Biol Lab
+Tech Manuscr US Army Biol Lab
+Tech Note Arct Aeromed Lab (US)
+Tech Note U S Natl Aeronaut Space Adm
+Tech Orthop
+Tech Rep Arct Aeromed Lab US
+Tech Rep Brookhaven Natl Lab
+Tech Rep CRDLR US Army Chem Res Dev Lab
+Tech Rep CWLR US Army Chem Warf Lab
+Tech Rep NAVTRADEVCEN
+Tech Rep NY Nav Shipyard Mater Lab
+Tech Rep SAM-TR
+Tech Urol
+Tech Vasc Interv Radiol
+Technikgesch Einzeldarst
+Technikgeschichte
+Technol Cancer Res Treat
+Technol Cult
+Technol Eval Cent Asses Program Exec Summ
+Technol Forecast Soc Change
+Technol Health Care
+Technol Rev
+Technol Soc
+Technologia
+Technology
+Tecnologica
+Tecnologica MAP Suppl
+Tel Aviver Jahrb Dtsch Gesch
+Telecomm Policy
+Telemed J
+Telemed J E Health
+Telemed Telehealth Netw
+Telemed Today
+Telemed Virtual Real
+Tellus B Chem Phys Meteorol
+Temas Odontol
+Temas Poblac
+Temple Dent Rev
+Temple Int Comp Law J
+Temple Law Q
+Temple Law Rev
+Temps Mod
+Tenn Code Annot Tenn
+Tenn Folk Soc Bull
+Tenn Hist Q
+Tenn Law Rev
+Tenn Med
+Tenn Nurse
+Tenn Publ Welfare Rec
+Tenn Stud Lit
+Teor Prak Fiz Kult
+Teor Prax Teles Vychovy
+Tequesta
+Ter Arkh
+Terapia
+Teratog Carcinog Mutagen
+Teratology
+Terr Incogn
+Terra
+Terramycine Inf
+Terrorism
+Tetrahedron
+Tetrahedron Asymmetry
+Tetrahedron Lett
+Tex Dent Assist Assoc Bull
+Tex Dent J
+Tex Heart Inst J
+Tex Hosp
+Tex Int Law J
+Tex J Pharm
+Tex J Sci
+Tex J Women Law
+Tex Law Rev
+Tex Med
+Tex Mon
+Tex Nurs
+Tex Rep Biol Med
+Tex Rev Law Polit
+Tex State J Med
+Tex Tech Law Rev
+Texana
+Texas Bar J
+Texas Stud Lit Lang
+Text Rent
+Textos NEPO
+Textual Pract
+Thai J Nurs
+Thalassia Salentina
+Theatre Surv
+Theol Dig
+Theol Stud
+Theol Sz
+Theol Today
+Theor Appl Genet
+Theor Biol Med Model
+Theor Chim Acta
+Theor Med
+Theor Med Bioeth
+Theor Popul Biol
+Theor Prax Korperkult
+Theor Surg
+Theoria
+Theory Decis
+Theory Psychol
+Theory Soc
+Ther Apher
+Ther Apher Dial
+Ther Ber
+Ther Drug Monit
+Ther Ggw
+Ther Hung
+Ther Immunol
+Ther Nova
+Ther Recreation J
+Ther Umsch
+Ther Umsch Med Bibliogr
+Therapeutikon
+Therapeutique
+Therapie
+Therapiewoche
+Theriaca
+Theriogenology
+Third Repub
+Third World Plann Rev
+Third World Q
+ThisWeek
+Thomas Jefferson Law Rev
+Thomist
+Thorac Cardiovasc Surg
+Thorac Surg Clin
+Thorax
+Thoraxchir Vask Chir
+Thoraxchirurgie
+Thought
+Thromb Diath Haemorrh
+Thromb Diath Haemorrh Suppl
+Thromb Haemost
+Thromb J
+Thromb Res
+Thromb Res Suppl
+Thurgood Marshall Law Rev
+Thymus
+Thyroid
+Thyroidology
+Tianjin Yi Yao
+Tibet News Rev
+Ticitl
+Tid Tann
+Tidskr Mil Halsov
+Tidskr Sjukvardspedagog
+Tidskr Sver Sjukskot
+Tidsskr Nor Laegeforen
+Tidsskr Prakt Tandlaeg
+Tidsskr Rettsvitenskap
+Tidsskr Samfunnsforsk
+Tidsskr Sygepl
+Tidsskr Sykepl
+Tidsskr Tandlaeger
+Tierarztl Prax
+Tierarztl Prax Ausg G Grosstiere Nutztiere
+Tierarztl Prax Ausg K Klientiere Heimtiere
+Tierarztl Prax Suppl
+Tierarztl Umsch
+Tiers Monde
+Tijdschr Bejaarden Kraam Ziekenverzorging
+Tijdschr Diergeneeskd
+Tijdschr Econ Soc Geogr
+Tijdschr Entomol
+Tijdschr Filos
+Tijdschr Gastroenterol
+Tijdschr Geneeskd
+Tijdschr Gerontol Geriatr
+Tijdschr Geschied
+Tijdschr Geschied Geneeskd Natuurwet Wiskd Tech
+Tijdschr Kindergeneeskd
+Tijdschr Soc Geneeskd
+Tijdschr Soc Geschied
+Tijdschr Soc Gezondheidsz
+Tijdschr Soc Wet
+Tijdschr Sociol
+Tijdschr Voor Tandheelkd
+Tijdschr Voor Ziekenverpl
+Tijdschr Ziekenverpl
+Tikkun
+Timarit Hjukrunarfel Isl
+Time
+Timeline
+Timely Top Med Cardiovasc Dis
+Times
+Times (Lond)
+Timisoara Med
+Tip Fak Mecm
+Tip Tarihi Arastirmalari
+Tirol Heim
+Tirol Heimatbl
+Tissue Antigens
+Tissue Cell
+Tissue Eng
+Tob Control
+Todays Cathol Teach
+Todays Christ Dr
+Todays FDA
+Todays Health
+Todays Nurs Home
+Todays OR Nurse
+Todays Surg Nurse
+Toho Igakkai Zasshi
+Tohogaku
+Tohoku Igaku Zasshi
+Tohoku J Exp Med
+Tohoku Shika Daigaku Gakkai Shi
+Tokai J Exp Clin Med
+Tokoginecol Pract
+Tokushima J Exp Med
+Tokyo Igaku
+Tokyo Ika Shika Daigaku Iyo Kizai Kenkyusho Hokoku
+Tokyo Jikeikai Ika Daigaku Zasshi
+Tonan Ajia Kenkyu
+Tongmul Hakhoe Chi
+Top Clin Nurs
+Top Clin Nutr
+Top Curr Chem
+Top Emerg Med
+Top HIV Med
+Top Health Care Financ
+Top Health Inf Manage
+Top Health Rec Manage
+Top Hosp Law
+Top Hosp Pharm Manage
+Top Magn Reson Imaging
+Top Probl Psychother
+Top Spinal Cord Inj Rehabil
+Top Stroke Rehabil
+Tor J Theol
+Tor Star
+Torace
+Torah U Madda J
+Torax
+Torreon Med
+Tort Insur Law J
+Tort Stat Tanulm
+Tort Trial Insur Pract Law J
+Tortenelmi Sz
+Touchstone (Nashv)
+Toulouse Med
+Toung Pao
+Tourbe Philos
+Touro Law Rev
+Town Ctry Plann
+Town Hall J
+Town Plan Rev
+Toxic Rep Ser
+Toxic Subst Mech
+Toxicol Appl Pharmacol
+Toxicol Eur Res
+Toxicol In Vitro
+Toxicol Ind Health
+Toxicol Lett
+Toxicol Pathol
+Toxicol Rev
+Toxicol Sci
+Toxicologist
+Toxicology
+Toxicon
+Toyoshi Kenkyu
+Tr Akad Med Nauk Sssr Mosc Inst Grudn Khir
+Tr Akad Nauk SSSR Inst Genet
+Tr Fiziol Lab Akad I P Pavlova
+Tr Inst Biol
+Tr Inst Fiz Akad Nauk Gruz Ssr
+Tr Inst Fiziol Im I P Pavlova
+Tr Inst Im Pastera
+Tr Inst Morfol Zhivotn An Severtsova
+Tr Inst Norm Patol Fiziol
+Tr Latv Padomju Soc Repub Zinat Akad Mikrobiol Inst
+Tr Leningr Inst Epidemiol Mikrobiol
+Tr Leningr Nauchnoissled Inst Epidemiol Mikrobiol
+Tr Leningr Sanitarnogig Med Inst
+Tr Mosk Nauchnoissled Inst Epidmiol Mikrobiol
+Trab Inst Cajal
+Trab Inst Cajal Invest Biol
+Trace Elem Med
+Track Rep
+Tractrix
+Tradimus
+Traditio
+Tradition
+Traffic
+Traffic Inj Prev
+Train Dev J
+Train Sch Bull (Vinel)
+Trained Nurse Hosp Rev
+Training
+Trans ASAE
+Trans Action
+Trans Am Acad Insur Med
+Trans Am Acad Ophthalmol Otolaryngol
+Trans Am Assoc Genitourin Surg
+Trans Am Clin Climatol Assoc
+Trans Am Coll Cardiol
+Trans Am Laryngol Assoc
+Trans Am Laryngol Rhinol Otol Soc
+Trans Am Microsc Soc
+Trans Am Neurol Assoc
+Trans Am Nucl Soc
+Trans Am Ophthalmol Soc
+Trans Am Otol Soc
+Trans Am Soc Artif Intern Organs
+Trans Am Soc Neurochem
+Trans Am Soc Ophthalmol Otolaryngol Allergy
+Trans Amer Fish Soc
+Trans Annu Meet Am Bronchoesophagol Assoc
+Trans Annu Meet Natl Tuberc Assoc
+Trans Assoc Am Physicians
+Trans Assoc Ind Med Off
+Trans Assoc Life Insur Med Dir Am
+Trans Aust Coll Ophthalmol
+Trans Aust Med Congr
+Trans Br Soc Hist Pharm
+Trans Br Soc Study Orthod
+Trans Bristol Glos Archaeol Soc
+Trans Camb Bibliogr Soc
+Trans Can Opthalmolog Soc
+Trans Engl Ceram Circle
+Trans Essex Archaeol Soc
+Trans Eur Orthod Soc
+Trans Hist
+Trans Hunter Soc
+Trans Ill State Acad Sci
+Trans Indiana Acad Ophthalmol Otolaryngol
+Trans Inst Br Geogr
+Trans Inst Indian Geogr
+Trans Int Conf Endod
+Trans Int Conf Oral Surg
+Trans Jew Hist Soc Engl
+Trans Jpn Soc Aeronaut Space Sci
+Trans Kans Acad Sci
+Trans Lancaster Cheshire Antiq Soc
+Trans Leicester Archaeol Hist Soc
+Trans Med Soc Lond
+Trans Meet Am Surg Assoc Am Surg Assoc
+Trans N Engl Obstet Gynecol Soc
+Trans N Y Acad Sci
+Trans Natl Saf Congr
+Trans New Orleans Acad Ophthalmol
+Trans Ophthalmol Soc Aust
+Trans Ophthalmol Soc N Z
+Trans Ophthalmol Soc U K
+Trans Opthal Soc U K
+Trans Pa Acad Ophthalmol Otolaryngol
+Trans Pac Coast Obstet Gynecol Soc
+Trans Pac Coast Otoophthalmol Soc Annu Meet
+Trans Proc Am Philol Assoc
+Trans R Hist Soc
+Trans R Sch Dent Stockh Umea
+Trans R Soc Can
+Trans R Soc Edinb Earth Sci
+Trans R Soc Trop Med Hyg
+Trans Rep Liverp Med Inst
+Trans Sect Ophthalmol Am Acad Ophthalmol Otolaryngol
+Trans Sect Otolaryngol Am Acad Ophthalmol Otolaryngol
+Trans Soc Occup Med
+Trans South Cent Sect Am Urol Assoc
+Trans Southeast Sect Am Urol Assoc
+Trans St Johns Hosp Dermatol Soc
+Trans Stud Coll Physicians Phila
+Trans West Sect Am Urol Assoc
+Transcult Psychiatry
+Transfus Apher Sci
+Transfus Clin Biol
+Transfus Med
+Transfus Med Rev
+Transfus Sci
+Transfusion
+Transfusion (Paris)
+Transgenic Res
+Transp Theory Stat Phys
+Transpl Immunol
+Transpl Infect Dis
+Transpl Int
+Transplant Bull
+Transplant Proc
+Transplant Rev
+Transplant Rev (Orlando)
+Transplant Sci
+Transplantation
+Trauma Violence Abuse
+Trav Hum
+Trav Lab Matiere Med Pharm Galenique Fac Pharm Paris
+Trav Sci Cherch Serv Sante Armees
+Trav Soc Pharm Montp
+Treat Endocrinol
+Treat Guidel Med Lett
+Treat Respir Med
+Treat Rev
+Treat Serv Bull
+Treatmentupdate
+Tree Physiol
+Trends Amplif
+Trends Analyt Chem
+Trends Biochem Sci
+Trends Biotechnol
+Trends Cardiovasc Med
+Trends Cell Biol
+Trends Cogn Sci
+Trends Ecol Evol
+Trends Endocrinol Metab
+Trends Genet
+Trends Health Care Law Ethics
+Trends Hist
+Trends Immunol
+Trends Microbiol
+Trends Mol Med
+Trends Neurosci
+Trends Parasitol
+Trends Pharmacol Sci
+Trends Plant Sci
+Trends Tech Contemp Dent Lab
+Trial
+Trial Lawyers Q
+Trials
+Triangle
+Triangulo Rev Sandoz Cienc Med
+Trib Int Womens Trib Cent
+Trib Odontol (B Aires)
+Trib Odontol (Guanabara)
+Trimest Econ
+Triumph
+Trodent
+Trop Anim Health Prod
+Trop Biomed
+Trop Dis Bull
+Trop Doct
+Trop Gastroenterol
+Trop Geogr Med
+Trop Heal
+Trop J Obstet Gynaecol
+Trop Med Int Health
+Trop Med Parasitol
+Tropenmed Parasitol
+Trudy Mosc Russ Vsesoiuznyi Nauchno Issled Vitam Inst
+Trudy Vsesoiuznoe Fiziol Obshchestvo Im I P Pavlov Orenb Otd
+Trustee
+Trusts Estates
+Tsa Chih Gaoxiong Yi Xue Yuan Tong Xue Hui
+Tsitol Genet
+Tsitologiia
+Tsukuba Daigaku Jinbun Chirigaku Kenkyu
+Tsurumi Shigaku
+Tuber Lung Dis
+Tuberc Airborne Dis Wkly
+Tubercle
+Tuberculol Thorac Dis
+Tuberculosis (Edinb)
+Tuberk Grenzgeb Einzeldarst
+Tuberk Kerdesei
+Tuberk Toraks
+Tuberk Tudobetegsegek
+Tuberkulosearzt
+Tuberkuloza
+Tuberkulozis
+Tufts Dent Outlook
+Tufts Folia Med
+Tufts Health Sci Rev
+Tulane Law Rev
+Tulsa Law J
+Tumor Res
+Tumori
+Tumour Biol
+Tunis Med
+Turk Hemsire Derg
+Turk Hij Deney Biyol Derg
+Turk Hij Tecr Biyol Derg
+Turk J Gastroenterol
+Turk J Pediatr
+Turk Ortodonti Derg
+Turk Psikiyatri Derg
+Turk Tip Cemiy Mecm
+Turk Tip Dernegi Derg
+Turnbull Libr Rec
+Turun Hist Ark (Finl)
+Twin Res
+Twin Res Hum Genet
+TydsKr Rasse Aangel
+Tydskrif Hedendaagse Romeins Holandse Reg
+U S Armed Forces Med J
+U S Code Annot U S
+U S Nav Med Bull
+U S Rep U S Supreme Court
+UCLA Forum Med Sci
+UCLA Law Rev
+UCLA Pac Basin Law J
+UCLA Rep
+UCLA Symp Mol Cell Biol
+UCLA Womens Law J
+UFSI Rep
+UJCD Union Jeunes Chir Dent
+UMKC Law Rev
+UN Chron
+UN Mon Chron
+UNA Commun
+UNA Nurs J
+UNDP News
+UNESCO Cour
+UNICEF News
+UNOS Update
+UR Rep
+US Cathol
+US Healthc
+US Long Term Rev
+US Med
+US Navy Med
+US Navy Med News Lett
+US News World Rep
+US Pharm
+US Statut Large
+US Supreme Court Rep
+USA Today
+USA Weekend
+USAF Med Serv Dig
+USAID Dev
+USAID Highlights
+USQR Union Semin Q Rev
+UWFL Rep
+Uch Zap Inst Farmakol Khimioter AMN SSSR
+Uchenye Zap Mosk Meditsinskii Inst
+Uchenye Zap Seriia Biolog Leningr Univ
+Uchu Koku Kankyo Igaku
+Uddannelse
+Udim
+Ugeskr Laeger
+Uirusu
+Uisahak
+Ukr Biokhim Zh
+Ukr Bot Z Ukr Bot Tovarystvo
+Ukr Istor Z
+Ukr Q
+Ukr Rev (Lond)
+Ulster Folklife
+Ulster Med J
+Ultramicroscopy
+Ultraschall Med
+Ultrason Imaging
+Ultrason Sonochem
+Ultrasonics
+Ultrasound Med Biol
+Ultrasound Obstet Gynecol
+Ultrasound Q
+Ultrastruct Pathol
+Ulus Travma Acil Cerrahi Derg
+Ulus Travma Derg
+Umsch Wiss Tech
+Undergrad J Philos
+Undersea Biomed Res
+Undersea Hyperb Med
+Unesco Bull Libr
+Unesco Sources
+Unfallchirurg
+Unfallchirurgie
+Unfallheilkunde
+Union Burma J Life Sci
+Union Med Can
+Unitas
+United Evangelical Action
+United Synag Rev
+Univ Ark Little Rock Law J
+Univ B C Law Rev
+Univ Baltimore Law Rev
+Univ Calif Davis Law Rev
+Univ Calif Publ Zool
+Univ Chic Law Rev
+Univ Chic Leg Forum
+Univ Cincinnati Law Rev
+Univ Colo Law Rev
+Univ Dayton Law Rev
+Univ Detroit J Urban Law
+Univ Detroit Law Rev
+Univ Detroit Mercy Law Rev
+Univ Edinb J
+Univ Fla J Law Public Policy
+Univ Fla Law Rev
+Univ Ghana Law J
+Univ Ill Law Forum
+Univ Ill Law Rev
+Univ Kans Law Rev
+Univ Louisv J Fam Law
+Univ Md Law Forum
+Univ Memphis Law Rev
+Univ Miami Entertain Sports Law Rev
+Univ Miami Law Rev
+Univ Mich J Law Reform
+Univ Mich Med Cent J
+Univ Minn Med Bull
+Univ N C News Lett
+Univ N S W Law J
+Univ Newcastle Tyne Med Gaz
+Univ PA Law Rev
+Univ Pa Med Bull
+Univ Pittsbg Law Rev
+Univ Puget Sound Law Rev
+Univ Qld Law J
+Univ Richmond Law Rev
+Univ San Fernando Valley Law Review
+Univ San Francisco Law Rev
+Univ Tasman Law Rev
+Univ Toledo Law Rev
+Univ Tor Dent J
+Univ Tor Fac Law Rev
+Univ Tor Law J
+Univ Toronto Med J
+Univ Toronto Undergrad Dent J
+Univ West Los Angel Law Rev
+Univ West Ont Law Rev
+Univ Wyo Publ
+Universitas (Ghana)
+Universitas (Stuttg)
+Unsere Heim
+Update
+Update Ethics
+Update Natl Minor AIDS Counc
+Update Pediatr Dent
+Ups J Med Sci
+Ups J Med Sci Suppl
+Upsala Lakareforen Forh
+Urban Aff Q
+Urban Geogr
+Urban Health
+Urban Health Newsl
+Urban Hist Rev
+Urban Hist Yearb
+Urban Leag Rev
+Urban Rev
+Urban Soc Change Review
+Urban Stud
+Urban anthropol
+Urbana
+Urbi
+Uremia Invest
+Urol Clin North Am
+Urol Cutaneous Rev
+Urol Int
+Urol Mosc
+Urol Nefrol (Mosk)
+Urol Nurs
+Urol Oncol
+Urol Pol
+Urol Radiol
+Urol Res
+Urol Surv
+Urologe
+Urologe A
+Urologe B
+Urologia
+Urologiia
+Urology
+Ursus
+User Model User-adapt Interact
+Usp Biol Khim
+Usp Fiziol Nauk
+Usp Khim
+Usp Sovrem Biol
+Utafiti
+Utah Code Annot 1953 Utah
+Utah Hist Q
+Utah Law Rev
+Utah Nurse
+Utah Sci
+Utne Read
+Utop Stud
+Uttar Pradesh State Dent J
+VA J Int Law
+VDI Ber
+VINA Q
+Va Bar News
+Va Cavalcade
+Va Dent J
+Va Explor
+Va J Sci
+Va J Soc Policy Law
+Va Law Rev
+Va Mag Hist Biogr
+Va Med
+Va Med Mon (1918)
+Va Med Q
+Va Nurse
+Va Nurse Q
+Va Q Rev
+Va Rev Sociol
+Vaccine
+Vaccine Immun News
+Vaccine Wkly
+Vakbl Biol
+Valparaiso Univ Law Rev
+Valsalva
+Value Health
+Vanc Sun
+Vanderbilt Law Rev
+Vard Nord Utveckl Forsk
+Vardfacket
+Vasa
+Vasa Suppl
+Vasc Dis
+Vasc Endovascular Surg
+Vasc Med
+Vasc Surg
+Vascul Pharmacol
+Vascular
+Vector Borne Zoonotic Dis
+Veja
+Veltro
+Venereology
+Venez Odontol
+Verh Anat Ges
+Verh Dtsch Ges Herz Kreislaufforsch
+Verh Dtsch Ges Inn Med
+Verh Dtsch Ges Kreislaufforsch
+Verh Dtsch Ges Pathol
+Verh Dtsch Ges Rheumatol
+Verh K Acad Geneeskd Belg
+Verh K Vlaam Acad Geneeskd Belg
+Verh Zool Bot Ges Wien
+Vernons Annot Revis Civ Statut State Tex Tex
+Veroff Int Ges Gesch Pharm
+Veroff Morphol Pathol
+Veroff Pathol
+Veroff Schweiz Ges Gesch Med Naturwiss
+Veroff Schweiz Ges Gesch Pharm
+Verpleegkunde
+Versicherungsmedizin
+Versl Meded K Viaam Acad Taal Lett
+Versl Meded K Vlaam Acad Taal Lett
+Vertex
+Vesalius
+Veska
+Vestis Lat Psr Zinat Akad
+Vestn Akad Med Nauk SSSR
+Vestn Akad Nauk SSSR
+Vestn Dermatol Venerol
+Vestn Drevnej Istor (Moskva)
+Vestn Khir Im I I Grek
+Vestn Leningr Univ Geol Geogr
+Vestn Leningr Univ [Biol]
+Vestn Mosk Univ Ser 8 Istor
+Vestn Mosk Univ Ser V
+Vestn Oftalmol
+Vestn Otorinolaringol
+Vestn Rentgenol Radiol
+Vestn Ross Akad Med Nauk
+Vestn Statistiki
+Vestn Venerol Dermatol
+Vet Anaesth Analg
+Vet Bull
+Vet Clin North Am
+Vet Clin North Am Equine Pract
+Vet Clin North Am Exot Anim Pract
+Vet Clin North Am Food Anim Pract
+Vet Clin North Am Large Anim Pract
+Vet Clin North Am Small Anim Pract
+Vet Clin Pathol
+Vet Comp Orthop Traumatol
+Vet Dermatol
+Vet Herit
+Vet Hist
+Vet Historisch Genoot Cah
+Vet Hum Toxicol
+Vet Immunol Immunopathol
+Vet Ital
+Vet J
+Vet Med
+Vet Med (Praha)
+Vet Med Nachr
+Vet Med Nauki
+Vet Med Rev
+Vet Med Small Anim Clin
+Vet Microbiol
+Vet Ophthalmol
+Vet Parasitol
+Vet Pathol
+Vet Pathol Suppl
+Vet Q
+Vet Radiol Ultrasound
+Vet Rec
+Vet Res
+Vet Res Commun
+Vet Surg
+Vet Ther
+Veterinaria
+Veterinarian
+Veterinariia
+Veterinarstvi
+Viata Med
+Viata Med Rev Inf Prof Stiint Cadrelor Medii Sanit
+Viator
+Vibro
+Vic Hist J
+Vic Hist Mag
+Vic Newsl
+Vic Period Newsl
+Vic Period Rev
+Vic Stud
+Vic Vet Proc
+Victimology
+Victor Bostrom Fund Rep
+Vida Nueva
+Vida Odontol
+Vie Lang
+Vie Med
+Vie Med Can Fr
+Vie Med Evolut Med Ther
+Vie Sante
+Vierteljahresberichte
+Vierteljahrschr Soz Wirtschaftsgesch
+Vierteljahrsh Zeitgesch
+Vierteljahrsschr Schweiz Sanitatsoff
+Vigilando
+Viitorul Soc
+Vikalpa
+Village Voice
+Villanova Law Rev
+Vingtieme Siecle
+Violence Against Women
+Violence Update
+Violence Vict
+Viral Immunol
+Virchows Arch
+Virchows Arch A Pathol Anat Histol
+Virchows Arch A Pathol Anat Histopathol
+Virchows Arch A Pathol Pathol Anat
+Virchows Arch B Cell Pathol
+Virchows Arch B Cell Pathol Incl Mol Pathol
+Virchows Arch Pathol Anat Physiol Klin Med
+Virol J
+Virol Monogr
+Virologie
+Virology
+Virtual Mentor
+Virus
+Virus Genes
+Virus Inf Exch Newsl South East Asia West Pac
+Virus Res
+Virus Res Suppl
+Vis Neurosci
+Vision Res
+Vita Hum Int Z Lebensalterforsch
+Vitae Scholasticae
+Vital Health Stat 1
+Vital Health Stat 10
+Vital Health Stat 11
+Vital Health Stat 13
+Vital Health Stat 14
+Vital Health Stat 2
+Vital Health Stat 20
+Vital Health Stat 21
+Vital Health Stat 23
+Vital Health Stat 3
+Vital Health Stat 4
+Vital Health Stat 5
+Vital Signs
+Vital Speeches Day
+Vitalst Zivilisationskr
+Vitam Horm
+Vitam Horm Leipzig
+Vivre Autrement
+Vnitr Lek
+Voeding
+Voen Istor Zh
+Voen Med Delo
+Voen Med Zh
+Vogue
+Voix Dent
+Voix Femme
+Vojen Zdrav Listy
+Vojenskozdrav Knih
+Vojnois Glas
+Vojnosanit Pregl
+Volkskunde
+Volunt Action Leadersh
+Volunt Adm
+Volunt Leader
+Vopr Biokhim Mozga
+Vopr Ekon
+Vopr Eksper Klinicheskoi Urol
+Vopr Elektropatol Elektrotravmatizma Elektrobezop
+Vopr Filos
+Vopr Fiziol
+Vopr Geogr
+Vopr Istor
+Vopr Istor Estestvozn Tekh
+Vopr Istor KPSS
+Vopr Klin Lecheniia Zlokachestvennykh Novoobraz
+Vopr Kurortol Fizioter Lech Fiz Kult
+Vopr Med Khim
+Vopr Med Virusol
+Vopr Neirokhir
+Vopr Okhr Materin Det
+Vopr Onkol
+Vopr Patol Serdechno Sosud Sist
+Vopr Pitan
+Vopr Psikhiatr Nevropatol
+Vopr Psikhol
+Vopr Revm
+Vopr Virusol
+Vopros Stat
+Vorgange
+Vox Sang
+Vrach Delo
+Vt Hist
+Vt Hist News
+Vt Law Rev
+Vt Regist Nurse
+Vt Statut Annot Vt
+Vutr Boles
+Vyziva Lidu
+W V Dent J
+W V Med J
+W Va Hist
+WADC Tech Note United States Air Force Wright Air Dev Cen Day Ohio
+WADC Tech Rep United States Air Force Wright Air Dev Cent Day Ohio
+WASH Rep
+WDA J
+WE Int
+WHO AIDS Tech Bull
+WHO Chron
+WHO Feature
+WHO Offset Publ
+WHO Reg Publ Eur Ser
+WIN News
+WMJ
+WORLD
+WRI Issues Ideas
+Waage
+Wakayama Med Rep
+Wake Forest Law Rev
+Waking Sleeping
+Wall St J (East Ed)
+Wall St J [Midwest Ed]
+War Hung
+War Soc
+Warasan Prachakon Lae Sangkhom
+Warta Demografi
+Warwickshire Hist
+Wash Drug Device Lett
+Wash Law Rev
+Wash Lee Law Rev
+Wash Memo Alan Guttmacher Inst
+Wash Mon
+Wash Nurse
+Wash Q
+Wash Rep Med Health
+Wash State Dent J
+Wash State J Nurs
+Wash Times
+Wash Univ Dent J
+Wash Univ J Urban Contemp Law
+Wash Univ Law Q
+Washburn Law J
+Washington Post
+Washingtonian
+Waste Manag
+Waste Manag Res
+Water Air Soil Pollut
+Water Environ Res
+Water Res
+Water Resour Res
+Water Sci Technol
+Watermark
+Watermark (Arch Libr Hist Health Sci)
+Way
+Wayne Law Rev
+Weather Vane
+Weekend Mag
+Weekly Mail
+Wehrmed Mitt
+Wehrmed Monatsschr
+Wei Sheng Wu Xue Bao
+Wei Sheng Yan Jiu
+Weleda Korrespondenzbl Aerzte
+Welf Rev
+Welsh Hist Rev
+Weltwirtsch Arch
+Wen Wu
+Wereld Zending
+Wesleyan Theol J
+West Afr J Med
+West Afr J Pharmacol Drug Res
+West Afr Med J
+West Afr Med J Niger Med Dent Pract
+West Afr Med J Niger Pract
+West Afr Pharm
+West Can J Anthropol
+West Dent Soc Bull
+West Engl Med J
+West Eur Polit
+West Folk
+West Hist Q
+West Hum Rev
+West Ill Reg Stud
+West Indian Med J
+West J Black Stud
+West J Med
+West J Med Surg
+West J Nurs Res
+West J Speech Commun
+West J Surg Obstet Gynecol
+West Med Med J West
+West New Engl Law Rev
+West Ont Law Rev
+West Pa Hist Mag
+West Polit Q
+West State Univ Law Rev
+West States Jew Hist
+West States Jew Hist Q
+West Tenn Hist Soc Pap
+West Tex Hist Asso Year B
+West VA Law Rev
+West's Atl Report
+Westminst Inst Rev
+Wests Annot Calif Codes Calif
+Wests Annot Indiana Code
+Wests Calif Report
+Wests Fed Rep
+Wests Fed Suppl
+Wests Fla Statut Annot Fla
+Wests N Y Suppl
+Wests North East Rep
+Wests North West Rep
+Wests Pac Report
+Wests Revis Code Wash Annot Wash State
+Wests South East Report
+Wests South Report
+Wests South West Report
+Wests Supreme Court Report
+Wests Wis Statut Annot Wis
+Wet Tijd
+Whittier Law Rev
+Whole Earth Rev
+Wiad Lek
+Wiad Lek Wars Pol 1948
+Wiad Parazytol
+Wiad Stat (Warsaw)
+Wiederherstellungschir Traumatol
+Wien Arch Psychol Psychiatr Neurologie
+Wien Beitr Chir
+Wien Beitr Dermatol
+Wien Beitr Geburtshilfe Gynakol
+Wien Beitr Gesch Neuzeit
+Wien Beitr Hals Nasen Ohrenheilkd
+Wien Beitr Hyg
+Wien Beitr Kinderheilkd
+Wien Beitr Zahnheilkd
+Wien Geschichtsbl
+Wien Klin Wochenschr
+Wien Klin Wochenschr Suppl
+Wien Med Wochenschr
+Wien Med Wochenschr Beih
+Wien Med Wochenschr Suppl
+Wien Stud Med Gesch Philos
+Wien Tierarztl Monatsschr
+Wien Z Inn Med
+Wien Z Kunde Morgenl
+Wien Z Nervenheilkd Grenzgeb
+Wies Wspolcz
+Wijsgerig Perspect Maatsch Wet
+Wilderness Environ Med
+Wildl Dis
+Wildl Soc Bull
+Willamette Law J
+Willamette Law Rev
+William Mary Bill Rights J
+William Mary J Women Law
+William Mary Law Rev
+William Mary Q
+William Mitchell Law Rev
+Wilson Libr Bull
+Wilson Q
+Windows Time
+Winnipeg Clin Q
+Winterthur Portf
+Wirtsch Stat
+Wirtschaftsdienst
+Wirtschaftswissenschaft
+Wirtschaftswissenschaftliches Stud
+Wis Acad Rev
+Wis Bar Bull
+Wis Dent Assoc J
+Wis L Rev
+Wis Mag Hist
+Wis Med J
+Wis Sess Laws Wis
+Wis Womens Law J
+Wisc Sociol
+Wiss Beitr Gesch Seelenheilkd
+Wiss Beitr Martin Luther Univ Halle Wittenberg
+Wiss Dienst Sudosteur
+Wiss Glaube
+Wiss Veroff Dtsch Ges Ernahr
+Wiss Z Ernst Moritz Arndt Univ [Ges Sprachwiss]
+Wiss Z Ernst Moritz Arndt Univ [Math]
+Wiss Z Friedrich Schiller Univ Jena Ges Sprachwiss
+Wiss Z Humboldt Univ Berl (Ges Sprachwiss)
+Wiss Z Humboldt Univ Berl [Math Naturwiss]
+Wiss Z Karl Marx Univ Leipzig [Ges Sprachwiss]
+Wiss Z Karl Marx Univ Math Naturwiss
+Wiss Z Wilhelm Pieck Univ Rostock [Ges Sprachwiss]
+Wistar Inst Symp Monogr
+Wkly Epidemiol Rec
+Wkly Law Rep
+Wolfenbutteler Renaiss Mitt
+Woman Physician
+Women 2000
+Women AIDS
+Women Action
+Women Alive
+Women Crim Justice
+Women Environ
+Women Envis
+Women Health
+Women Lawyers J
+Women Polit
+Women Ther
+Women Vietnam
+Women's Rights Law Report
+Womens Annu
+Womens Health
+Womens Health Data Book
+Womens Health Issues
+Womens Health J
+Womens Health Newsl
+Womens Health Update
+Womens Health Wkly
+Womens Stud
+Womens Stud Forum
+Womens Stud Int Forum
+Womens Stud Int Q
+Womens Watch
+Womens World
+Worcester Med News
+Word World
+Work
+Work Environ Health
+Work Occup
+Work Pap Mag
+Work Pap New Soc
+Work Stress
+Work Woman
+Worklife
+World AIDS Day Features
+World AIDS Day Newsl
+World Archaeol
+World Bank Econ Rev
+World Bank Res News
+World Bank Res Obs
+World Citz News
+World Conserv
+World Dev Forum
+World Educ Rep
+World Environ Rep
+World Futures
+World Health
+World Health Forum
+World Health Organ Tech Rep Ser
+World Health Stat Q
+World Health Stat Rep
+World Hosp
+World Hosp Health Serv
+World Ir Nurs
+World J Biol Psychiatry
+World J Gastroenterol
+World J Orthod
+World J Surg
+World J Surg Oncol
+World J Urol
+World Med
+World Med J
+World Neighb Action
+World Neurol
+World News Maxillofac Radiol
+World Policy J
+World Polit
+World Psychiatry
+World Rev Nutr Diet
+World Review
+World Smoking Health
+World Tob
+World Today
+World Watch
+World Wide Abstr Gen Med
+World Work
+WorldAIDS
+Worlds Poult Sci J
+Worldview
+Wound Repair Regen
+Wurzbg Medizinhist Forsch
+Wurzbg Medizinhist Mitt
+Wyo Nurses Newsl
+Wyo Statut Annot Wyo
+Wythe Cty Hist Rev
+Xaloc
+Xenobiotica
+Xenotransplantation
+Xi Bao Yu Fen Zi Mian Yi Xue Za Zhi
+Xi Psi Phi Q
+Xianggang Hu Li Za Zhi
+Xray Tech
+Y
+Y Rep
+Yad Vashem Stud Eur Jew Catastrophe Resist
+Yakubutsu Seishin Kodo
+Yakugaku Kenkyu
+Yakugaku Zasshi
+Yakushigaku Zasshi
+Yale J Biol Med
+Yale J Health Policy Law Ethics
+Yale J Law Fem
+Yale J Regul
+Yale Law J
+Yale Law Policy Rev
+Yale Univ Libr Gaz
+Yalkut Moreshet
+Yan Jiu Bao Gao
+Yan Ke Xue Bao
+Yan ke xue shu hui kan
+Yao Xue Xue Bao
+Yatros
+Year B Leo Baeck Inst
+Year Immunol
+Yearb Am Philos Soc
+Yearb Conf Lat Am Geogr
+Yearb Ger Am Stud
+Yearb Phys Anthropol
+Yearb Popul Res Finl
+Yeast
+Yeni Tip Tarihi Arastirmalari
+Yi Chuan
+Yi Chuan Xue Bao
+Ying Yang Xue Bao
+Ying Yong Sheng Tai Xue Bao
+Yngre Laeger
+Yod
+Yojana
+Yokohama Med Bull
+Yokufuen Chosa Kenkyu Kiyo
+Yonago Acta Med
+Yonago Igaku Zasshi
+Yonsei Med J
+York State Trad
+Young Child
+Your Child Patient
+Your Okla Dent Assoc J
+Youth Soc
+Yugosl Surv
+Z Aachener Geschichtsver
+Z Aegypt Sprach Altertumskd
+Z Allg Mikrobiol
+Z Allg Wissenschaftstheor
+Z Allgemeinmed
+Z Alternsforsch
+Z Anat Entwicklungsgesch
+Z Angew Bader Klimaheilkd
+Z Arbeitswiss
+Z Arztl Fortbild (Berl)
+Z Arztl Fortbild (Jena)
+Z Arztl Fortbild Beih (Jena)
+Z Arztl Fortbild Qualitatssich
+Z Bayer Landesgesch
+Z Bevolkerungswiss
+Z Bibliothekswes Bibliogr
+Z Biol
+Z Dtsch Altert Dtsch Lit
+Z Dtsch Morgenl Ges
+Z Entwicklungspsychol Padagog Psychol
+Z Erdkundeunterr
+Z Erkr Atmungsorgane
+Z Erkr Atmungsorgane Folia Bronchol
+Z Ernahrungswiss
+Z Ernahrungswiss Suppl
+Z Ethnol
+Z Exp Angew Psychol
+Z Exp Chir
+Z Exp Chir Transplant Kunstliche Organe
+Z Exp Psychol
+Z Flugwiss Weltraumforsch
+Z Fr Spr Lit
+Z Gastroenterol
+Z Gastroenterol Verh
+Z Geburtshilfe Gynakol
+Z Geburtshilfe Neonatol
+Z Geburtshilfe Perinatol
+Z Gerontol
+Z Gerontol Geriatr
+Z Ges Schleswig Holstein Gesch
+Z Gesamte Exp Med
+Z Gesamte Hyg
+Z Gesamte Inn Med
+Z Gesamte Staatswiss
+Z Gesch Arab Islam Wiss
+Z Geschichtswiss
+Z Haut Geschlechtskr
+Z Hautkr
+Z Heereskd
+Z Hist Forsch
+Z Hist Ver Schwaben Neuburg
+Z Hist Ver Steiermark
+Z Hyg Infektionskr
+Z Immun exp ther
+Z Immunitats Allergieforsch
+Z Immunitatsforsch Allerg Klin Immunol
+Z Immunitatsforsch Exp Klin Immunol
+Z Immunitatsforsch Immunobiol
+Z Indukt Abstamm Vererbungsl
+Z Jagdwiss
+Z Kardiol
+Z Kardiol Suppl
+Z Kinder Jugendpsychiatr
+Z Kinder Jugendpsychiatr Psychother
+Z Kinderchir
+Z Kinderchir Grenzgeb
+Z Kinderheilkd
+Z Kirchengesch
+Z Klin Chem Klin Biochem
+Z Klin Med
+Z Klin Psychol Psychiatr Psychother
+Z Klin Psychol Psychopathol Psychother
+Z Klin Psychol Psychother
+Z Krankenpfl
+Z Krebsforsch
+Z Krebsforsch Klin Onkol Cancer Res Clin Oncol
+Z Kreislaufforsch
+Z Kunstgesch
+Z Laryngol Rhinol Otol
+Z Lebensm Unters Forsch
+Z Lymphol
+Z Mag
+Z Med Isotopenforsch Deren Grenzgeb
+Z Med Lab Diagn
+Z Med Labortech
+Z Med Mikrobiol Immunol
+Z Med Phys
+Z Med Psychol
+Z Mensch Vererb Konstitutionsl
+Z Mikrosk Anat Forsch
+Z Morphol Anthropol
+Z Naturforsch B
+Z Naturforsch [B]
+Z Naturforsch [C]
+Z Naturwiss Med Grundlagenforsch
+Z Neurol
+Z Orthop Ihre Grenzgeb
+Z Ostforsch
+Z Papyrologie Epigraphik
+Z Parasitenkd
+Z Philos Forsch
+Z Phys A At Nucl
+Z Physiother
+Z Phytother
+Z Plast Chir
+Z Pola Walki
+Z Polit
+Z Prakt Anasth
+Z Prakt Anasth Wiederbeleb Intensivther
+Z Praventivmed
+Z Psychol Z Angew Psychol
+Z Psychosom Med
+Z Psychosom Med Psychoanal
+Z Psychosom Med Psychother
+Z Psychother Med Psychol
+Z Rechtsmed
+Z Relig Geistesgesch
+Z Rheumaforsch
+Z Rheumatol
+Z Rom Philol
+Z Saugetierkd
+Z Savigny Stift Rechtsgesch Ger Abt
+Z Savigny Stift Rechtsgesch Kanon Abt
+Z Sozialreform
+Z Soziol
+Z Stomatol
+Z Tierphysiol Tierernahr Futtermittelkd
+Z Tierpsychol
+Z Tierzuecht Zuechtungsbiol
+Z Tropenmed Parasitol
+Z Tuberk
+Z Tuberk Erkr Thoraxorg
+Z Unfallchir Versicherungsmed
+Z Unfallchir Versicherungsmed Berufskr
+Z Unfallmed Berufskr
+Z Urol
+Z Urol Nephrol
+Z Ver Lubeck Gesch Altertumskd
+Z Vererbungsl
+Z Versuchstierkd
+Z Vitam Horm Fermentforsch
+Z Wirtsch Sozialwiss
+Z Wirtschgeogr
+Z Wiss Mikrosk
+Z Wiss Zool
+Z Wurttemb Landesgesch
+Z Zellforch Microsk Anat Histochem
+Z Zellforsch Mikrosk Anat
+ZFA
+ZFA (Stuttgart)
+ZPG Report
+ZWR
+Zacchia
+Zahn Mund Kieferheilkd Zentralbl
+Zahnarzt
+Zahnarztebl Baden Wurttemb
+Zahnarztl Gesundheitsdienst
+Zahnarztl Mitt
+Zahnarztl Nachr Sudwurttemb
+Zahnarztl Prax
+Zahnarztl Praxisfuhr
+Zahnarztl Rundsch
+Zahnarztl Welt
+Zahnarztl Welt Zahnarztl Reform Zwr
+Zahnarztl Welt Zahnarztl Rundsch ZWR Zahnarztl Reform
+Zahntechnik (Berl)
+Zahntechnik (Zur)
+Zahntechniker (Basel)
+Zaire Afr
+Zambezia
+Zambia Nurse
+Zambia Nurse J
+Zaragoza
+Zasshi Kanazawa Daigaku Igakubu Juzen Igakkai
+Zasshi Tokyo Ika Daigaku
+Zb Istor
+Zb Lek Fak Kosice
+Zb Vojnomed Akad
+Zb Zgodovino Naravoslovja Teh
+Zdr Delo
+Zdrav Aktual
+Zdrav Prac
+Zdrav Rev Vestn Minist Zdrav
+Zdrav Vestn
+Zdravookhr Beloruss
+Zdravookhr Kirg
+Zdravookhr Ross Fed
+Zdravookhr Turkmenistana
+Zdravookhranenie
+Zdravookhranenie Belarusi
+Zdravookhranenie Kazakhstana
+Zdrow Publiczne
+Zeitgesch
+Zent Arb Arb Beih
+Zentralasiat Stud
+Zentralbl Allg Pathol
+Zentralbl Arbeitsmed
+Zentralbl Arbeitsmed Arbeitsschutz Prophyl
+Zentralbl Arbeitsmed Arbeitsschutz Prophyl Ergonomie
+Zentralbl Bakteriol
+Zentralbl Bakteriol A
+Zentralbl Bakteriol Mikrobiol Hyg [A]
+Zentralbl Bakteriol Mikrobiol Hyg [B]
+Zentralbl Bakteriol Naturwiss
+Zentralbl Bakteriol Parasitenkd Infektionskr Hyg
+Zentralbl Bakteriol [B]
+Zentralbl Bakteriol [Orig A]
+Zentralbl Bakteriol [Orig B]
+Zentralbl Bakteriol [Orig]
+Zentralbl Biol Aerosolforsch
+Zentralbl Chir
+Zentralbl Gynakol
+Zentralbl Haut Geschlechtskr Grenzgeb
+Zentralbl Haut Und Geschlechtskr
+Zentralbl Hyg Umweltmed
+Zentralbl Mikrobiol
+Zentralbl Neurochir
+Zentralbl Pathol
+Zentralbl Phlebol
+Zentralbl Verkehrsmed Verkehrspsychol Luft Raumfahrtmed
+Zentralbl Veterinarmed A
+Zentralbl Veterinarmed B
+Zentralbl Veterinarmed [C]
+Zentralblatt Arbeitsmedizin Arbeitsschutz Erogon
+Zero Popul Growth Natl Rep
+Zest Hist
+Zh Eksp Klin Med
+Zh Evol Biokhim Fiziol
+Zh Mikrobiol Epidemiol Immunobiol
+Zh Nevrol Psikhiatr Im S S Korsakova
+Zh Nevropatol Psikhiatr Im S S Korsakova
+Zh Obshch Biol
+Zh Ushn Nos Gorl Bolezn
+Zh Vopr Neirokhir Im N N Burdenko
+Zh Vyssh Nerv Deiat Im I P Pavlova
+Zhe Jiang Yi Ke Da Xue Xue Bao
+Zhejiang Da Xue Xue Bao Yi Xue Ban
+Zhen Ci Yan Jiu
+Zhi Wu Sheng Li Yu Fen Zi Sheng Wu Xue Xue Bao
+Zhong Nan Da Xue Xue Bao Yi Xue Ban
+Zhong Xi Yi Jie He Xue Bao
+Zhong Xi Yi Jie He Za Zhi
+Zhong Yao Cai
+Zhong Yao Tong Bao
+Zhongguo Dang Dai Er Ke Za Zhi
+Zhongguo Ji Sheng Chong Xue Yu Ji Sheng Chong Bing Za Zhi
+Zhongguo Shi Yan Xue Ye Xue Za Zhi
+Zhongguo Wei Zhong Bing Ji Jiu Yi Xue
+Zhongguo Xiu Fu Chong Jian Wai Ke Za Zhi
+Zhongguo Yao Li Xue Bao
+Zhongguo Yi Liao Qi Xie Za Zhi
+Zhongguo Yi Xue Ke Xue Yuan Xue Bao
+Zhongguo Ying Yong Sheng Li Xue Za Zhi
+Zhongguo Zhen Jiu
+Zhongguo Zhong Xi Yi Jie He Za Zhi
+Zhongguo Zhong Yao Za Zhi
+Zhonghua Bing Li Xue Za Zhi
+Zhonghua Er Bi Yan Hou Ke Za Zhi
+Zhonghua Er Bi Yan Hou Tou Jing Wai Ke Za Zhi
+Zhonghua Er Ke Za Zhi
+Zhonghua Fang She Xian Yi Xue Za Zhi
+Zhonghua Fang She Xue Za Zhi
+Zhonghua Fu Chan Ke Za Zhi
+Zhonghua Gan Zang Bing Za Zhi
+Zhonghua Hu Li Za Zhi
+Zhonghua Jie He He Hu Xi Xi Ji Bing Za Zhi
+Zhonghua Jie He He Hu Xi Za Zhi
+Zhonghua Kou Qiang Ke Za Zhi
+Zhonghua Kou Qiang Yi Xue Za Zhi
+Zhonghua Lao Dong Wei Sheng Zhi Ye Bing Za Zhi
+Zhonghua Liu Xing Bing Xue Za Zhi
+Zhonghua Min Guo Wei Sheng Wu Ji Mian Yi Xue Za Zhi
+Zhonghua Min Guo Wei Sheng Wu Xue Za Zhi
+Zhonghua Min Guo Xiao Er Ke Yi Xue Hui Za Zhi
+Zhonghua Nan Ke Xue
+Zhonghua Nei Ke Za Zhi
+Zhonghua Shao Shang Za Zhi
+Zhonghua Shen Jing Ge Za Zhi
+Zhonghua Shen Jing Jing Shen Ke Za Zhi
+Zhonghua Shi Yan He Lin Chuang Bing Du Xue Za Zhi
+Zhonghua Wai Ke Za Zhi
+Zhonghua Wei Chang Wai Ke Za Zhi
+Zhonghua Xin Xue Guan Bing Za Zhi
+Zhonghua Xue Ye Xue Za Zhi
+Zhonghua Ya Yi Xue Hui Za Zhi
+Zhonghua Yan Ke Za Zhi
+Zhonghua Yi Shi Za Zhi
+Zhonghua Yi Xue Yi Chuan Xue Za Zhi
+Zhonghua Yi Xue Za Zhi
+Zhonghua Yi Xue Za Zhi (Taipei)
+Zhonghua Yu Fang Yi Xue Za Zhi
+Zhonghua Zheng Xing Shao Shang Wai Ke Za Zhi
+Zhonghua Zheng Xing Wai Ke Za Zhi
+Zhonghua Zhong Liu Za Zhi
+Zidonghua Xuebao
+Ziekenhuiswezen
+Zimb J Econ
+Zimbabwe Nurse
+Zimbabwe Rhod Nurse
+Zinbun
+Zion
+Ziva Antika
+Zobozdrav Vestn
+Zool Anz
+Zool Jahrb Abt Anat Ontogenie Tiere
+Zool Pol
+Zool Sci Contrib N Y Zool Soc
+Zool Scr
+Zool Zhurnal
+Zoolog Sci
+Zoology (Jena)
+Zoomorphology
+Zoonoses Res
+Zooprofilassi
+Zpr Geogr Ust CSAV
+Zu Guo
+Zuchthygiene
+Zur Medizingesch Abh
+Zur Taschenb
+Zwingliana
+Zygon
+Zygote
+[Rep No] NAMC ACEL United States Air Crew Equip Lab Phila
+eHealth Int
diff --git a/lib/ArrayExpress/Curator/ExperimentChecker.pm b/lib/ArrayExpress/Curator/ExperimentChecker.pm
new file mode 100644
index 0000000..0088c02
--- /dev/null
+++ b/lib/ArrayExpress/Curator/ExperimentChecker.pm
@@ -0,0 +1,2428 @@
+#!/usr/bin/env perl
+#
+# Module to provide basic methods used by the expt_check script.
+#
+# Tim Rayner 2005, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: ExperimentChecker.pm 2069 2008-06-04 14:33:52Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../index.html">
+                <img src="../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: ExperimentChecker.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Curator::ExperimentChecker - a module used by expt_check.pl
+
+=head1 SYNOPSIS
+
+ use base qw/ArrayExpress::Curator::ExperimentChecker/;
+
+=head1 DESCRIPTION
+
+This module represents an abstract parent class providing methods for
+data file and experiment annotation checks to its child classes. See
+the following documentation for concrete checker classes:
+L<ArrayExpress::Curator::Validate> (Tab2MAGE),
+L<ArrayExpress::MAGETAB::Checker> (MAGE-TAB),
+L<ArrayExpress::Curator::MIAMExpress> (MIAMExpress) and
+L<ArrayExpress::Curator::Standalone> (standalone).
+
+=head1 OPTIONS
+
+The objects created by this module can be instantiated with the
+following options, common to all submission routes.
+
+=over 2
+
+=item C<log_to_current_dir>
+
+Write the log files to the current working directory, rather than in
+the submissions directory.
+
+=item C<is_standalone>
+
+Run checker in standalone mode.
+
+=item C<clobber>
+
+Overwrite existing log and graph files without asking for confirmation.
+
+=item C<adf_filename>
+
+The name of the file to use as ADF in feature/reporter checks. This
+overrides any array designs specified in the submission.
+
+=item C<array_accession>
+
+The ArrayExpress accession number of the array design to use for
+feature/reporter checks. This overrides any array accessions specified
+in the submission.
+
+=item C<qt_filename>
+
+The name of the file to be used for QT definitions. See
+L<ArrayExpress::Datafile::QT_list> for information on the format of
+this file. See also C<include_default_qts>.
+
+=item C<include_default_qts>
+
+Use the QT definitions supplied with these scripts alongside any new
+QT definitions provided by the C<qt_filename> option.
+
+=back
+
+=head1 METHODS
+
+=over 2
+
+=item C<check()>
+
+Starts the checks, based on the options specified in the constructor.
+
+=item C<get_miamexpress_software_type()>
+
+Returns the software term to use for a MIAMExpress export, if a
+unanimous verdict can be reached. Otherwise returns undef. This is
+also used for information purposes with Tab2MAGE and MAGE-TAB
+submissions, which is why it's in this superclass.
+
+=back
+
+=head1 FILES
+
+There are currently five log files written out by the script:
+
+=over 2
+
+=item C<expt_report.log>
+
+Contains some summary information and data quality statistics for each
+data file.
+
+=item C<expt_errors.log>
+
+Lists the errors that were encountered in parsing the data
+files. Details on Feature and QuantitationType errors are given in
+separate log files. Qualifier Value Source usage is also logged in
+this error log file.
+
+=item C<expt_biomaterials.log>
+
+Provides a basic sanity check of the flow of BioMaterials through the
+experiment (i.e. Sample -> Extract -> Labeled Extract ->
+Hybridization). For Tab2MAGE checking the majority of the information
+in this file has been moved to an output PNG file, since it is a much
+clearer and more flexible visualization format.
+
+=item C<expt_feature.log>
+
+Lists feature coordinates and/or reporter identifiers (FGEM only)
+missing from the array design(s) used in the experiment. Entries here
+will typically mean that a dummy array has to be used. Update: This
+file now also lists duplicate features in a separate list.
+
+=item C<expt_columnheadings.log>
+
+Lists unrecognized QuantitationTypes or hybridization IDs (FGEM only)
+appearing in the data column headings.
+
+=back
+
+=head1 TESTS
+
+The following tests are performed by this module, with output
+printed to the error and/or report filehandles:
+
+=over 4
+
+=item B<File existence>
+
+Checks that the data files referred to in the Tab2MAGE
+spreadsheet/MAGE-TAB SDRF/MIAMExpress database actually exist on the
+filesystem (error log).
+
+=item B<Affymetrix CHP file as normalized data>
+
+Checks that CHP files have been submitted as normalized rather than
+raw data (error log).
+
+=item B<Text file check>
+
+Confirms that submitted data files are text, not binary. This test
+is not applied to Affymetrix CHP files.
+
+=item B<File line endings check>
+
+Checks for Unix/DOS/Mac line endings (report log).
+
+=item B<Affymetrix EXP file check>
+
+Checks that EXP files are submitted as raw data and that they all have
+Protocol, Station and Module information (error log).
+
+=item B<Basic data file summary>
+
+Prints out the file format (Affymetrix, GenePix or Generic), the type
+(raw, normalized or transformed), row and column counts (report log).
+
+=item B<Duplicate columns>
+
+Checks for repeated column headings in each data file (error log).
+
+=item B<Excel truncated files>
+
+Checks for possible data corruption by Excel truncation of the file
+(error log).
+
+=item B<QuantitationTypes>
+
+Checks column headings against a list of known QTs. Reports on
+unrecognized QTs (error log, column headings log). This is a work in
+progress.
+
+=item B<FGEM hybridization IDs>
+
+Checks FGEM column headings against the hybridization IDs for the
+submission, reports on those which are not recognized. This
+incorporates a check on the QTs for FGEM files (error log, column
+headings log).
+
+=item B<FGEM BioDataCube order>
+
+Checks that the included final data matrix is laid out in DBQ order,
+rather than DQB. The ArrayExpress MAGE-ML loader software does not
+support the DQB order.
+
+=item B<Data checks>
+
+Data checks are only performed on recognized QT columns. Checks are
+for:
+
+  Text in numeric columns
+  Null values in numeric columns
+  Floats in integer columns
+  Inappropriate boolean values (i.e., not 0 or 1)
+  Log ratios outside reasonable range
+  Basic check on saturation indicators (primarily GenePix files)
+
+(error log).
+
+=item B<Benford's law>
+
+Calculates Benford's law across dimensioned float data. In theory
+this should be approximately 30% for good data (report log).
+
+=item B<Percent null>
+
+Calculates overall percent null across the whole data set. Zero values
+are also counted as null. In practice under 10% null values seems to
+be a reasonable expectation (report log).
+
+=item B<Feature/Reporter check vs. array design>
+
+Checks the feature coordinates (raw, normalized data) or the reporter
+identifiers (FGEM) against either the array designs linked to the
+hybridization in MIAMExpress, or against a user-supplied ADF. Prints
+out a list of features not found in the array design (error log,
+features log). Also alerts the curator when significantly fewer
+features are found in the data file compared to the array design
+(error log). Note that this will give false errors on array designs
+associated with dummy array designs (e.g., some Affy arrays).
+
+=item B<Duplicate Features/Reporters>
+
+Checks that Features or Reporter identifiers are not repeated within
+the same file. Prints out a list of duplicate features/reporters
+(error log, features log).
+
+=item B<Duplicate filenames>
+
+Checks that there are no duplicate files associated with the
+submission (error log).
+
+=item B<Row/column count consistency>
+
+Checks that all the files of a given type (raw, normalized)
+have consistent numbers of rows and columns (error log).
+
+=item B<Affy EXP file consistency>
+
+Checks that the parameters in each EXP file which should be the same
+are the same (error log).
+
+=item B<Potentially duplicated files with different names>
+
+Checks a single line from each file against the same line in every
+other file, and reports on any matching pairs of files (error log).
+
+=back
+
+=head1 SEE ALSO
+
+=over 1
+
+=item L<ArrayExpress::Curator::Validate>
+
+=item L<ArrayExpress::MAGETAB::Checker>
+
+=item L<ArrayExpress::Curator::MIAMExpress>
+
+=item L<ArrayExpress::Curator::Standalone>
+
+=item L<ArrayExpress::Datafile::QT_list>
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2004.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Curator::ExperimentChecker;
+
+use strict;
+use warnings;
+
+use Carp;
+use File::Spec;
+use Scalar::Util qw(looks_like_number);    # Used for checking data values
+use English qw( -no_match_vars );
+use List::Util qw(max);
+use List::MoreUtils qw(any none notall);
+use Readonly;
+use Class::Std;
+use IO::Handle;
+use Digest::MD5 qw( md5 );    # Used for tracking missing/duplicated features
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+require ArrayExpress::Datafile::DataMatrix;
+
+use ArrayExpress::Datafile::QT_list qw(get_QTs);
+
+require ArrayExpress::Datafile;
+require ArrayExpress::Datafile::Affymetrix;
+
+use ArrayExpress::Datafile::Metrics qw(
+    check_data_metrics
+    calculate_benford
+    pearson_correlation
+);
+
+use ArrayExpress::Curator::Database qw(
+    retrieve_AE_adf
+    parse_adf
+    arrayaccession_in_aedw
+    map_affy_accno_to_name
+);
+
+use ArrayExpress::Curator::Report qw(report_accumulated_errors);
+
+use ArrayExpress::Curator::Logger;
+use base qw(ArrayExpress::Curator::Logger);
+
+# Attributes available for initialization.
+my %is_standalone        : ATTR( :name<is_standalone>,          :default<undef> );
+my %adf_filename         : ATTR( :get<adf_filename>,            :init_arg<adf_filename>,            :default<undef> );
+my %array_accession      : ATTR( :get<array_accession>,         :init_arg<array_accession>,         :default<undef> );
+my %qt_filename          : ATTR( :get<qt_filename>,             :init_arg<qt_filename>,             :default<undef> );
+my %include_default_qts  : ATTR( :get<include_default_qts>,     :init_arg<include_default_qts>,     :default<undef> );
+my %skip_data_checks     : ATTR( :get<skip_data_checks>,        :init_arg<skip_data_checks>,        :default<undef> );
+my %safe_filechecks      : ATTR( :get<safe_filechecks>,         :init_arg<safe_filechecks>,         :default<undef> );
+my %source_directory     : ATTR( :get<source_directory>,        :init_arg<source_directory>,        :default<undef> );
+my %reporter_prefix      : ATTR( :get<reporter_prefix>,         :init_arg<reporter_prefix>,         :default<undef> );
+my %compseq_prefix       : ATTR( :get<compseq_prefix>,          :init_arg<compseq_prefix>,          :default<undef> );
+my %ignore_size_limits   : ATTR( :name<ignore_size_limits>,     :default<undef> );
+
+# Accumulative accessors; each of these has an "add_" method defined below.
+my %miame                : ATTR( :get<miame>,              :default<0>  );
+my %aedw_score           : ATTR( :get<aedw_score>,         :default<0>  );
+my %software_types       : ATTR( :get<software_types>,     :default<[]> );
+my %missing_features     : ATTR( :get<missing_features>,   :default<{}> );
+my %duplicate_features   : ATTR( :get<duplicate_features>, :default<{}> );
+my %missing_features_cache   : ATTR( :get<missing_features_cache>,   :default<{}> );
+my %duplicate_features_cache : ATTR( :get<duplicate_features_cache>, :default<{}> );
+my %duplicate_columns    : ATTR( :get<duplicate_columns>,  :default<{}> );
+my %unknown_qts          : ATTR( :get<unknown_qts>,        :default<{}> );
+my %unknown_hybs         : ATTR( :get<unknown_hybs>,       :default<{}> );
+
+# These accessors simply accumulate unique hash keys; get_* returns an
+# arrayref of those keys.
+my %arrayexpress_arrays  : ATTR( :get<arrayexpress_arrays>,:default<[]> );
+my %expt_designs         : ATTR( :get<expt_designs>,       :default<[]> );
+my %expt_organisms       : ATTR( :get<expt_organisms>,     :default<[]> );
+
+# This is a cache of array designs keyed by id.
+my %array_design_cache   : ATTR( :default<{}> );
+
+sub START {
+    my ( $self, $id, $args ) = @_;
+
+    $self->set_progname( $CONFIG->get_EXPTCHECK_PROGNAME() );
+    $self->set_version( $CONFIG->get_EXPTCHECK_VERSION() );
+
+    return;
+}
+
+sub get_files_and_annotation : PRIVATE {
+
+    # This is an abstract class: MIAMExpress, Tab2MAGE, and standalone
+    # checking are handled by overriding this method in their
+    # respective subclasses.
+
+    my ( $self ) = @_;
+
+    confess("Error: Stub method called in abstract class " . __PACKAGE__);
+
+}
+
+sub add_miame : RESTRICTED {
+
+    # Takes an integer, bitwise ORs it with $miame{ident $self},
+    # stores and returns the result. Used to store the MIAME scores
+    # for compliance reporting.
+
+    my ( $self, $err ) = @_;
+
+    $miame{ident $self} ||= 0;
+    if ($err) { $miame{ident $self} |= $err }
+
+    return $miame{ident $self};
+}
+
+sub add_aedw_score : RESTRICTED {
+
+    # Takes an integer, bitwise ORs it with $aedw_score{ident $self},
+    # stores and returns the result. Used to store the AEDW scores
+    # for compliance reporting.
+
+    my ( $self, $err ) = @_;
+
+    $aedw_score{ident $self} ||= 0;
+    if ($err) { $aedw_score{ident $self} |= $err }
+
+    return $aedw_score{ident $self};
+}
+
+sub _check_aedw_and_miame : PRIVATE {
+
+    # Run some general tests on aedw suitability and MIAME compliance.
+    my ($self, $hyb_ids) = @_;
+
+    # Initialize some logs which we will always want written to. Note
+    # that this must be done _after_ connecting to the database, or
+    # the names don't get localized properly:
+    $self->logprint('aedw',  q{});
+    $self->logprint('miame', q{});
+
+    #######################################
+    # Top-level tests on AEDW suitability #
+    #######################################
+
+    # Report on the arrays used, and check against the DW. Each array
+    # must pass MIAME (in the autosubmissions DB, calculated by Anna
+    # Farne's scripts) for the score to be added to the total.
+    my @ae_arrays         = @{ $self->get_arrayexpress_arrays() };
+    my $array_miame       = $CONFIG->get_MIAME_ARRAYSEQ();
+    my $all_arrays_loaded = 1;
+
+    my %miame_compliant_pipeline
+	= map { $_ => 1 } @{ $CONFIG->get_MIAME_COMPLIANT_ARRAY_PIPELINES() || [] };
+
+    # If the array is not in AE, we have a problem.
+    unless (@ae_arrays) {
+	$self->logprint(
+	    'aedw',
+	    "Problem: no public AE arrays associated with submission.\n"
+	);
+	$array_miame       = 0;
+	$all_arrays_loaded = 0;
+    }
+
+    # Check on known AE-loaded arrays.
+    foreach my $accno ( @ae_arrays ) {
+	$self->logprint(
+	    'report',
+	    "ArrayExpress array used: $accno\n",
+	);
+	unless ( arrayaccession_in_aedw($accno) ){
+	    $all_arrays_loaded = 0;
+	    $self->logprint(
+		'aedw',
+		"Problem: Array $accno is not in the data warehouse.\n"
+	    );
+	}
+
+	# Check that this isn't from a pipeline with special MIAME
+	# dispensation, then check that it's compliant in the
+	# autosubmissions database. Accession numbers are assumed to
+	# be in the form \D{n}\d{n}.
+	my ($acc_prefix) = ($accno =~ m/\A (\D*?) \d+ \z/xms);
+	unless ( $miame_compliant_pipeline{$acc_prefix} ) {
+	    if ( $CONFIG->get_AUTOSUBS_DSN() ) {
+		require ArrayExpress::AutoSubmission::DB::ArrayDesign;
+		my $array = ArrayExpress::AutoSubmission::DB::ArrayDesign->retrieve(
+		    accession => $accno,
+		);
+
+		# At the moment, array MIAME is yes or no.
+		unless ( $array && $array->miame_score() ) {
+		    $self->logprint(
+			'miame',
+			"Problem: Array $accno is not MIAME compliant.\n"
+		    );
+		    $array_miame = 0;
+		}
+	    }
+	    else {
+		$self->logprint(
+		    'miame',
+		    "Problem: Unable to check array MIAME compliance in subs database.\n"
+		);
+	    }
+	}
+    }
+
+    # Add score for the data warehouse if all loaded okay.
+    $self->add_aedw_score($CONFIG->get_AEDW_ARRAYLOADED()) if $all_arrays_loaded;
+
+    # Note the final score.
+    $self->add_miame($array_miame);
+
+    # Data warehouse only wants experiments with more than x no. of hybs.
+    if (scalar(grep { defined $_ } values %$hyb_ids)
+	    >= $CONFIG->get_AEDW_MINIMUM_HYBS() ) {
+	$self->add_aedw_score($CONFIG->get_AEDW_HYBNUMBER());
+    }
+    else {
+	$self->logprint(
+	    'aedw',
+	    "Problem: Experiment has fewer than "
+		. $CONFIG->get_AEDW_MINIMUM_HYBS()
+		. " hybridizations.\n"
+	);
+    }
+
+    # Only certain organisms and design types allowed in the DW. At
+    # least one design type must be in AEDW_DESIGN_TYPES; if not,
+    # check against AEDW_UNWANTED_DESIGN_TYPES and note if the
+    # experiment may be under-annotated.
+    my %allowed_design   = map { $_ => 1 } @{ $CONFIG->get_AEDW_DESIGN_TYPES() || [] };
+    my %unallowed_design = map { $_ => 1 } @{ $CONFIG->get_AEDW_UNWANTED_DESIGN_TYPES() || [] };
+    my @design_types = @{ $self->get_expt_designs() };
+    my $is_bad_design_type;
+
+    # Check the positive stuff first.
+    if ( ! scalar @design_types ) {
+
+	# No design type
+	$is_bad_design_type++;
+	$self->logprint(
+	    'aedw',
+	    "Problem: Experiment design is not given.\n"
+	);
+    } elsif ( any { $allowed_design{$_} } @design_types ) {
+
+	# Good design type
+	$self->add_aedw_score($CONFIG->get_AEDW_DESIGNTYPE());
+    }
+    else {
+
+	# Bad design type
+	$is_bad_design_type++;
+	$self->logprint(
+	    'aedw',
+	    "Problem: Experiment design is not one of the required types:\n\t- ",
+	    join("\n\t- ", sort keys %allowed_design),
+	    "\n",
+	);
+    }
+
+    # Hold our noses and check the negative stuff.
+    if (    $is_bad_design_type
+	 && ( ! scalar @design_types || none { $unallowed_design{$_} } @design_types ) ) {
+	$self->logprint(
+	    'aedw',
+	    "Caveat: Experiment design is not one of the following unsuitable types,\n    ",
+	    "and may yet be suitable for the data warehouse:\n\t- ",
+	    join("\n\t- ", sort keys %unallowed_design),
+	    "\n",
+	);
+    }
+
+    return;
+}
+    
+sub check {
+
+    # Takes a list of options (see perldocs above), checks the
+    # submission and returns the error code.
+
+    my $self = shift;
+
+    # Set STDOUT and STDERR to unbuffered, for live feedback to a
+    # command-line user.
+    STDOUT->autoflush(1);
+    STDERR->autoflush(1);    # in case someone else has tampered...
+
+    # Connect to MX, or read in the spreadsheet; populate array design info.
+    my ( $filelist, $hyb_ids, $norm_ids )
+	= $self->get_files_and_annotation();
+
+    # Check the return values, just in case.
+    unless ( ( ref $filelist eq 'ARRAY' )
+	  && ( ref $hyb_ids  eq 'HASH' )
+          && ( ref $norm_ids eq 'HASH' ) ) {
+	$self->logprint(
+	    'error',
+	    "FATAL ERROR: Unable to retrieve data file and/or annotation information\n",
+	);
+	$self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+	return $self->get_error();
+    }
+
+    # AEDW and MIAME checks (annotation only).
+    $self->_check_aedw_and_miame($hyb_ids);
+
+    ##############################
+    # Begin the datafile reports #
+    ##############################
+
+    # Sort out our QuantitationTypes
+
+    $self->logprint_line( 'error', 'QT file parsing START' );
+
+    # Get the QTs, either discarding or including known QTs
+    my $QTs = get_QTs( $self->get_qt_filename, $self->get_include_default_qts );
+
+    $self->logprint_line( 'error', 'QT file parsing END' );
+
+    # This is the heavyweight method that does all the work.
+    $self->check_file_list($filelist, $QTs, $hyb_ids, $norm_ids);
+
+    # Implementation of this method is left to the concrete subclasses.
+    $self->visualize_experiment($filelist);
+
+    return $self->get_error;
+
+}
+
+sub visualize_experiment : RESTRICTED {
+
+    my ( $self ) = @_;
+
+    croak("ERROR: Abstract stub method called in ExperimentChecker superclass.");
+}
+
+sub check_file_list : PRIVATE {
+
+    my ($self, $filelist, $QTs, $hyb_ids, $norm_ids) = @_;
+    
+    ######################
+    # START OF FILE LOOP #
+    ######################
+
+    # Start checking the files
+    $self->logprint_line( 'error', 'Data file parsing START' );
+
+    print STDOUT ( "\n" . scalar(@$filelist) . " files to process.\n\n" );
+
+    my (%file_name_count, %file_type_count);
+
+    # If the -x option was used, skip the heavyweight checks.
+    if ( $self->get_skip_data_checks() ) {
+	$self->logprint( 'error', "\nWARNING: SKIPPING data file checks.\n\n" );
+	$self->logprint( 'report', "** Data file checks were skipped. **\n\n" );
+	print STDOUT ( "** Skipping data file checks **.\n\n" );
+    }
+
+    # Run some checks on the files.
+    # Set FV "missing" flag to one if there are no files at all.
+    my $factorval_missing = scalar @$filelist ? 0 : 1;
+    foreach my $file (@$filelist) {
+
+	# Record some aggregate metadata.
+	$file_name_count{ $file->get_name() }++;
+	$file_type_count{ $file->get_data_type() }++;
+
+	# DW submissions must have factor values, attached here to the
+	# files (MX checking does not attach FVs to FGEM datafile object).
+	unless ( scalar( grep { defined $_ } values %{ $file->get_factor_value() } )
+	      || ( $file->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE()
+	      ||   $file->get_data_type() eq $CONFIG->get_RAW_DM_FILE_TYPE()
+	      ||   $file->get_is_exp() ) ) {
+
+	    $factorval_missing++;
+	}
+
+	# Check file, unless told not to (-x).
+	unless ( $self->get_skip_data_checks() ) {
+	    $self->check_file($file, $QTs, $hyb_ids, $norm_ids);
+	}
+    }
+
+    # Note any FV problems.
+    if ( $factorval_missing ) {
+
+	# AEDW
+	$self->logprint(
+	    'aedw',
+	    "Problem: Experiment does not have factor value for one or more hybs.\n"
+	);
+
+	# MIAME
+	$self->logprint(
+	    'miame',
+	    "Problem: Experiment does not have factor value for one or more hybs.\n"
+	);
+    }
+    else {
+
+	# Record the AEDW and MIAME scores.
+	$self->add_aedw_score($CONFIG->get_AEDW_FACTORVALUES());
+	$self->add_miame($CONFIG->get_MIAME_FACTORVALUES());
+    }
+
+    ####################
+    # END OF FILE LOOP #
+    ####################
+
+    $self->logprint_line( 'report' );
+
+    $self->logprint_line( 'error', 'Data file parsing END' );
+
+    # Commence aggregate checks on the parsed files.
+    $self->logprint_line( 'error', 'Final consistency check START' );
+
+    # Check for duplicate file uploads (simply by name; no check of contents)
+    foreach my $filename ( keys %file_name_count ) {
+        if ( $file_name_count{$filename} > 1 ) {
+            $self->logprint( 'error',
+                "ERROR: Files named $filename have been uploaded multiple times.\n"
+            );
+            $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+        }
+    }
+
+    # Check that we have raw and (norm || fgem) files; this is a DW
+    # (and/or MIAME) requirement and may be elaborated upon later.
+    my $good_aedw_data = 1;
+    if ($file_type_count{'raw'}) {
+	$self->add_miame($CONFIG->get_MIAME_RAWDATA);
+    }
+    else {
+	$self->logprint(
+	    'miame',
+	    "Problem: Raw data is absent.\n"
+	);
+    }
+
+    if ( $file_type_count{'normalized'}
+	     || $file_type_count{$CONFIG->get_FGEM_FILE_TYPE()} ) {
+	$self->add_miame($CONFIG->get_MIAME_NORMDATA);
+    }
+    else {
+	$good_aedw_data = 0;
+	$self->logprint(
+	    'aedw',
+	    "Problem: Normalized data is absent.\n"
+	);
+	$self->logprint(
+	    'miame',
+	    "Problem: Normalized data is absent.\n"
+	);
+    }
+    $self->add_aedw_score($CONFIG->get_AEDW_GOODDATA()) if $good_aedw_data;
+
+    # Check row and column counts across files.
+    $self->_check_datafile_consistency($filelist);
+
+    # Report on QTs, FGEM hyb_ids and duplicate columns
+    report_accumulated_errors(
+        $self->get_unknown_qts(),
+        $self->log_fh('columns'),
+        'Error: The following unknown QuantitationTypes:'
+    );
+    report_accumulated_errors(
+        $self->get_unknown_hybs(),
+        $self->log_fh('columns'),
+        'Error: The following unknown Hyb/Norm IDs:'
+    );
+    report_accumulated_errors(
+        $self->get_duplicate_columns(),
+        $self->log_fh('columns'),
+        'ERROR: The following duplicated column headings:'
+    );
+    report_accumulated_errors(
+        $self->get_missing_features(),
+        $self->log_fh('feature'),
+        'ERROR: The following features or identifiers missing from the array:'
+    );
+    report_accumulated_errors(
+        $self->get_duplicate_features(),
+        $self->log_fh('feature'),
+        'ERROR: The following duplicated feature coordinates / identifiers:'
+    );
+
+    # Sort out which are the EXP files
+    my $exp_filelist;
+    foreach my $file (@$filelist) {
+        push( @$exp_filelist, $file ) if $file->get_is_exp();
+    }
+
+    # Check the EXP files
+    my $exp_inconsistencies = $self->_check_exp_params($exp_filelist);
+
+    # Print out what we find
+    foreach my $section ( sort keys %$exp_inconsistencies ) {
+        foreach my $param ( sort keys %{ $exp_inconsistencies->{$section} } )
+        {
+            $self->logprint( 'error',
+                "Warning: parameter differs across the submitted EXP files: [$section] $param\n"
+            );
+            $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+        }
+    }
+
+    # Pairwise many-to-many comparison of a single selected data line
+    # from each file to catch renamed duplicates. This has been
+    # overloaded in the case of binary files to hold an md5 hash of
+    # the entire file.
+    for ( my $i = 0; $i <= $#$filelist; $i++ ) {
+        for ( my $j = $i + 1; $j <= $#$filelist; $j++ ) {
+            my $line_i = $filelist->[$i]->get_test_data_line();
+            my $line_j = $filelist->[$j]->get_test_data_line();
+
+            # Binary files may not have the test line.
+            if ( $line_i && $line_j && ( $line_i eq $line_j ) ) {
+                $self->logprint(
+                    'error',
+                    "WARNING: Files ",
+                    $filelist->[$i]->get_name(),
+                    " and ",
+                    $filelist->[$j]->get_name(),
+                    " may be identical.\n"
+                );
+                $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+            }
+        }
+    }
+
+    $self->logprint_line( 'error', 'Final consistency check END' );
+
+    print STDOUT ("\nDone processing files.\n");
+
+    return;
+}
+
+sub non_rowbased_data_test : PRIVATE {
+
+    # For data files which don't conform to the usual model of rows of
+    # data with headings, we run some basic tests here. This includes
+    # all Affy and binary file types.
+
+    # N.B. Affy CELv3 used to be treated as row-level data but when we
+    # export them as MAGE-ML we use a separate parser, so it would be
+    # better to invoke that during checking (FIXME?).
+
+    my ( $self, $file ) = @_;
+
+    # We can't sample the data, so we just take the MD5 hash.
+    $file->set_test_data_line( $file->get_md5_digest() );
+
+    # Check the file against those already loaded in AE.
+    if ( $CONFIG->get_AUTOSUBS_DSN() ) {
+	$self->check_file_against_loaded_data( $file );
+    }
+
+    $file->set_row_count(0);    # reset the row count
+
+    return;
+}
+
+sub affymetrix_data_test : PRIVATE {
+
+    my ( $self, $file ) = @_;
+
+    # Affymetrix CHP or XDA CEL; record software for consensus
+    # calculation.
+    $file->set_format_type('Affymetrix');
+    $self->add_software_types('Affymetrix');
+
+    $self->confirm_affy_chip_type( $file );
+
+    # Very quick check to make sure CHP files are in the right place
+    if (   $file->get_path() =~ m/\.CHP$/i
+	       && $file->get_data_type ne 'normalized' ) {
+
+	$self->logprint(
+	    'error',
+	    "Error: CHP file ",
+	    $file->get_name(), " submitted as raw data or FGEM.\n",
+	);
+	$self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+    }
+
+    # Some generic tests we can run.
+    $self->non_rowbased_data_test( $file );
+
+    return;
+}
+
+sub affymetrix_expfile_test : PRIVATE {
+
+    # Check Affy EXP files are in the right place, and that they
+    # look okay.
+    my ( $self, $file ) = @_;
+
+    $file->set_format_type('Affymetrix');
+
+    # EXP files should be submitted as raw data (MX) or as a dedicated
+    # EXP file type (Tab2MAGE).
+    unless (    $file->get_data_type() eq 'raw'
+	     || $file->get_data_type() eq 'EXP' ) {
+	$self->logprint(
+	    'error',
+	    "Error: EXP file ",
+	    $file->get_name(), " submitted as either normalized or FGEM.\n",
+	);
+	$self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+    }
+
+    # Check on EXP files; populate the {exp_data} hash key,
+    # set format etc.
+    unless ( $file->parse_exp_file() ) {
+	$self->logprint(
+	    'error',
+	    sprintf(
+		"ERROR parsing EXP file %s\n",
+		$file->get_name(),
+	    ),
+	);
+	$self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+    }
+
+    my $exp_data = $file->get_exp_data();
+
+    unless (   $exp_data->{Fluidics}{Protocol}
+            && $exp_data->{'Sample Info'}{'Chip Lot'} ) {
+
+	$self->logprint(
+	    'error',
+	    "Warning: EXP file ",
+	    $file->get_name(),
+	    " lacks either chip lot or protocol info.\n",
+	);
+
+	# Have relaxed this since we won't be using GDAC when
+	# automating anyway.
+	$self->add_error( $CONFIG->get_ERROR_MIAME() );
+    }
+
+    return;
+}
+
+sub check_file : PRIVATE {
+
+    # Returns 1 on successful check (whether the file is good or not),
+    # undef if there was a problem checking.
+
+    my ( $self, $file, $QTs, $hyb_ids, $norm_ids ) = @_;
+
+    $self->logprint_line( 'report' );
+
+    my ($feature_coords);
+
+    # Set the data file error filehandle.
+    $file->set_error_fh( $self->log_fh('error') );
+
+    # just the filename
+    $file->set_name( ( File::Spec->splitpath( $file->get_path() ) )[2] );
+
+    my $hybdir =
+      ( ! $file->get_hyb_sysuid()
+          || ( $file->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE() ) )
+      ? undef
+      : "hybrid" . $file->get_hyb_sysuid();
+    my $hyb_filename = $hybdir
+      ? File::Spec->catfile( $hybdir, $file->get_name() )
+      : $file->get_name();
+
+    print STDOUT ( "Checking file ", $file->get_name(), "... " );
+    $self->logprint_line('error', sprintf( "  %s  ", $file->get_name() ));
+
+    # Check that files are actually present and readable.
+    unless ( -r $file->get_path() ) {
+        $self->logprint( 'error', "FILE ERROR: File ",
+            $file->get_path(), " not found or unreadable.\n" );
+        $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+        print STDOUT ("\n");
+        return;    # Bail out with undef return
+    }
+
+    # Quick file size check - sometimes uploads result in
+    # zero-byte files.
+    if ( -s $file->get_path() == 0 ) {
+        $self->logprint(
+         'error', "ERROR: File ",
+           $file->get_path(), " is empty (zero bytes).\n"
+        );
+        $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+    }
+
+    # If file exists, list it in the report log
+    $self->logprint( 'report', "Filename     : ",
+		     $file->get_path(),      "\n" );
+    $self->logprint( 'report', "File type    : ",
+		     $file->get_data_type(), "\n" );
+
+    # Handle Affymetrix files here. In general we now don't bother
+    # running tests on these files. This at least confirms that the
+    # file header can be parsed (confirm_affy_chip_type).
+    if ( $file->get_path() =~ m/\. (CHP|CEL|EXP) \z/ixms ) {
+
+	if ( $file->get_path() =~ m/\. EXP \z/ixms ) {
+	    $self->affymetrix_expfile_test( $file );
+	}
+	else {
+	    $self->affymetrix_data_test( $file );
+	}
+
+	$self->logprint(
+	    'report', "File format  : ", $file->get_format_type(), "\n",
+	);
+
+	# Finish the STDOUT row.
+	print STDOUT "done.\n";
+
+        return 1;
+    }
+
+    # Quick data type check here (text vs. binary).
+    if ( $file->get_is_binary() ) {    # unless file is ASCII...
+
+        $self->logprint( 'report',
+            "File format  : Binary (skipped further analysis)\n" );
+
+	# Warn on unrecognized binary file type. We've already handled
+	# Affy, so this file must not be recognized.
+	print STDOUT (
+	    "Error: File ",
+	    $file->get_name(), " is a binary file. Skipping.\n"
+	);
+	$self->logprint(
+	    'error',
+	    "Error: File ", $file->get_name(),
+	    " is a binary file; skipped further analysis.\n",
+	);
+	$self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+
+	# Some generic tests we can run.
+	$self->non_rowbased_data_test( $file );
+
+	# Finish the STDOUT row.
+	print STDOUT "done.\n";
+
+	# Bail out with undef return
+        return;
+    }
+
+    # File size check for safety's sake
+    unless ( ( -s $file->get_path() < $CONFIG->get_MAX_DATAFILE_SIZE() )
+	       || $self->get_ignore_size_limits() ) {
+        print STDERR ( "Error: File ", $file->get_name(),
+            " is too large. Skipping.\n" );
+        $self->logprint(
+	    'report',
+	    "File format  : Unknown (file too large: skipped further analysis)\n"
+        );
+        $self->logprint( 'error', "ERROR: File ", $file->get_name(),
+            " is too large to be processed safely; further analysis skipped.\n"
+        );
+
+        # Likely these large files will break downstream processing, and
+        # we haven't checked them anyway.
+        $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+        $file->set_row_count(0);    # reset the row count
+        return;    # Bail out with undef return
+    }
+
+    # Now we've discarded the corner cases, get some actual
+    # information from the file. This also populates the $file object.
+    my $rc;
+    ( $feature_coords, $rc ) = $file->parse_datafile(
+	$QTs,
+	$hyb_ids,
+	$norm_ids,
+    );
+
+    # Note down the software type so we can derive a consensus later.
+    my $qt_type = $file->get_qt_type();
+    if ( $qt_type ) {
+
+	# Take everything up to the first square bracket.
+	$qt_type =~ s/^([^\[]*).*/$1/;
+	$self->add_software_types($qt_type);
+    }
+    if ($rc) {
+	$self->logprint(
+	    'error',
+	    "PARSE ERROR: file ",
+	    $file->get_name(), ": $rc",
+	);
+	$self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+    }
+
+    # Print out some basic information on the files
+    $self->logprint(
+        'report',
+        "File format  : ",
+        ( $file->get_qt_type() or 'Unknown' ), "\n"
+    );
+    $self->logprint( 'report', "Line endings : ",
+		     $file->get_line_format(), "\n" );
+
+    $self->logprint(
+	'report',
+	"Rows         : ",
+	( $file->get_row_count() || 'N/A' ), "\n"
+    );
+    my $column_count = scalar( @{ $file->get_column_headings() } );
+    $self->logprint(
+	'report',
+	"Columns      : ",
+	( $column_count || 'N/A' ), "\n"
+    );
+
+    unless ($column_count) {
+	$self->logprint(
+	    'error',
+	    "PARSE ERROR: Failed to parse column headings in file ",
+	    $file->get_name(), "\n",
+	);
+	$self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+	$file->increment_parse_errors();
+    }
+
+    # Check for repeated column headings in each data file.
+    $self->_check_duplicate_columns( $file, $hyb_filename );
+
+    # Crappy Excel truncates files. Here we check for that.
+    if ( $file->get_row_count() == 65535 ) {
+	$self->logprint(
+	    'error',
+	    "WARNING: Possible Excel-truncated file ",
+	    $file->get_name(), "\n",
+	);
+	$self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+    }
+
+    # Report on bad column heading QTs
+    if ( scalar @{ $file->get_fail_columns() } ) {
+	$self->add_unknown_qts( $hyb_filename, $file->get_fail_columns() );
+	$self->logprint(
+	    'error',
+	    "Warning: Possible QuantitationType problems in file ",
+	    $file->get_name(), ". See column heading log file.\n",
+	);
+
+	# In the past this has been PARSEBAD for tab2mage,
+	# INNOCENT for MX. Whilst the autosubmissions system uses
+	# keep_all_qts, however, we can relax this.
+	$self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+    }
+
+    # This is for bad FGEM column heading hyb ids
+    if ( scalar @{ $file->get_fail_hybs() } ) {
+	$self->add_unknown_hybs( $hyb_filename, $file->get_fail_hybs() );
+	$self->logprint(
+	    'error',
+	    "Error: Hyb/Norm ID problems in file ",
+	    $file->get_name(), ". See column heading log file.\n",
+	);
+	$self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+    }
+
+    if ( $file->get_data_type eq $CONFIG->get_FGEM_FILE_TYPE() ) {
+
+	my $datamatrix = ArrayExpress::Datafile::DataMatrix->new();
+
+	my ( $ba_list, $qt_list, $order ) =
+	    $datamatrix->get_dimension_lists(
+		$file->get_heading_hybs(),
+		$file->get_heading_qts(),
+	    );
+	if ( !$order ) {
+	    $self->logprint(
+		'error',
+		"PARSE ERROR: FGEM data cube order cannot be determined.\n"
+	    );
+	    $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+	}
+	elsif ( $order eq 'DQB' ) {
+	    $self->logprint(
+		'error',
+		qq{WARNING: FGEM data cube order "$order" }
+	      . qq{is unsupported by ArrayExpress.\n}
+	    );
+
+	    # Not really a bad parsing, but we don't support
+	    # this coding order in ArrayExpress yet.
+	    $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+	}
+    }
+
+    # Call check_data_metrics() to check data for consistency, one
+    # column at a time
+    check_data_metrics( $file->get_data_metrics );
+
+    while ( my ( $header, $qt_metric ) = each %{ $file->get_data_metrics() } ) {
+	foreach my $error_message ( keys %{ $qt_metric->{errors} } ) {
+
+	    # Filter out the singletons; they're not much of a
+	    # problem, and all too common
+	    if ( $qt_metric->{errors}{$error_message} > 10 ) {
+		$self->logprint(
+		    'error',
+		    "Warning: $error_message (for ",
+		    $qt_metric->{errors}{$error_message}
+			. " values) in column \'$header\', file ",
+		    $file->get_name(),
+		    "\n"
+		);
+
+		# We see these all the time; maybe make this stricter
+		# when QT processing is more finalized?
+		$self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+	    }
+	}
+    }
+
+    # Print out the overall Benford's law value for each datafile (can
+    # do this column-wise if there's demand).  N.B. we're only
+    # calculating from MeasuredSignal values at this time, because
+    # that's where the interesting data is.
+    my $benval = calculate_benford( $file->get_data_metrics() );
+    if ( looks_like_number($benval) ) {
+	$self->logprint(
+	    'report',
+	    sprintf( "Benford's Law value: %.2f%%\n", $benval ),
+	);
+
+# Benford's law warnings deactivated for now (we can't do much about bad data).
+#  if ($benval < 20 || $benval > 40){ # This value should be around 30%
+#    $self->logprint('error', "Warning: Benford's Law violated by data in file ",$file->get_name(),". See report log.\n");
+#    $self->add_error($CONFIG->get_ERROR_INNOCENT());
+#  }
+
+    }
+    else {
+	$self->logprint(
+	    'report',
+	    sprintf( "Benford's Law value: %s\n", $benval ),
+	);
+    }
+
+    # Print out percent null values for all the columns containing
+    # recognized QTs.
+    my $percent_null = $file->percent_null;
+    if ( looks_like_number($percent_null) ) {
+	$self->logprint(
+	    'report',
+	    sprintf( "Percent null values: %.2f%%\n", $percent_null ),
+	);
+
+	# This value should be less than around 10-15%, based on what
+	# I've seen so far.
+	if ( $percent_null > 50 ) {
+	    $self->logprint(
+		'error',
+		"WARNING: Percent null data in ",
+		$file->get_name(), " > 50%. See report log.\n",
+	    );
+	    $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+	}
+    }
+    else {
+	$self->logprint(
+	    'report',
+	    sprintf( "Percent null values: %s\n", $percent_null ),
+	);
+    }
+
+    # We check raw and normalized files by feature coordinate,
+    # combined (transformed) data files by reporter identifier.
+    my $num_index_columns = scalar( @{ $file->get_index_columns() } );
+
+    # Skip files which weren't parsed correctly.
+    if (   $num_index_columns
+	&& $feature_coords
+        && @{$feature_coords} ) {
+
+	$self->_compare_index_to_design(
+	    $file,
+	    $feature_coords,
+	    $hyb_filename,
+	);
+
+	# Check for repeated features, reporters within a file. Note
+	# that repeated reporters or compseqs ($num_index_columns ==
+	# 1) are okay.
+	unless ( $num_index_columns == 1 ) {
+	    $self->_check_duplicate_features(
+		$file,
+		$feature_coords,
+		$hyb_filename,
+	    );
+	}
+    }
+
+    else {
+
+	# Bail if there are no recognizable features.
+	$self->logprint(
+	    'error',
+	    "PARSE ERROR: Failed to parse features in file ",
+	    $file->get_name(), ". Skipping feature/reporter check.\n",
+	);
+	$self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+    }
+
+    # And now we're done with this file
+    print STDOUT ("done.\n");
+
+    return 1;
+}
+
+sub get_miamexpress_software_type {
+
+    # Returns the software term to use for a MIAMExpress export, if a
+    # unanimous verdict can be reached. Otherwise returns undef.
+    my ( $self ) = @_;
+
+    my %consensus;
+
+    foreach my $software ( @{ $self->get_software_types() } ) {
+        $consensus{$software}++;
+    }
+
+    my $consensus_number = scalar( grep { defined $_ } values %consensus );
+
+    if ( $consensus_number == 1 ) {
+        return (keys %consensus)[0];    # Only one key.
+    }
+    else {
+        return;    # Beware list context in caller.
+    }
+
+}
+
+#################################
+# Accumulative accessor methods #
+#################################
+
+sub add_missing_features : PRIVATE {
+
+    my ( $self, @args ) = @_;
+
+    if (@args) {
+	my %tmp = @args;
+	while ( my ( $file, $features ) = each %tmp ) {
+
+	    # We use an md5 hash string here rather than the full
+	    # listing to save on memory; typically multiple files will
+	    # share a list of missing features.
+	    my $md5 = Digest::MD5->new();
+	    foreach my $de_id (@$features) { $md5->add($de_id); }
+	    my $ded_md5 = $md5->digest;
+
+	    # Reuse the list generated from previous files. Note that
+	    # we can also try writing the cache to disk and just index
+	    # via $ded_md5 in future.
+	    $missing_features_cache{ident $self}{ $ded_md5 } ||= $features;
+	    $missing_features{ident $self}{ $file }
+		= $missing_features_cache{ident $self}{ $ded_md5 };
+	}
+    }
+
+    return;
+}
+
+sub add_duplicate_features : PRIVATE {
+
+    my ( $self, @args ) = @_;
+
+    if (@args) {
+	my %tmp = @args;
+	while ( my ( $file, $features ) = each %tmp ) {
+
+	    # We use an md5 hash string here rather than the full
+	    # listing to save on memory; typically multiple files will
+	    # share a list of duplicated features.
+	    my $md5 = Digest::MD5->new();
+	    foreach my $de_id (@$features) { $md5->add($de_id); }
+	    my $ded_md5 = $md5->digest;
+
+	    # Reuse the list generated from previous files. Note that
+	    # we can also try writing the cache to disk and just index
+	    # via $ded_md5 in future.
+	    $duplicate_features_cache{ident $self}{ $ded_md5 } ||= $features;
+	    $duplicate_features{ident $self}{ $file }
+		= $duplicate_features_cache{ident $self}{ $ded_md5 };
+	}
+    }
+
+    return;
+}
+
+sub add_duplicate_columns : PRIVATE {
+
+    my ( $self, @args ) = @_;
+
+    if (@args) {
+	my %tmp = @args;
+	@{ $duplicate_columns{ident $self} }{ keys %tmp } = values %tmp;
+    }
+    return $duplicate_columns{ident $self};
+}
+
+sub add_unknown_qts : PRIVATE {
+
+    my ( $self, @args ) = @_;
+
+    if (@args) {
+	my %tmp = @args;
+	@{ $unknown_qts{ident $self} }{ keys %tmp } = values %tmp;
+    }
+    return $unknown_qts{ident $self};
+}
+
+sub add_unknown_hybs : PRIVATE {
+
+    my ( $self, @args ) = @_;
+
+    if (@args) {
+	my %tmp = @args;
+	@{ $unknown_hybs{ident $self} }{ keys %tmp } = values %tmp;
+    }
+    return $unknown_hybs{ident $self};
+}
+
+sub add_arrayexpress_arrays : PRIVATE {
+
+    my ( $self, @args ) = @_;
+
+    if (@args) {
+	my %array = map { $_ => 1 }
+	    @{ $arrayexpress_arrays{ident $self} };
+	foreach my $elem (@args) {
+	    $array{$elem}++;
+	}
+	$arrayexpress_arrays{ident $self} = [ keys %array ];
+    }
+    return $arrayexpress_arrays{ident $self};
+}
+
+sub add_expt_designs : RESTRICTED {
+
+    my ( $self, @args ) = @_;
+
+    if (@args) {
+	my %array = map { $_ => 1 }
+	    @{ $expt_designs{ident $self} };
+	foreach my $elem (@args) {
+	    $array{$elem}++;
+	}
+	$expt_designs{ident $self} = [ keys %array ];
+    }
+    return $expt_designs{ident $self};
+}
+
+sub add_expt_organisms : RESTRICTED {
+
+    my ( $self, @args ) = @_;
+
+    if (@args) {
+	my %array = map { $_ => 1 }
+	    @{ $expt_organisms{ident $self} };
+	foreach my $elem (@args) {
+	    $array{$elem}++;
+	}
+	$expt_organisms{ident $self} = [ keys %array ];
+    }
+    return $expt_organisms{ident $self};
+}
+
+###################
+# Private methods #
+###################
+
+sub confirm_affy_chip_type : PRIVATE {
+
+    my ( $self, $file ) = @_;
+
+    # Takes an Affy Datafile (CHP or CEL), finds its chip_type (needs
+    # to parse the files, wrapped in an eval); compares this to the
+    # value returned from map_affy_accno_to_name for the
+    # ArrayDesign.accession (assuming both these values can be
+    # obtained), and alert the user if they differ.
+
+    my ( $chip_type, $is_gdac_chp );
+
+    local $EVAL_ERROR;
+    my $sighandler = $SIG{__DIE__};
+    delete $SIG{__DIE__};
+    eval {
+	my $fac    = ArrayExpress::Datafile::Affymetrix->new();
+	my $parser = $fac->make_parser( $file->get_path() );
+
+	# FIXME One day we hope to be able to eliminate this call.
+	$parser->parse_header();
+
+	$chip_type = $parser->get_chip_type();
+	$is_gdac_chp
+	    = $parser->isa('ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP');
+    };
+    $SIG{__DIE__} = $sighandler if $sighandler;
+
+    if ( $EVAL_ERROR ) {
+
+	# Affy parser failed, we need to report that.
+	$self->logprint(
+	    'error',
+	    sprintf(
+		qq{Error: Unable to parse Affymetrix file "%s": %s\n},
+		$file->get_name(),
+		$EVAL_ERROR,
+	    ),
+	);
+	$self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+    }
+    else {
+
+	# Check the chip type against the array accession.
+	my $accession;
+	if ( my $array = $file->get_array_design() ) {
+	    $accession = $array->get_accession();
+	}
+	if ( $accession && $chip_type ) {
+	    my $wanted = map_affy_accno_to_name( $accession );
+	    if ( $wanted ) {
+		unless ( $wanted eq $chip_type ) {
+		    $self->logprint(
+			'error',
+			sprintf(
+			    qq{Warning: File "%s" [%s] assigned to incorrect array design (%s).\n},
+			    $file->get_name(),
+			    $chip_type,
+			    $accession,
+			),
+		    );
+		    $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+		}
+	    } else {
+		$self->logprint(
+		    'error',
+		    "Warning: No [chip type] term in array design name for $accession\n",
+		);
+		$self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+	    }
+	}
+	elsif ( ! $is_gdac_chp ) {
+
+	    # We skip this warning for old GDAC CHP files since we'd
+	    # have to parse the file fully (not just the header) to
+	    # get the chip_type.
+	    $self->logprint(
+		'error',
+		sprintf(
+		    "Warning: Either array accession or chip type unavailable for %s.\n",
+		    $file->get_name(),
+		),
+	    );
+	    $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+	}
+    }
+
+    return;
+}
+
+sub check_file_against_loaded_data : PRIVATE {
+
+    # Checks a binary (or ascii CELv3) file's md5 hash against those
+    # computed for AE loaded data.
+    my ( $self, $file ) = @_;
+
+    unless ( $CONFIG->get_AUTOSUBS_DSN() ) {
+	confess(
+	    "Error: Cannot check file against loaded data: no database connection"
+	);
+    }
+
+    require ArrayExpress::AutoSubmission::DB::LoadedData;
+
+    my @previous = ArrayExpress::AutoSubmission::DB::LoadedData->search(
+	md5_hash => $file->get_md5_digest(),
+    );
+
+    if ( scalar @previous ) {
+	my @accessions;
+	foreach my $data ( @previous ) {
+	    foreach my $expt ( $data->experiments() ) {
+		push @accessions, $expt->accession();
+	    }
+	}
+	my $accs = join(", ", @accessions);
+	my $filename = $file->get_name();
+	$self->logprint(
+	    'error',
+	    "Warning: File $filename has been previously loaded for experiments $accs\n",
+	);
+	$self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+    }
+}
+
+sub add_software_types : PRIVATE {
+
+    my ( $self, @args ) = @_;
+
+    if (@args) {
+        push( @{ $software_types{ident $self} }, @args );
+    }
+
+    return $software_types{ident $self};
+
+}
+
+sub cache_user_supplied_arrays : RESTRICTED {
+
+    # If an ADF or AE accession has been specified, sort it out
+    # here. This method should not be called unless a user-supplied
+    # argument has been provided.
+    my ( $self, $filelist ) = @_;
+
+    ref $filelist eq 'ARRAY'
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my $array_designs = {};
+
+    # Default supplied for cases where an accessionless ADF is used.
+    my $design_id = $self->get_array_accession() || 'user supplied';
+
+    # Set the design_ids so that all the files point to the user
+    # supplied design id.
+    foreach my $file (@$filelist) {
+        $file->set_array_design_id($design_id);
+    }
+
+    # Use the user-supplied ADF, overriding anything in the database.
+    if ( $self->get_adf_filename() ) {
+
+        $self->logprint( 'error', "ADF filename ", $self->get_adf_filename,
+            " supplied to script. Ignoring links to array designs experiment annotation.\n"
+        );
+        $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+
+        # Get a reference to a hash with keys corresponding to
+        # period-delimited feature coordinates
+	if ( $self->get_skip_data_checks() ) {
+	    print STDOUT ("Skipping ADF parsing.\n");
+	}
+	else {
+	    my $adf_fh = IO::File->new( $self->get_adf_filename, '<' )
+		or croak( "Error: Unable to open ADF "
+			 . $self->get_adf_filename
+			 . ": $!\n" );
+
+	    my $array = parse_adf(
+		$adf_fh,
+		undef,
+		$self->get_reporter_prefix(),
+		$self->get_compseq_prefix(),
+	    );
+	    $self->set_cached_array_design( $design_id, $array );
+	}
+    }
+
+    # Use a user-supplied ArrayExpress accession number, if available
+    elsif ( $self->get_array_accession() ) {
+
+        $self->logprint(
+            'error',
+            "ArrayExpress accession number ",
+            $self->get_array_accession(),
+            " supplied to script. Ignoring links to array designs in MIAMExpress database.\n"
+        );
+        $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+
+        # Parse the ADF
+        $self->get_ae_arraydesign({
+	    accession => $self->get_array_accession,
+	});
+    }
+
+    else { warn("Warning: No array design associated with file.\n"); }
+
+    return;
+}
+
+sub set_cached_array_design : RESTRICTED {
+
+    my ( $self, $design_id, $array ) = @_;
+
+    $design_id or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    $array && $array->isa('ArrayExpress::Datafile::ArrayDesign')
+	or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    # Store the array design for later perusal.
+    $array_design_cache{ident $self}{$design_id} = $array;
+
+    return;
+}
+
+sub get_cached_array_design : RESTRICTED {
+
+    # Requires an array id
+    my ( $self, $design_id ) = @_;
+
+    $design_id or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    # Return undef if array not in cache.
+    return $array_design_cache{ident $self}{$design_id};
+}
+
+sub populate_file_arraydesigns : RESTRICTED {
+
+    my ( $self, $filelist ) = @_;
+
+    ref $filelist eq 'ARRAY'
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    DATAFILE:
+    foreach my $file (@$filelist) {
+
+        $file->isa('ArrayExpress::Datafile')
+            or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+        # Put the ADF features in the file hash. This links up two
+        # database queries: one datafile-based, the other array design
+        # id-based. The design_id is the key.
+	my $design_id = $file->get_array_design_id();
+
+	# Skip EXP files.
+        next DATAFILE if $file->get_is_exp();
+
+	if ( my $array = $self->get_cached_array_design( $design_id ) ) {
+	    $file->set_array_design( $array );
+	}
+    }
+
+    return;
+}
+
+sub get_ae_arraydesign : RESTRICTED {
+
+    # Takes either an AE database id or an accession number (as part
+    # of the $args hashref), returns an ADF hashref as returned by
+    # Database::parse_adf().
+    my ($self, $args) = @_;
+
+    my ($temp_adf_fh, $accno);
+
+    # Attempt to get the ADF from AE, alongside the accession if only
+    # the database_id is known. We trap any errors here, and avoid
+    # them propagating further up the execution stack.
+    local $EVAL_ERROR;
+    my $sighandler = $SIG{__DIE__};
+    delete $SIG{__DIE__};
+    eval {
+	( $temp_adf_fh, $accno ) = retrieve_AE_adf(
+	    $args->{database_id},
+	    $args->{accession},
+	    $self->log_fh('error'),
+	    $self->get_skip_data_checks(),
+	);
+    };
+    $SIG{__DIE__} = $sighandler if $sighandler;
+
+    # Warn if the AE connection fails.
+    if ( $EVAL_ERROR ) {
+
+	print STDERR (
+	    "Warning: Problem connecting to ArrayExpress:\n  $EVAL_ERROR"
+		. " ...Attempting to continue...\n"
+	    );
+
+	$self->logprint(
+	    'error',
+	    "Warning: ADF retrieval from"
+		. " ArrayExpress failed: $EVAL_ERROR\n"
+	    );
+
+	# Set an error, since now we can't check against the array
+	# design.
+	$self->add_error(
+	    $CONFIG->get_ERROR_PARSEBAD()
+	);
+    }
+
+    # $temp_adf_fh is undef if we're skipping ADF checks.
+    elsif ( $temp_adf_fh ) {
+	
+	my $array = parse_adf(
+	    $temp_adf_fh,
+	    $accno,
+	    $self->get_reporter_prefix(),
+	    $self->get_compseq_prefix(),
+	);
+
+	$self->set_cached_array_design( $accno, $array );
+    }
+    else {
+	print STDOUT ("Skipping ADF parsing.\n");
+    }
+
+    # Note the array accession for further checks later.
+    $self->add_arrayexpress_arrays($accno) if $accno;
+}
+
+sub _check_datafile_consistency : PRIVATE {
+
+    my ( $self, $filelist ) = @_;
+
+    # Sanity check of row and column counts across datafiles (raw, norm
+    # separately) Now checks for inconsistencies only within data files
+    # associated with a given array.  Could check the column headings
+    # for consistency here also FIXME.
+    foreach my $type ( @{ $CONFIG->get_T2M_FILE_TYPES() } ) {
+        my ( $rowno, $colno );
+
+        DATA_FILE:
+        foreach my $file (@$filelist) {
+
+	    # Don't bother comparing non-existent or read-protected
+	    # files against everything else.
+            next DATA_FILE unless ( -r $file->get_path() );
+
+            # Only count file types from $CONFIG->get_T2M_FILE_TYPES().
+            next DATA_FILE
+                unless ( $file->get_data_type() eq $type );
+
+            # Leave out EXP files (these can still be raw, although
+            # they should be EXP).
+            next DATA_FILE if ( $file->get_is_exp() or $file->get_parse_errors() );
+
+            # Affy binary files are assumed okay; other binary file
+            # errors are reported elsewhere.
+            next DATA_FILE if ( $file->get_is_binary() );
+
+            # Count of data rows.
+            $rowno->{ $file->get_array_design_id() }{ $file->get_row_count() }++;
+
+            # Count of column headings.
+            my $column_count = scalar( @{ $file->get_column_headings } );
+            $colno->{ $file->get_array_design_id() }{$column_count}++;
+        }
+
+        # Count the number of different row and column counts across the
+        # data file set.
+        ROW_COUNT:
+        foreach my $array_rowno ( values %$rowno ) {
+            unless ($array_rowno) {
+                $self->logprint( 'error',
+                    "Error: Data row counts not found in any files!\n" );
+                $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+                next ROW_COUNT;
+            }
+            if ( scalar( grep { defined $_ } values %$array_rowno ) > 1 ) {
+                $self->logprint( 'error',
+                    "Warning: Files of type \'$type\' have inconsistent numbers of data rows:\n"
+                );
+                $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+                foreach my $rowcount ( keys %$array_rowno ) {
+                    $self->logprint( 'error',
+                        "        $array_rowno->{$rowcount} files have $rowcount rows;\n"
+                    );
+                }
+            }
+        }
+
+        COLUMN_COUNT:
+        foreach my $array_colno ( values %$colno ) {
+            unless ($array_colno) {
+                $self->logprint( 'error',
+                    "Error: Data column counts not found in any files!\n" );
+                $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+                next COLUMN_COUNT;
+            }
+            if ( scalar( grep { defined $_ } values %$array_colno ) > 1 ) {
+                $self->logprint( 'error',
+                    "Warning: Files of type \'$type\' have inconsistent numbers of columns:\n"
+                );
+                $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+                foreach my $colcount ( keys %$array_colno ) {
+                    $self->logprint( 'error',
+                        "        $array_colno->{$colcount} files have $colcount columns;\n"
+                    );
+                }
+            }
+        }
+    }
+
+    return;
+}
+
+sub _check_exp_params : PRIVATE {
+
+    my ( $self, $exp_filelist ) = @_;
+
+    # List of parameters that can change
+    my @ignore_exp_params = (
+        'Sample Type',
+        'Description',
+        'Chip Type',
+        'Chip Lot',
+        'Scan Date',
+        'Hybridize Date',
+        'Station',
+        'Module',
+        'Operator',
+    );
+
+    # Get a general list of params, divided by section, and an example
+    # value for each.
+    my %paramlist;
+    foreach my $file (@$exp_filelist) {
+	while ( my ($section, $data) = each %{ $file->get_exp_data() } ) {
+	    while ( my ($param, $value) = each %{ $data } ) {
+
+                # We don't care which value ends up here, as long as it's in
+                # at least one of the files.
+                $paramlist{$section}{$param} = $value;
+            }
+        }
+    }
+
+    # Scan through the files looking for missing or different params
+    my %inconsistencies;
+    foreach my $file (@$exp_filelist) {
+	while ( my ($sectionname, $data) = each %paramlist ) {
+
+	    my $expsection = $file->get_exp_data()->{$sectionname};
+
+	    PARAM:
+	    while ( my ($param, $value) = each %{ $data } ) {
+
+		next PARAM if (any { $param eq $_ } @ignore_exp_params);
+
+                unless ( $expsection && exists($expsection->{$param})
+		    && ( $expsection->{$param} eq $value ) ) {
+                    $inconsistencies{$sectionname}{$param}++;
+                }
+                elsif ( $expsection && exists($expsection->{$param}) ) {
+                    $data->{$param} = $expsection->{$param};
+                }
+            }
+        }
+    }
+    return \%inconsistencies;
+}
+
+sub _check_features : PRIVATE {
+
+    # Accepts refs to array of data features and hash of ADF features
+    my ( $self, $data_features, $adf_features, $filename, $hyb_filename,
+        $type )
+        = @_;
+
+    # This subroutine only deals with feature coordinates in Generic
+    # MetaColumn/MetaRow format. GenePix Block/Column/Row formats are
+    # converted into this in Datafile->parse_datafile().
+
+    $type     ||= 'Identifier';
+    $filename ||= q{};
+
+    my @missing_features;
+
+    foreach my $coord_key (@$data_features) {
+        push( @missing_features, $coord_key )
+            unless ( $adf_features->{$coord_key} );
+    }
+
+    # If there are any missing features, print them out
+    if ( scalar @missing_features ) {
+        $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+        $self->add_missing_features( $hyb_filename, \@missing_features );
+        $self->logprint( 'error',
+            "WARNING: ${type}s found in data file ${filename} which are missing from the ADF. See features log file.\n"
+        );
+    }
+
+    return scalar @missing_features;    # Effectively an error code.
+}
+
+sub _check_duplicate_columns : PRIVATE {
+
+    my ( $self, $file, $hyb_filename ) = @_;
+
+    # Check for duplicate column headings
+    my $headings    = $file->get_column_headings();
+    my $hyb_ids     = $file->get_heading_hybs();
+    my %is_indexcol = map { $_ => 1 } @{ $file->get_index_columns() };
+    my %column_name_count;
+    foreach my $i ( 0..$#{ $headings } ) {
+	if ( $is_indexcol{$i} || ! defined $hyb_ids->[$i] ) {
+	    $column_name_count{ $headings->[$i] }++;
+	}
+	else {
+	    $column_name_count{ "$headings->[$i]($hyb_ids->[$i])" }++;
+	}
+    }
+    my @duplicates;
+    foreach my $heading ( keys %column_name_count ) {
+        if ( $column_name_count{$heading} > 1 ) {
+
+            push( @duplicates, $heading );
+
+            # We have to take an average of the nulls in the column to
+            # prevent negative % null values in our report
+	    my $qt_metric = $file->get_data_metrics()->{$heading};
+            if ( $qt_metric && defined( $qt_metric->{notnull} ) ) {
+                $qt_metric->{notnull}
+                    = $qt_metric->{notnull} / $column_name_count{$heading};
+            }
+        }
+    }
+    if (@duplicates) {
+        $self->add_duplicate_columns( $hyb_filename, \@duplicates );
+        $self->logprint(
+            'error',
+            "ERROR: File ",
+            $file->get_name(),
+            " contains ",
+            scalar(@duplicates),
+            " duplicated columns. See column headings log file for details.\n"
+        );
+        $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+    }
+
+    return;
+}
+
+sub _check_duplicate_features : PRIVATE {
+
+    my ( $self, $file, $feature_coords, $hyb_filename ) = @_;
+
+    # Check for duplicate features in the data file
+    my $dupe_feature_check;
+    foreach my $feature (@$feature_coords) {
+        $dupe_feature_check->{$feature}++;
+    }
+
+    my @features;
+    if ( max( values %$dupe_feature_check ) > 1 ) {    # We've got trouble
+
+        $self->logprint( 'error',
+            "ERROR: Duplicate features found in data file ",
+            $file->get_name(), ". See features log file.\n" );
+
+        # FIXME so that reporters/CSs are not flagged in this way?
+        $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+        foreach my $feature ( sort keys %$dupe_feature_check ) {
+            push( @features, $feature )
+                if ( $dupe_feature_check->{$feature} > 1 );
+        }
+        $self->add_duplicate_features( $hyb_filename, \@features );
+    }
+
+    return;
+}
+
+sub _compare_index_to_design : PRIVATE {
+
+    my ( $self, $file, $feature_coords, $hyb_filename ) = @_;
+
+    my $array_design = $file->get_array_design();
+
+    # Check that there's actually an array design for this file (not
+    # so in some Standalone mode checks).
+    unless ( $array_design ) {
+	$self->logprint(
+	    'error',
+	    "Warning: Data file not associated with an array design: ",
+	    $file->get_name(),
+	    ". Skipping identifier check.\n"
+	);
+
+	# Block downstream processing on no check
+	$self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+
+	return;
+    }
+
+    # Feature coordinates.  If only one index column, we assume it is an
+    # identifier column (Reporter or CS).
+    if ( scalar( @{ $file->get_index_columns() } ) == 1 ) {
+
+        # Double-check that we've got what we think we've got.
+        my $identifiers;
+        my $allowed = $CONFIG->get_T2M_INDICES();
+        my $identifier_heading
+            = $file->get_column_headings()->[ $file->get_index_columns()->[0] ];
+
+        if ( $identifier_heading =~ m/$allowed->{FGEM}[0]/i ) {
+            $identifiers = $array_design->get_reporters();
+        }
+        elsif ($identifier_heading =~ m/$allowed->{FGEM_CS}[0]/i
+            || $identifier_heading =~ m/$allowed->{AffyNorm}[0]/i
+	    || $identifier_heading =~ m/$allowed->{GEO}[0]/i ) {
+            $identifiers = $array_design->get_compseqs();
+        }
+        else {
+            die("Internal script error: File ",
+                $file->get_name(),
+                " has unrecognized "
+                    . $file->get_format_type()
+                    . " column heading: $identifier_heading\n"
+            );
+        }
+
+        my $num_identifiers = scalar( grep { defined $_ } values %$identifiers );
+        if ($num_identifiers) {
+            $self->_check_features(
+                $feature_coords, $identifiers, $file->get_name(),
+                $hyb_filename,   'Identifier'
+            );
+
+            # Check that at least 95% of the array identifiers are present.
+            # We may want to relax this independently of Features, below.
+            if ( $#$feature_coords < ( $num_identifiers * 0.95 ) ) {
+                my $pc = ( $#$feature_coords / $num_identifiers ) * 100;
+                $self->logprint(
+                    'error',
+                    sprintf(
+                        "Warning: Data file %s has fewer identifiers than expected from the array design (%d%%).\n",
+                        $file->get_name(), $pc
+                    )
+                );
+                $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+            }
+
+        }
+        else {
+            $self->logprint(
+                'error',
+                "Warning: No suitable design element identifiers retrieved for file ",
+                $file->get_name(),
+                ". Skipping identifier check.\n"
+            );
+
+            # Block downstream processing on no check
+            $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+        }
+    }
+
+    elsif ( scalar( @{ $file->get_index_columns() } ) > 1 ) {
+
+	my $adf_features = $array_design->get_features();
+
+	my $num_adf_features
+            = scalar( grep { defined $_ } values %{ $adf_features } );
+
+	if ($num_adf_features) {
+            my $rc = $self->_check_features(
+		$feature_coords,
+                $adf_features,
+		$file->get_name(),
+		$hyb_filename,
+		'Feature'
+	    );
+
+            # Check that at least 95% of the array features are present
+            if ( $#$feature_coords < ( $num_adf_features * 0.95 ) ) {
+                my $pc = ( $#$feature_coords / $num_adf_features ) * 100;
+                $self->logprint(
+                    'error',
+                    sprintf(
+                        "Warning: Data file %s has fewer features than expected from the array design (%d%%).\n",
+                        $file->get_name(), $pc
+                    )
+                );
+                $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+            }
+
+        }
+        else {
+            $self->logprint( 'error',
+                "Warning: No ADF features retrieved for file ",
+                $file->get_name(), ". Skipping feature check.\n" );
+
+	    # No checks? not good to go, then
+            $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+        }
+    }
+
+    else {           # This really, really should never happen
+        die( "Internal script error: file ",
+            $file->get_name(), " has a negative number of index columns!\n" );
+    }
+
+    return;
+}
+
+sub check_date_format : RESTRICTED {
+
+    # Check date against the AE required format, generate warnings if bad.
+    my ( $self, $date, $type ) = @_;
+
+    my $AE_dateformat = qr/\A \d{4}-\d{2}-\d{2} \z/xms;
+
+    my $datetype = defined ( $type )
+	         ? "Experiment $type date"
+	         : 'Date';
+
+    if ( $date ) {
+	if ( $date !~ $AE_dateformat ) {
+	    $self->logprint(
+		'error',
+		"Warning: $datetype ($date) not in ArrayExpress format (YYYY-MM-DD).\n",
+	    );
+	    $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+	}
+    }
+    else {
+        $self->logprint(
+	    'error',
+            "Warning: $datetype has not been set.\n",
+	);
+        $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+    }
+
+    return;
+}
+
+sub check_mage_bmchars : RESTRICTED {
+
+    # Check a list of MAGE BioMaterials to assess whether they're
+    # sufficiently annotated.
+
+    my ( $self, $bm_list ) = @_;
+
+    foreach my $material ( @{ $bm_list || [] } ) {
+        my $bmc_list = $material->getCharacteristics();
+
+        # Less than 3 is the warning level (arbitrary)
+        if ( ! $bmc_list || ( scalar @$bmc_list < 3 ) ) {
+            $self->logprint(
+		'error',
+		sprintf(
+		    qq{Warning: Material "%s" may not be fully annotated.\n},
+		    $material->getName(),
+		),
+	    );
+            $self->add_error( $CONFIG->get_ERROR_MIAME() );
+        }
+    }
+
+    return;
+}
+
+sub check_mage_labeledextracts : RESTRICTED {
+
+    # Check that all LabeledExtracts have an associated Label.
+
+    my ( $self, $les ) = @_;
+
+    foreach my $le ( @{ $les } ) {
+	my $labels = $le->getLabels() || [];
+
+	unless ( scalar @{ $labels } ) {
+	    $self->logprint(
+		'error',
+		sprintf(
+		    qq{Warning: Labeled Extract "%s" is not associated with a Label.\n},
+		    $le->getName(),
+		),
+	    );
+	    $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+	}
+    }
+
+    return;
+}
+
+sub generate_workflow_from_bags : RESTRICTED {
+
+    my ( $self, $bags, $classes ) = @_;
+
+    # Generate a very basic workflow from the mage bags.
+    # $classes is an arrayref of single-element hashrefs.
+
+    # Set a decent page margin at which to start.
+    my $margin = 1;
+
+    while ( my $instance = shift @{ $classes || [] } ) {
+
+	# NB one element only here!
+        my ( $thing, $name ) = each %$instance;
+
+        my $count = $#{ $bags->{$thing}->() } + 1;
+
+        if ($count) {
+            $self->logprint(
+		'workflow',
+		( ( "  " x $margin ), "$count $name --> \n\n" ),
+	    );
+            $margin++;
+        }
+    }
+
+    $self->logprint(
+	'workflow',
+	( ( "  " x $margin ), "Done.\n" ),
+    );
+
+    return;
+}
+
+sub check_mage_treatments_protapps : RESTRICTED {
+
+    my ( $self, $materials, $type ) = @_;
+
+    foreach my $obj ( @{ $materials } ) {
+	foreach my $treatment ( @{ $obj->getTreatments() || [] } ) {
+
+	    my $found;
+	    foreach my $pa ( @{ $treatment->getProtocolApplications() || [] } ) {
+		$found++ if $pa->getProtocol();
+	    }
+	    unless ( $found ) {
+		my $actionvalue = $treatment->getAction()->getValue();
+		$self->logprint(
+		    'error',
+		    sprintf(
+			qq{Warning: %s "%s" lacks a %s treatment protocol.\n},
+			$type,
+			$obj->getName(),
+			$actionvalue,
+		    ),
+		);
+		$self->add_error( $CONFIG->get_ERROR_MIAME() );
+	    }
+        }
+    }
+
+    return;
+}
+
+sub check_mage_hyb_protapps : RESTRICTED {
+
+    my ( $self, $pbas ) = @_;
+
+    foreach my $pba ( @{ $pbas } ) {
+
+        # Check hyb protocol.
+	if ( my $bac = $pba->getBioAssayCreation() ) {
+	    my $found;
+            foreach my $pa ( @{ $bac->getProtocolApplications() || [] } ) {
+		$found++ if $pa->getProtocol();
+	    }
+	    unless ( $found ) {
+		$self->logprint(
+		    'error',
+		    sprintf(
+			qq{Warning: Hybridization "%s" lacks a hybridization protocol.\n},
+			$pba->getName(),
+		    ),
+		);
+		$self->add_error( $CONFIG->get_ERROR_MIAME() );
+            }
+        }
+        else {
+
+	    # This is actually quite serious, as all hyb PBAs _must_ have a BAC.
+            $self->logprint(
+		'error',
+		qq{Warning: Hybridization PBA lacks a BioAssayCreation object.\n},
+	    );
+            $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+        }
+    }
+
+    return;
+}
+
+sub compare_fvs_to_bmcs : RESTRICTED {
+
+    my ( $self, $fvhashlist, $bmchashlist ) = @_;
+
+    # Takes lists of hashrefs, each hashref containing a single
+    # category => value pair, compares them and logs the results if
+    # there's a BMC variable not reflected in the FVs.
+
+    my %listed_as_variable;
+    foreach my $pair ( @{ $fvhashlist } ) {
+	while ( my ( $cat, $val ) = each %{ $pair } ) {
+	    $listed_as_variable{$cat} = 1;
+	}
+    }
+
+    my %bmc_valuecount;
+    foreach my $pair ( @{ $bmchashlist } ) {
+	while ( my ( $cat, $val ) = each %{ $pair } ) {
+	    $bmc_valuecount{$cat}{$val}++;
+	}
+    }
+
+    while ( my ( $cat, $valhash ) = each %bmc_valuecount ) {
+
+	# More than one value constitutes a variable.
+	if ( scalar(grep { defined $_ } values %{ $valhash }) > 1 ) {
+	    unless ( $listed_as_variable{$cat} ) {
+		$self->logprint(
+		    'error',
+		    qq{Warning: Experimental variable "$cat" is not listed as a FactorValue.\n},
+		);
+		$self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+	    }
+	}
+    }
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Curator/Logger.pm b/lib/ArrayExpress/Curator/Logger.pm
new file mode 100644
index 0000000..5c499cf
--- /dev/null
+++ b/lib/ArrayExpress/Curator/Logger.pm
@@ -0,0 +1,409 @@
+#!/usr/bin/env perl
+#
+# Superclass to provide basic logging facilities to
+# ExperimentChecker.pm and Tab2MAGE.pm.
+#
+# Tim Rayner 2006, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id$
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../index.html">
+                <img src="../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: ExperimentChecker.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Curator::Logger
+
+=head1 SYNOPSIS
+
+ use base qw/ArrayExpress::Curator::Logger/;
+
+=head1 DESCRIPTION
+
+This module is subclassed by ExperimentChecker and Tab2MAGE classes to
+provide them both with logging and error recording facilities.
+
+=head1 OPTIONS
+
+The objects created by this module can be instantiated with the
+following options.
+
+=over 2
+
+=item C<log_to_current_dir>
+
+A flag indicating whether to write log files to the current directory
+or not. Note that this has been overloaded such that any true value is
+now interpreted as the name of a directory into which to write.
+
+=item C<clobber>
+
+A flag indicating whether to silently overwrite old logs or not.
+
+=item C<progname>
+
+A program name string, used in log headers.
+
+=item C<version>
+
+A version string, used in log headers.
+
+=back
+
+=head1 PUBLIC METHODS
+
+=over 2
+
+=item C<add_error( $error )>
+
+Takes an integer, bitwise ORs it with the object's internally-stored
+error, records and returns the result. Used to store the overall error
+code determining suitability for MAGE-ML export. The final value is
+accessible using get_error().
+
+=item C<logfiles( $type )>
+
+Given the desired log file type, returns the desired filename
+template. Otherwise, returns a list of log file types.
+
+=item C<log_fh( $type )>
+
+Given the desired log file type, returns the opened filehandle.
+
+=item C<logprint( $type, $message )>
+
+Given the desired log file type and a message, prints the message to
+that log file. Linebreaks are not added to the output.
+
+=item C<logprint_line( $type, $message )>
+
+Given the desired log file type and a message, prints the message as
+part of a section-delimiting line in the log file. In this case
+linebreaks are added to the output.
+
+=item C<localize_logfiles( $args )>
+
+Given an optional hashref argument (keys: "volume", "directory",
+"name"), attempts to localize the logfiles to the directory structure
+given. Overridden by the log_to_current_dir attribute when setting log
+file location.
+
+=back
+
+=head1 AVAILABLE LOG TYPES AND TEMPLATES
+
+=over 2
+
+=item error    => 'expt_error.log',
+
+=item report   => 'expt_report.log',
+
+=item workflow => 'expt_biomaterials.log',
+
+=item feature  => 'expt_feature.log',
+
+=item columns  => 'expt_columnheadings.log',
+
+=item sample   => 'expt_samples.log',
+
+=item protocol => 'expt_protocols.log',
+
+=item miame    => 'expt_miame.log',
+
+=item aedw     => 'expt_aedw.log',
+
+=item tab2mage => 'tab2mage.log',
+
+=item magetab  => 'magetab.log',
+
+=back
+
+=head1 SEE ALSO
+
+=over 1
+
+=item L<ArrayExpress::Curator::ExperimentChecker>
+
+=item L<ArrayExpress::Curator::Tab2MAGE>
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2008.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Curator::Logger;
+
+use strict;
+use warnings;
+
+use Carp;
+use File::Spec;
+use File::Copy;
+use Class::Std;
+use Readonly;
+
+use ArrayExpress::Curator::Common qw(date_now);
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+my (%logfiles, %log_fh) : ATTRS;
+
+# Attributes available for initialization.
+my %log_to_current_dir : ATTR( :get<log_to_current_dir>, :init_arg<log_to_current_dir>, :default<undef> );
+my %clobber            : ATTR( :name<clobber>,  :default<undef> );
+my %progname           : ATTR( :name<progname>, :default<undef> );
+my %version            : ATTR( :name<version>,  :default<undef> );
+
+# Accumulative accessors; each of these has an "add_" method defined below.
+my %error              : ATTR( :get<error>,     :default<0>  );
+
+Readonly my $REPORT_WIDTH => 80;
+
+sub add_error {
+
+    # Takes an integer, bitwise ORs it with $error{ident $self},
+    # stores and returns the result. Used to store the overall error
+    # code determining suitability for MAGE-ML export.
+
+    my ( $self, $err ) = @_;
+
+    $error{ident $self} ||= 0;
+    if ($err) { $error{ident $self} |= $err }
+
+    return $error{ident $self};
+}
+
+sub logfiles {
+
+    # Given an argument, returns the desired filename
+    # template. Otherwise, returns a list of log file types.
+
+    my ( $self, $wanted ) = @_;
+
+    # Log files. The following are just templates. The files will be
+    # written in the top-level directory for the submission unles the -p
+    # command-line option is supplied.
+    my %filenames = (
+        error    => 'expt_error.log',
+        report   => 'expt_report.log',
+        workflow => 'expt_biomaterials.log',
+        feature  => 'expt_feature.log',
+        columns  => 'expt_columnheadings.log',
+        sample   => 'expt_samples.log',
+        protocol => 'expt_protocols.log',
+        miame    => 'expt_miame.log',
+        aedw     => 'expt_aedw.log',
+        tab2mage => 'tab2mage.log',
+        magetab  => 'magetab.log',
+    );
+
+    # Fill in any blanks in our logfile list
+    @filenames{ keys %{ $logfiles{ident $self} } }
+	= values %{ $logfiles{ident $self} };
+
+    $logfiles{ident $self} = \%filenames;
+
+    if ($wanted) {
+        unless ( $logfiles{ident $self}{$wanted} ) {
+            croak(
+                qq{Error: Log file of type "$wanted" has no predefined filename.\n}
+            );
+        }
+        else {
+            return $logfiles{ident $self}{$wanted};
+        }
+    }
+    else {
+        return [ keys %{ $logfiles{ident $self} } ];
+    }
+}
+
+sub log_fh {
+
+    # Requires a log file type argument, returns the opened filehandle.
+
+    my ( $self, $wanted ) = @_;
+
+    $wanted or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    $log_fh{ident $self} ||= {};
+
+    unless ( $log_fh{ident $self}{$wanted} ) {
+
+        # Some of our log files we prefer without a header. This is
+        # especially true if the file is normally empty (file size 0) and
+        # only written to upon error.
+        my %no_headers = ( columns => 1, feature => 1 );
+
+        my @open_params =
+            $no_headers{$wanted}
+            ? ()
+            : ( $self->get_progname(), $self->get_version() );
+
+        my $filename = $self->logfiles($wanted)
+            or croak(qq{Error: Undefined log file type "$wanted".\n});
+
+        my $fh = $self->open_log_file( $filename, @open_params );
+
+        croak("Error: File $filename exists. User canceled script execution.")
+            unless $fh;
+
+        $log_fh{ident $self}{$wanted} = $fh;
+    }
+
+    return $log_fh{ident $self}{$wanted};
+
+}
+
+sub logprint {
+
+    my ( $self, $type, @messages ) = @_;
+
+    print { $self->log_fh($type) }(@messages);
+
+    return;
+}
+
+sub logprint_line {
+
+    my ( $self, $type, @messages ) = @_;
+
+    my $message = join(q{}, @messages);
+    my $line = $message
+	? q{---} . " $message "
+	    . (q{-} x ($REPORT_WIDTH - (length($message) + 5)))
+	: q{-} x $REPORT_WIDTH;
+    $self->logprint($type, "$line\n");  
+
+    return;
+}
+
+sub change_logfilename : PRIVATE {
+
+    my ( $self, $type, $filename ) = @_;
+
+    $type     or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    $filename or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    $logfiles{ident $self}{$type} = $filename;
+
+    return;
+}
+
+sub localize_logfiles {
+
+    my ( $self, $args ) = @_;
+
+    $args ||= {};
+
+    # Localize to current directory unless told otherwise.
+    $args->{directory} ||= q{.};
+
+    # Only change the logfile names (not the whole path) if we want
+    # them in the current working directory anyway.
+    if ( defined($self->get_log_to_current_dir) ) {
+        $args->{directory} = $self->get_log_to_current_dir || q{.};
+    }
+
+    foreach my $logtype ( @{ $self->logfiles } ) {
+
+	# Insert string into each logfile name.
+	my $filename = $self->logfiles($logtype);
+	if ( $args->{name} ) {
+	    $filename =~ s/(expt\_)(.*.log)/$1$args->{name}\_$2/;
+	}
+	$self->change_logfilename(
+	    $logtype,
+	    File::Spec->catpath(
+		$args->{volume},
+		$args->{directory},
+		$filename
+	    )
+	);
+    }
+
+    return;
+}
+
+sub open_log_file : PRIVATE {
+    my ( $self, $file, $programname, $version ) = @_;
+
+    # Test for pre-existing file, act as necessary.
+    if ( -e $file ) {
+
+        # Handle the clobber option.
+        unless ( $self->get_clobber() ) {
+            print STDERR ( "\n\nFile \'$file\' aleady exists. "
+                    . "Overwrite? [Y(es)\/N(o)\/A(ll)]\n" );
+            chomp( my $answer = lc <STDIN> );
+
+            SWITCH:
+            {
+                $answer eq 'y' && do { last SWITCH; };
+                $answer eq 'a' && do { $self->set_clobber(1); last SWITCH; };
+                return;
+            }
+        }
+
+        # Create a backup of the previous file (delete old backup first).
+	if ( -e "$file.bak" ) {
+	    unlink "$file.bak" or die(
+		"Error: Could not overwrite old backup log file $file.bak: $!\n"
+	    );
+	}
+        copy( $file, "$file.bak" )
+            or die("Error: Could not create backup log file $file.bak: $!\n");
+    }
+    my $filehandle = IO::File->new( $file, '>' )
+        or croak("Error: Could not open file $file: $!\n");
+
+    # Set logs for unbuffered output (I'm tired of waiting...)
+    $filehandle->autoflush();
+
+    # Print a header
+    if ( defined($version) ) {
+        my $title = "$programname version $version log file output "
+	    . date_now();
+        print $filehandle (
+            "$title\n" . ( "=" x ( length($title) ) ) . "\n\n" );
+    }
+
+    return ( $filehandle );
+}
+
+1;
diff --git a/lib/ArrayExpress/Curator/MAGE.pm b/lib/ArrayExpress/Curator/MAGE.pm
new file mode 100644
index 0000000..b3bea89
--- /dev/null
+++ b/lib/ArrayExpress/Curator/MAGE.pm
@@ -0,0 +1,2504 @@
+#!/usr/bin/env perl
+#
+# Module to provide basic access to MAGE objects and MAGE-ML files
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: MAGE.pm 2069 2008-06-04 14:33:52Z tfrayner $
+#
+
+package ArrayExpress::Curator::MAGE;
+
+use strict;
+use warnings;
+
+use Carp;
+use Bio::MAGE qw(:ALL);
+
+# Used for checking FVs (Value vs. Measurement)
+use Scalar::Util qw(looks_like_number);
+use List::Util qw(first);
+use English qw( -no_match_vars );
+
+use ArrayExpress::Curator::Common qw(decamelize);
+
+use ArrayExpress::Curator::MAGE::Definitions qw(
+    $OE_CAT_ACTION
+    $OE_CAT_BIOSAMPLETYPE
+    $OE_CAT_MATERIALTYPE
+    $OE_CAT_PROTOCOLTYPE
+    $OE_CAT_SOFTWARETYPE
+    $OE_CAT_HARDWARETYPE
+    $OE_CAT_DERIVEDBIOASSAYTYPE
+    $OE_CAT_EXPERIMENTALFACTORCATEGORY
+    $OE_CAT_DATATYPE
+    $OE_CAT_SCALE
+    $OE_CAT_RELEASEDATE
+    $OE_CAT_SUBMISSIONDATE
+    $OE_CAT_ROLE
+    $OE_VAL_SYNTHETICDNA
+    $OE_VAL_TOTALRNA
+    $OE_VAL_GENOMICDNA
+    $OE_VAL_WHOLEORGANISM
+    $OE_VAL_ORGANISMPART
+    $OE_VAL_EXTRACT
+    $OE_VAL_NOTEXTRACT
+    $OE_VAL_POOL
+    $OE_VAL_SPECIFIEDBIOMATERIALACTION
+    $OE_VAL_NUCLEICACIDEXTRACTION
+    $OE_VAL_IMMUNOPRECIPITATE
+    $OE_VAL_LABELING
+    $OE_VAL_FLOAT
+    $OE_VAL_UNKNOWN
+    $OE_VAL_LINEARSCALE
+    $AE_LABELCOMPOUND_PREFIX
+    $AE_CHANNEL_PREFIX
+    $MAGE_UNITS
+);
+
+use base 'Exporter';
+our @EXPORT_OK = qw(
+    read_mage
+    write_mage
+    unique_identifier
+    postprocess_mage
+    make_container
+    new_ontologyentry
+    new_person
+    new_biosource
+    new_biosample
+    new_extract
+    new_immunoprecipitate
+    new_labeledextract
+    new_labelcompound
+    new_image
+    new_hybridization
+    new_scan
+    new_physicalbioassay
+    new_featureextraction
+    new_measuredbioassay
+    new_measuredbioassaydata
+    new_derivedbioassay
+    new_producertransformation
+    new_derivedbioassaydata
+    new_bioassaymap
+    new_quantitationtypemap
+    new_protocol
+    new_parameter
+    new_hardwareapplication
+    new_software
+    new_hardware
+    new_armanuf
+    new_array
+    new_factorvalue
+    new_experimentalfactor
+    new_quantitationtype
+    new_qtd
+    add_summary_stats
+    update_factorvalues
+);
+
+# Internal identifier counter
+my $ID_Count = 0;
+
+#
+# make_container
+#
+# Takes:   coderef to a sub which returns a value to be entered uniquely in the closure hash
+#          flag indicating whether unique identifiers should be passed to that coderef
+#
+# Returns: coderef to a wrapper sub incorporating a closure around the original coderef
+#
+
+# We *need* to postpone @_ unpacking here as part of the closure
+# creation. We therefore turn off Perl::Critic carping about this:
+
+## no critic RequireArgUnpacking
+sub make_container {
+
+    my ( $coderef, $pass_identifier ) = splice( @_, 0, 2 );
+
+    my $hash = {};
+
+    # It's actually important to use @_ in the following anon sub
+    # declaration, rather than some copy of whatever arguments were
+    # passed into this function at container creation time.
+    return
+        sub { _container_closure( $hash, $coderef, $pass_identifier, @_ ) };
+
+}
+## use critic RequireArgUnpacking
+
+#
+# _container_closure
+#
+# Takes:   hashref for storing values
+#          coderef to sub returning those values
+#          flag indicating whether to pass the identifier through
+#          identifier for the value returned by coderef
+#          [list of arguments passed to coderef]
+#
+# Returns: A unique value returned by coderef and stored in hashref container
+#
+#    * If no arguments are supplied to be passed to the coderef, *
+#    * all the values (objects?) in the container are returned.  *
+#
+sub _container_closure {
+
+    my ( $hash, $coderef, $pass_identifier, @args ) = @_;
+
+    # Return all the objects (unsorted) if no arguments.
+    return [ values %$hash ] unless scalar @args;
+
+    my $identifier = shift @args;
+
+    # Must have an identifier here. Confess for better debugging.
+    confess("Internal Error: Undefined local identifier passed to container function.\n")
+        unless defined($identifier);
+
+    # If identifier passed but no attributes hashref, just return the
+    # desired object without updating it.
+    if ( ! scalar @args || ( ref $args[0] ne 'HASH' ) ) {
+
+	if ( $args[0] && $args[0] eq 'delete' ) {
+
+	    # Remove the object from the container.
+	    my $rc = defined($hash->{$identifier});
+	    delete $hash->{$identifier};
+
+	    return $rc;
+	}
+	else {
+
+	    # Query lookup for the identified object. This does not
+	    # attempt to update the object (pass a hashref in @args to update).
+	    return $hash->{$identifier};
+	}
+    }
+
+    # Add a pre-existing object to the passed arguments if found.
+    if ( $hash->{$identifier} ) {
+        push( @args, ( $hash->{$identifier} ) );
+    }
+
+    # Add back the identifier to @args if required
+    unshift( @args, $identifier ) if $pass_identifier;
+
+    # Get a new object, or an updated object if we found an old one
+    my @objects = ( $coderef->(@args) );
+
+    # Store the object in the container
+    $hash->{$identifier} = $objects[0];
+
+    return wantarray ? @objects : $objects[0];
+
+}
+
+sub unique_identifier {
+
+    return ++$ID_Count;
+
+}
+
+sub read_mage {
+
+    my ( $magepath, $dtd, $verbose ) = @_;
+
+    # Invoke our XML::Reader object
+    require Bio::MAGE::XML::Reader;
+    my $sax1 = 1;
+
+    #  my $verbose = 5;
+    my $logfh         = \*STDERR;
+    my $external_data = 1;
+    my $reader        = Bio::MAGE::XML::Reader->new(
+        sax1          => $sax1,
+        verbose       => $verbose,
+        log_file      => $logfh,
+        external_data => $external_data,
+    );
+
+    my $dir = ( File::Spec->splitpath($magepath) )[1];
+    my $localdtd
+        = $dir ? File::Spec->catfile( $dir, "MAGE-ML.dtd" ) : "MAGE-ML.dtd";
+
+    my $dtd_delete_flag;
+    unless ( -e $localdtd ) {
+        print STDOUT ("Linking $dtd to $localdtd\n");
+        symlink( $dtd, $localdtd )
+            or croak("Error symlinking DTD file: $!\n");
+        $dtd_delete_flag++;
+    }
+
+    # Actually read in the file
+    my $mage = $reader->read($magepath);
+
+    # Remove the MAGE-ML.dtd symlink, if we created it
+    if ( $dtd_delete_flag and -l $localdtd ) {
+        print STDOUT ("Deleting $localdtd symlink.\n");
+        unlink($localdtd) or croak("Error removing $localdtd symlink: $!\n");
+    }
+    return $mage;
+}
+
+sub write_mage {
+
+    my ( $mage, $fh, $dataformat, $version ) = @_;
+
+    require Bio::MAGE::XML::Writer;
+    $version    ||= "1.1";
+    $dataformat ||= "tab delimited";
+
+    my $writer = Bio::MAGE::XML::Writer->new(
+        fh              => $fh,
+        external_data   => 1,
+        data_format     => $dataformat,
+        cube_holds_path => 1,
+        public_id       => "-//OMG//DTD MAGE-ML $version//EN",
+    );
+    $writer->write($mage);
+
+    return;
+}
+
+sub postprocess_mage {
+    my ( $ded_fh, $old_fh, $new_fh, $dummy_id, $data_format ) = @_;
+
+    foreach my $filehandle ( $ded_fh, $old_fh, $new_fh ) {
+        if ($filehandle) {
+            seek( $filehandle, 0, 0 )
+                or croak("Error rewinding filehandle: $!\n");
+        }
+    }
+
+    my $error = 0;
+
+    MAGE_LINE:
+    while ( my $line = <$old_fh> ) {
+
+        # Skip over any dummy FeatureDimensions we've been told about
+        if (   $dummy_id
+            && $line =~ m!<FeatureDimension identifier="$dummy_id"! ) {
+            while ( my $skipped = <$old_fh> ) {
+                next MAGE_LINE if ( $skipped =~ m!</FeatureDimension>! );
+            }
+        }
+
+        # Insert our DEDs just before the close tag
+        if ( $ded_fh && $line =~ m!</DesignElementDimension_assnlist>! ) {
+            while ( my $fdline = <$ded_fh> ) {
+                print $new_fh $fdline or $error++;
+            }
+        }
+
+        if ( my ($filename)
+            = ( $line =~ m/DataExternal filenameURI="([^\"]+)"/ ) ) {
+            if ( $data_format->{$filename} ) {
+                until ( $line
+                        =~ s/dataFormat="[^\"]+"/q{dataFormat="}.$data_format->{$filename}.q{"}/e
+                    ) {
+                    print $new_fh $line;
+                    $line = <$old_fh>;
+                }
+            }
+        }
+
+        print $new_fh $line or $error++;
+    }
+
+    return !$error;
+
+}
+
+sub new_ontologyentry {
+
+    my ($args) = @_;
+
+    my $obj = Bio::MAGE::Description::OntologyEntry->new(
+        category => $args->{category},
+        value    => $args->{value}
+    );
+
+    # OntologyReference
+    ( $args->{database_ref} && $args->{database_accession} ) && do {
+        my $databaseentry = Bio::MAGE::Description::DatabaseEntry->new(
+            database  => $args->{database_ref},
+            accession => $args->{database_accession}
+        );
+        $obj->setOntologyReference($databaseentry);
+    };
+
+    return $obj;
+
+}
+
+sub _parse_person_name {
+
+    my $namestring = shift;
+
+    # Parse out the first and last names, and middle initials.
+    # N.B. this parsing isn't perfect, but will deal with things
+    # like "Mary Ann F.X. van Doone"
+
+    # Strip surrounding whitespace:
+    $namestring =~ s/\s*(.*?)\s*/$1/;
+
+    # Allow "Rayner, Tim F." form:
+    $namestring =~ s/(.*)\s*\,\s*(.*)/$2 $1/;
+    my @name_elements = split /[\.\s]+/, $namestring;
+
+    foreach my $part (@name_elements) { $part =~ s/[\. ]//g; }
+
+    my $last  = pop @name_elements;
+    my $first = shift @name_elements;
+
+    NAME_PART:
+    while ( my $part = shift(@name_elements) ) {
+
+        # Hopefully this will take care of van Whatever
+        unless ( ( length($part) == 1 ) || ( $part eq lc($part) ) ) {
+            $first = "$first $part";
+        }
+        else {
+            unshift( @name_elements, $part );
+            last NAME_PART;
+        }
+    }
+
+    NAME_PART:
+    while ( my $part = pop(@name_elements) ) {
+        unless ( length($part) == 1 ) {
+            $last = "$part $last";
+        }
+        else {
+            push( @name_elements, $part );
+            last NAME_PART;
+        }
+    }
+
+    my $midinitials;
+    while ( my $part = shift(@name_elements) ) {
+        $midinitials .= "$part.";
+    }
+
+    return ( $first, $midinitials, $last );
+
+}
+
+sub new_person {
+
+    my ( $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::AuditAndSecurity::Person->new unless $obj;
+
+    # Take the name if given, otherwise just the email address;
+    # otherwise croak.
+    my $name_id;
+    my ( $first, $midinitials, $last );
+    if ( $args->{name} ) {
+	( $first, $midinitials, $last ) = _parse_person_name($args->{name});
+	$name_id = "$first";
+	$name_id .= " $midinitials" if $midinitials;
+	$name_id .= " $last";
+    }
+    elsif ( $args->{email} ) {
+	$name_id = $args->{email};
+    }
+    else {
+	croak("Error: Neither person name nor email address set.");
+    }
+
+    # Identify the Person object (limit name part of identifier to 50
+    # chars for AE database column safety).
+    my $identifier = sprintf(
+	"%s:%s.Person",
+	$args->{namespace},
+	substr($name_id, 0, 50),
+    );
+    $obj->setIdentifier($identifier)
+        unless $obj->getIdentifier;
+
+    # Sort out the name if available.
+    $last && do {
+	$obj->setLastName($last)           unless $obj->getLastName;
+    };
+    $first && do {
+	$obj->setFirstName($first)         unless $obj->getFirstName;
+    };
+    $midinitials && do {
+        $obj->setMidInitials($midinitials) unless $obj->getMidInitials;
+    };
+
+    # Email, role and organization.
+    $args->{email} && do {
+        $obj->setEmail( $args->{email} ) unless $obj->getEmail;
+    };
+    $args->{role} && do {
+        my $found;
+        if ( my $preexisting_roles = $obj->getRoles ) {
+            my $new_role = $args->{role};
+            $found = first { $_->getValue eq $new_role } @$preexisting_roles;
+        }
+        unless ($found) {
+            my $role = new_ontologyentry(
+                {   category => $OE_CAT_ROLE,
+                    value    => $args->{role}
+                }
+            );
+            $obj->addRoles($role);
+        }
+    };
+    ( $args->{organization} || $args->{address} ) && do {
+
+	# Use just the first 50 characters in identifier creation to
+	# avoid AE database column limits.
+        my $identifier = sprintf(
+	    "%s:%s.Organization",
+	    $args->{namespace},
+	    substr(( $args->{organization} || $args->{address} ), 0, 50),
+	);
+        my $organization = Bio::MAGE::AuditAndSecurity::Organization->new(
+            identifier => $identifier,
+            name       => ( $args->{organization} || $args->{address} ),
+            address    => $args->{address},
+        );
+        $obj->setAffiliation($organization)  unless $obj->getAffiliation;
+        $obj->setAddress( $args->{address} ) unless $obj->getAddress;
+    };
+
+    return $obj;
+}
+
+sub _check_pooling {
+
+    my ( $obj, $protocol, $identifier_template ) = @_;
+
+    return unless ( $obj && $protocol );
+
+    # Count our biomaterials
+    my %bmm_hash;
+    foreach my $treatment ( @{ $obj->getTreatments() || [] } ) {
+
+        # Check the protocol hasn't already been applied in this treatment set
+        my $found;
+        if ( my $preexisting_protclapps
+            = $treatment->getProtocolApplications ) {
+            my $new_identifier = $protocol->getIdentifier;
+            $found
+                = first { $_->getProtocol()->getIdentifier eq $new_identifier }
+                @$preexisting_protclapps;
+        }
+	return if ( $found );
+
+        # Make a hash of the BioMaterialMeasurements
+        if ( my $source_bmms = $treatment->getSourceBioMaterialMeasurements )
+        {
+            foreach my $bmm (@$source_bmms) {
+                $bmm_hash{ $bmm->getBioMaterial()->getIdentifier } = $bmm;
+            }
+        }
+    }
+
+    # Add the new treatment (with each BMM) if more than one BMM
+    my $treatments;
+    if ( scalar( grep { defined $_ } values %bmm_hash ) > 1 ) {  # More than one BMM
+        $treatments = new_treatments(
+            {   protocols           => [$protocol],
+                identifier_template => $identifier_template,
+		default_action      => $OE_VAL_POOL,
+            }
+        );
+
+        # Correct the order of the treatment.
+        foreach my $treatment (@$treatments) {
+            $treatment->setOrder(0);    # This appears to be legal
+        }
+
+        # Put every BMM in the pooling treatment
+        foreach my $bmm ( values %bmm_hash ) {
+            $treatments = _update_treatments( $treatments, $bmm );
+        }
+    }
+
+    return $treatments;
+}
+
+sub new_labelcompound {
+
+    my ( $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::BioMaterial::Compound->new unless $obj;
+
+    $obj->setIdentifier( $AE_LABELCOMPOUND_PREFIX . $args->{dye} )
+        unless $obj->getIdentifier;
+
+    $obj->setName( $args->{dye} ) unless $obj->getName;
+
+    if ( $args->{indices} ) {
+	foreach my $new ( @{ $args->{indices} } ) {
+	    my $found;
+	    if ( my $preexisting = $obj->getCompoundIndices() ) {
+		my $new_cat = $new->getCategory();
+		my $new_val = $new->getValue();
+		$found = first {
+		    $_->getCategory() eq $new_cat
+		    && $_->getValue() eq $new_val
+		} @$preexisting;
+	    }
+	    $obj->addCompoundIndices( $new ) unless $found;
+	}
+    }
+
+    return ($obj);
+
+}
+
+sub new_labeledextract {
+
+    my ( $name, $args, $obj ) = @_;
+
+    # Set some default values.
+    $args->{material_type}  ||= $OE_VAL_SYNTHETICDNA;
+
+    # Create a new object if we don't already have one
+    $obj = Bio::MAGE::BioMaterial::LabeledExtract->new unless $obj;
+
+    # Identifier, Name , MaterialType
+    my $identifier = "$args->{identifier_template}.LabeledExtract"
+        . ( $args->{dye} ? ".$args->{dye}" : q{} );
+    my $materialtype = &new_ontologyentry(
+        {   category => $OE_CAT_MATERIALTYPE,
+            value    => $args->{material_type},
+        }
+    );
+
+    $obj->setIdentifier($identifier)      unless $obj->getIdentifier;
+    $obj->setName($args->{name} || $name) unless $obj->getName;
+    $obj->setMaterialType($materialtype)  unless $obj->getMaterialType;
+
+    # Treatments
+    if ( $args->{derived_from} ) {
+	my $source_bmm
+	    = Bio::MAGE::BioMaterial::BioMaterialMeasurement->new(
+		bioMaterial => $args->{derived_from},
+	    );
+
+	my $treatments = new_treatments(
+	    {   source_bmm          => $source_bmm,
+		protocols           => $args->{protocols},
+		parameters          => $args->{parameters},
+		parameter_bag       => $args->{parameter_bag},
+		identifier_template => "$args->{identifier_template}."
+		    . ( $args->{dye} || q{} ),
+		default_action      => $OE_VAL_LABELING,
+	    }
+	);
+
+	if ( $obj->getTreatments ) {
+	    $obj->setTreatments(
+		&_update_treatments( $obj->getTreatments, $source_bmm ) );
+	}
+	else {
+	    $obj->setTreatments($treatments);
+	}
+    }
+
+    # Pooling
+    if ( $args->{pooling} ) {
+        my $pooling_treatments = _check_pooling(
+	    $obj,
+	    $args->{pooling},
+            $args->{identifier_template},
+	);
+        $obj->addTreatments(@$pooling_treatments) if $pooling_treatments;
+    }
+
+    # Labels
+    if ( $args->{dye} ) {
+
+        my $compound = $args->{compound_bag}
+            ->( $args->{dye}, { dye => $args->{dye} } );
+
+        my $found;
+        if ( my $preexisting_labels = $obj->getLabels ) {
+            my $new_identifier = $compound->getIdentifier;
+            $found = first { $_->getIdentifier eq $new_identifier }
+                @$preexisting_labels;
+        }
+        $obj->addLabels($compound) unless $found;
+    }
+
+    return $obj;
+
+}
+
+sub new_treatments {
+
+    my ($args) = @_;
+
+    my @treatments;
+
+    # Protocols and parameters are both arrays which must be fed to this
+    # sub in synchronous order:
+    # $protocols = [procotol1, protocol2, protocol3]
+    # $parameters = [param set 1, param set 2, param set 3]
+    foreach my $protocol ( @{ $args->{protocols} } ) {
+
+        my $ptype       = $protocol->getType;
+        my $actionvalue = $ptype->getValue;
+
+        my $action = &new_ontologyentry(
+            {   category => $OE_CAT_ACTION,
+                value    => $actionvalue,
+            }
+        );
+
+        my $protocolapplication
+            = Bio::MAGE::Protocol::ProtocolApplication->new(
+            protocol     => $protocol,
+            activityDate => 'n/a',
+
+            # FIXME, (also performers, hardware & software applications)
+            );
+
+        $args->{parameters} && do {
+
+            my $param_set = shift( @{ $args->{parameters} } )
+                or croak(
+                "Internal script error: Insufficient parameter sets for protocol "
+                    . $protocol->getIdentifier
+                    . ".\n" );
+
+            foreach my $param_id ( sort keys %{$param_set} ) {
+
+                my $parameter = $args->{parameter_bag}->( $param_id );
+
+		unless ( $parameter ) {
+		    croak(qq{Internal error: Parameter "$param_id" not defined.\n});
+		}
+
+                $protocolapplication->addParameterValues(
+                    Bio::MAGE::Protocol::ParameterValue->new(
+                        value         => $param_set->{$param_id},
+                        parameterType => $parameter
+                    )
+                );
+
+            }
+        };
+
+        my $treatment = Bio::MAGE::BioMaterial::Treatment->new(
+            identifier =>
+                "$args->{identifier_template}.$actionvalue.Treatment",
+            action               => $action,
+            protocolApplications => [$protocolapplication],
+            order                => ( $#treatments + 2 ),
+        );
+
+        $treatment->setSourceBioMaterialMeasurements(
+            [ $args->{source_bmm} ] )
+            if $args->{source_bmm};
+
+        push( @treatments, $treatment );
+    }
+
+    unless ( scalar @treatments ) {
+
+        # We want at least one treatment, even if it doesn't have a
+        # protocolapplication, to hang our source_bmm off.
+
+        # Generic action.
+	my $action_val = $args->{default_action}
+	              || $OE_VAL_SPECIFIEDBIOMATERIALACTION;
+        my $action = new_ontologyentry(
+            {   category => $OE_CAT_ACTION,
+                value    => $action_val,
+            }
+        );
+
+	# Make sure the identifier is truly unique; otherwise there is
+	# the risk of duplicate Treatment objects.
+	my $uniq_number = unique_identifier();
+        my $treatment = Bio::MAGE::BioMaterial::Treatment->new(
+            identifier => "$args->{identifier_template}.$uniq_number.Treatment",
+            action     => $action,
+            order      => ( $#treatments + 2 ),
+        );
+        $treatment->setSourceBioMaterialMeasurements(
+            [ $args->{source_bmm} ] )
+            if $args->{source_bmm};
+        push( @treatments, $treatment );
+    }
+
+    return \@treatments;
+
+}
+
+sub _update_treatments {    # Used in e.g. pooling
+
+    my ( $treatments, $source_bmm ) = @_;
+
+    if ($source_bmm) {
+        foreach my $treatment (@$treatments) {
+
+            my $old_bmms       = $treatment->getSourceBioMaterialMeasurements;
+            my $new_identifier = $source_bmm->getBioMaterial()->getIdentifier;
+            my $found          = first {
+                $_->getBioMaterial()->getIdentifier eq $new_identifier;
+                }
+                @$old_bmms;
+
+            $treatment->addSourceBioMaterialMeasurements($source_bmm)
+                unless $found;
+        }
+    }
+
+    return $treatments;
+
+}
+
+sub _new_biomaterial_biosample {
+
+    my ( $args, $obj ) = @_;
+
+    # New object if necessary
+    $obj = Bio::MAGE::BioMaterial::BioSample->new unless $obj;
+
+    # Identifier, Name
+    $obj->setIdentifier( $args->{identifier} ) unless $obj->getIdentifier;
+    $obj->setName( $args->{name} )             unless $obj->getName;
+
+    # MaterialType
+    my $materialtype = &new_ontologyentry(
+        {   category => $OE_CAT_MATERIALTYPE,
+            value    => $args->{material_type},
+        }
+    );
+    $obj->setMaterialType($materialtype) unless $obj->getMaterialType;
+
+    # Type
+    my $type = &new_ontologyentry(
+        {   category => $OE_CAT_BIOSAMPLETYPE,
+            value    => $args->{biosample_type},
+        }
+    );
+    $obj->setType($type) unless $obj->getType;
+
+    # Treatments
+    if ( $args->{derived_from} ) {
+	my $source_bmm
+	    = Bio::MAGE::BioMaterial::BioMaterialMeasurement->new(
+		bioMaterial => $args->{derived_from},
+	    );
+
+	my $treatments = new_treatments(
+	    {   source_bmm          => $source_bmm,
+		protocols           => $args->{protocols},
+		parameters          => $args->{parameters},
+		parameter_bag       => $args->{parameter_bag},
+		identifier_template => $args->{identifier_template},
+		default_action      => $args->{default_action},
+	    }
+	);
+
+	if ( $obj->getTreatments ) {
+	    $obj->setTreatments(
+		&_update_treatments( $obj->getTreatments, $source_bmm ) );
+	}
+	else {
+	    $obj->setTreatments($treatments);
+	}
+    }
+
+    # Pooling
+    if ( $args->{pooling} ) {
+        my $pooling_treatments = _check_pooling(
+	    $obj,
+	    $args->{pooling},
+            $args->{identifier_template},
+	);
+        $obj->addTreatments(@$pooling_treatments) if $pooling_treatments;
+    }
+
+    return $obj;
+
+}
+
+sub new_biosample {
+
+    my ( $name, $args, $biosample ) = @_;
+
+    $args->{name}          ||= $name;
+    $args->{material_type} ||= $OE_VAL_ORGANISMPART; # Set some default values
+    $args->{biosample_type} ||= $OE_VAL_NOTEXTRACT;
+    $args->{identifier} = "$args->{identifier_template}.Sample";
+
+    return &_new_biomaterial_biosample( $args, $biosample );
+
+}
+
+sub new_extract {
+
+    my ( $name, $args, $extract ) = @_;
+
+    $args->{name}           ||= $name;
+    $args->{material_type}  ||= $OE_VAL_TOTALRNA;    # Set some default values
+    $args->{biosample_type} ||= $OE_VAL_EXTRACT;
+    $args->{default_action} ||= $OE_VAL_NUCLEICACIDEXTRACTION;
+    $args->{identifier} = "$args->{identifier_template}.Extract";
+
+    return &_new_biomaterial_biosample( $args, $extract );
+
+}
+
+sub new_immunoprecipitate {
+
+    my ( $name, $args, $ip ) = @_;
+
+    $args->{name}           ||= $name;
+    $args->{material_type}  ||= $OE_VAL_GENOMICDNA;  # Set some default values
+    $args->{biosample_type} ||= $OE_VAL_EXTRACT;
+    $args->{default_action} ||= $OE_VAL_IMMUNOPRECIPITATE;
+    $args->{identifier} = "$args->{identifier_template}.Immunoprecipitate";
+
+    return &_new_biomaterial_biosample( $args, $ip );
+
+}
+
+sub new_protocol {
+
+    my ( $identifier, $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::Protocol::Protocol->new unless $obj;
+
+    $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+
+    $args->{protocol_type} && do {
+        my $type = &new_ontologyentry(
+            {   category => $OE_CAT_PROTOCOLTYPE,
+                value    => $args->{protocol_type},
+            }
+        );
+        $obj->setType($type) unless $obj->getType;
+    };
+
+    $args->{name}
+        && do { $obj->setName( $args->{name} ) unless $obj->getName; };
+    $args->{text}
+        && do { $obj->setText( $args->{text} ) unless $obj->getText; };
+
+    if ( $args->{parameters} ) {
+
+        # Sort out the parameters
+        my @parameters;
+
+        while ( my ( $param_name, $param_hash )
+            = each %{ $args->{parameters} } ) {
+
+            push(
+                @parameters,
+                $args->{parameter_bag}->(
+                    $param_hash->{identifier},
+                    {   name => $param_name,
+                        unit => $param_hash->{unit},
+                    }
+                )
+            );
+        }
+
+        $obj->setParameterTypes( \@parameters )
+            unless $obj->getParameterTypes;    # FIXME more granular update?
+
+    }
+
+    if ( $args->{software} ) {
+	_update_protocol_software( $obj, $args->{software} );
+    }
+    if ( $args->{hardware} ) {
+	_update_protocol_hardware( $obj, $args->{hardware} );
+    }
+
+    return $obj;
+
+}
+
+sub new_array {
+
+    my ( $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::Array::Array->new unless $obj;
+
+    # Identifier
+    my $identifier = "$args->{identifier_template}.Array";
+    $obj->setIdentifier($identifier) unless $obj->getIdentifier();
+
+    # PhysicalArrayDesign
+    my $accession;
+    if ( my $ad = $args->{arraydesign} ) {
+	$obj->setArrayDesign($ad) unless $obj->getArrayDesign();
+	$accession = $ad->getIdentifier();
+    }
+    elsif ( defined( $args->{arrayaccession} ) ) {
+	my $pad = Bio::MAGE::ArrayDesign::PhysicalArrayDesign->new(
+	    identifier => $args->{arrayaccession},
+	);
+	$obj->setArrayDesign($pad) unless $obj->getArrayDesign();
+	$accession = $args->{arrayaccession};
+    }
+    else {
+	croak("Error: No array design information provided to new_array.");
+    }
+
+    # Array serial
+    $args->{serial} && do {
+        $obj->setArrayIdentifier( $args->{serial} )
+            unless $obj->getArrayIdentifier;
+    };
+
+    if ( my $get_armanuf = $args->{armanuf_bag} ) {
+
+	# ArrayManufacture - unique for each accession/serial no. combination
+	my $armanuf_key = $accession . ( $args->{serial} || q{} );
+	my $armanuf = $get_armanuf->(
+	    $armanuf_key,
+	    {   array               => $obj,
+		identifier_template => $args->{identifier_template},
+	    }
+	);
+	$obj->setInformation($armanuf) unless $obj->getInformation;
+    }
+
+    return $obj;
+
+}
+
+sub new_armanuf {
+
+    my ( $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::Array::ArrayManufacture->new unless $obj;
+
+    # Identifier
+    my $pad        = $args->{array}->getArrayDesign;
+    my $identifier = $pad->getIdentifier;
+    $obj->setIdentifier(
+        "$args->{identifier_template}.$identifier.ArrayManufacture")
+        unless $obj->getIdentifier;
+
+    # Arrays
+    my $found;
+    if ( my $preexisting_arrays = $obj->getArrays ) {
+        my $new_identifier = $args->{array}->getIdentifier;
+        $found = first { $_->getIdentifier eq $new_identifier }
+            @$preexisting_arrays;
+    }
+    $obj->addArrays( $args->{array} ) unless $found;
+
+    return $obj;
+
+}
+
+sub new_factorvalue {
+
+    my ( $args, $obj ) = @_;
+
+    my ( $ef, $new );
+
+    # New object if needed; add to the ExperimentalFactor
+    unless ( $obj ) {
+        $obj = Bio::MAGE::Experiment::FactorValue->new();
+        $new++;    # Flag so we know to add new FVs to the EF - see end of sub
+    }
+
+    # Identifier
+    unless ( $obj->getIdentifier() ) {
+        my $identifier = sprintf(
+	    "%s:%s.%s.%s.FactorValue",
+	    $args->{namespace},
+	    $args->{exptaccession},
+	    $args->{category},
+	    $args->{value},
+	);
+        $obj->setIdentifier($identifier);
+    }
+
+    # Name
+    $obj->setName( $args->{value} ) unless $obj->getName;
+
+    # Value or Measurement (OE)
+    ( ( $args->{value} || $args->{value} eq '0' ) && $args->{category} )
+        && do {
+
+        # All numbers treated as measurements, unit class taken from
+        # category until we figure out something better
+        if ( looks_like_number( $args->{value} ) ) {
+
+            my $measurement;
+
+            # Unit is within parens.
+            if ( my ($unitname)
+                = ( $args->{category} =~ m/\(\s*(.*?)\s*\)/ ) ) {
+
+                $measurement = _measurement_from_unit_name($unitname);
+
+                # Strip any units from the category so the
+                # ExperimentalFactorCategory is correctly parsed.  Unit class
+                # is everything before the first parenthesis.
+                $args->{category} = ( split /\s*\(/, $args->{category} )[0];
+            }
+
+            else {
+                $measurement = Bio::MAGE::Measurement::Measurement->new;
+            }
+
+            $measurement->setValue( $args->{value} );
+            $obj->setMeasurement($measurement) unless $obj->getMeasurement;
+
+        }
+        else {
+
+            my $value = &new_ontologyentry(
+                {   category => $args->{category},
+                    value    => $args->{value}
+                }
+            );
+            $obj->setValue($value) unless $obj->getValue;
+
+        }
+        };
+
+    # Get a new ExperimentalFactor, by category
+    if ( my $get_ef = $args->{expt_factor_bag} ) {
+	$ef = $get_ef->(
+
+	    # Category used as internal ID for tracking
+	    $args->{category},
+	    $args,
+	);
+    }
+    $ef->addFactorValues($obj) if $new;
+
+    return ( $obj, $ef );
+
+}
+
+sub new_experimentalfactor {
+
+    my ( $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::Experiment::ExperimentalFactor->new unless $obj;
+
+    # Identifier
+    my $identifier = sprintf(
+	"%s:%s.%s.ExperimentalFactor",
+	$args->{namespace},
+	$args->{exptaccession},
+	$args->{category},
+    );
+    $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+
+    # Name
+    $obj->setName( $args->{category} ) unless $obj->getName;
+
+    # Category (OE)
+    my $category_value = decamelize($args->{category});
+
+    my $category = new_ontologyentry(
+        {   category => $OE_CAT_EXPERIMENTALFACTORCATEGORY,
+            value    => $category_value
+        }
+    );
+    $obj->setCategory($category) unless $obj->getCategory;
+
+    return $obj;
+
+}
+
+sub new_quantitationtype {
+
+    my ( $args, $obj ) = @_;
+
+    my $qt_metrics = $args->{data_metrics}{ $args->{name} };
+
+    unless ($obj) {
+
+        # Check for subclass defined in QT data.  Avoid autovivification
+        # here.
+        if ( $qt_metrics && $qt_metrics->{subclass} ) {
+
+            eval {
+                $obj
+                    = ("Bio::MAGE::QuantitationType::$qt_metrics->{subclass}")
+                    ->new;
+                }
+                or do {
+                print STDERR (
+                    "Warning: Invalid QuantitationType subclass specified: "
+                        . "$qt_metrics->{subclass}. Creating SpecalizedQuantitationType object.\n"
+                );
+                $obj
+                    = Bio::MAGE::QuantitationType::SpecializedQuantitationType
+                    ->new;
+                };
+
+            # $qt_metrics->{confmap} identifies to which QT a confidence
+            # indicator maps.
+            if ( $qt_metrics->{confmap} && $args->{container} ) {
+
+                my $target_identifier
+                    = $args->{prefix} . $qt_metrics->{confmap};
+
+                # Recurse down one level only (container is not passed).
+                my $target_qt = $args->{container}->(
+                    $target_identifier,
+                    {   identifier   => $target_identifier,
+                        name         => $qt_metrics->{confmap},
+                        prefix       => $args->{prefix},
+                        data_metrics => $args->{data_metrics},
+                    }
+                );
+
+                croak(
+                    "Internal MAGE.pm error: unable to identify target QT $qt_metrics->{confmap}\n"
+                    )
+                    unless $target_qt;
+
+                if (     ( $qt_metrics->{subclass} eq 'PValue' )
+		      || ( $qt_metrics->{subclass} eq 'Error' )
+		      || ( $qt_metrics->{subclass} eq 'ExpectedValue' ) ) {
+
+		    # Set our target.
+		    $obj->setTargetQuantitationType($target_qt);
+
+		    # Update the target to point to us, if not already done.
+		    my $found;
+		    foreach my $confind (
+			@{ $target_qt->getConfidenceIndicators || [] } ) {
+			$found++ if ($confind->getIdentifier() eq $args->{identifier} );
+		    }
+		    $target_qt->addConfidenceIndicators($obj) unless $found;
+                }
+            }
+
+        }
+        else {
+
+            # No subclass given; fall back to specialized QT
+            $obj = Bio::MAGE::QuantitationType::SpecializedQuantitationType
+                ->new;
+        }
+    }
+
+    # Identifier, Name
+    $obj->setIdentifier( $args->{identifier} ) unless $obj->getIdentifier();
+    unless ( $obj->getName() ) {
+	my $name = $args->{name};
+	if ( $args->{software} ) {
+	    $name = "$args->{software}:$name";
+	}
+	$obj->setName( $name );
+    }
+
+    $qt_metrics ||= {};
+
+    # DataType
+    my $datatypevalue = $qt_metrics->{datatype} || $OE_VAL_UNKNOWN;
+    my $datatype = &new_ontologyentry(
+        {   category => $OE_CAT_DATATYPE,
+            value    => $datatypevalue
+        }
+    );
+    $obj->setDataType($datatype) unless $obj->getDataType;
+
+    # Scale
+    my $scalevalue = $qt_metrics->{scale} || $OE_VAL_UNKNOWN;
+    my $scale = &new_ontologyentry(
+        {   category => $OE_CAT_SCALE,
+            value    => $scalevalue
+        }
+    );
+    $obj->setScale($scale) unless $obj->getScale;
+
+    # IsBackground
+    my $isbackground = $qt_metrics->{is_background} || 0;
+    $obj->setIsBackground($isbackground) unless $obj->getIsBackground;
+
+    # Channel
+    $qt_metrics->{channel} && do {
+        my $channel = Bio::MAGE::BioAssay::Channel->new(
+            identifier => $AE_CHANNEL_PREFIX . $qt_metrics->{channel},
+	    name       => $qt_metrics->{channel},
+	);
+        $obj->setChannel($channel);
+    };
+
+    # Description
+    $qt_metrics->{description} && do {
+        my $description = Bio::MAGE::Description::Description->new(
+            text => $qt_metrics->{description} );
+        $obj->setDescriptions( [$description] );
+    };
+
+    return $obj;
+
+}
+
+sub new_qtd {
+
+    my ( $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::BioAssayData::QuantitationTypeDimension->new
+        unless $obj;
+
+    # Identifier
+    unless ( $obj->getIdentifier ) {
+        my $identifier
+            = "$args->{identifier_template}.QuantitationTypeDimension";
+        $obj->setIdentifier($identifier);
+    }
+
+    # QuantitationTypes. FIXME to add? - maybe rewrite QTD/QT handling
+    $obj->setQuantitationTypes( $args->{qt_list} )
+        unless $obj->getQuantitationTypes;
+
+    return $obj;
+
+}
+
+sub new_biosource {
+
+    my ( $args, $obj ) = @_;
+
+    # Defaults
+    $args->{material_type} ||= $OE_VAL_WHOLEORGANISM;
+
+    # New object if necessary
+    $obj = Bio::MAGE::BioMaterial::BioSource->new unless $obj;
+
+    # Identifier, Name
+    my $identifier = "$args->{identifier_template}.BioSource";
+    $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+    $obj->setName( $args->{name} )   unless $obj->getName;
+
+    # MaterialType
+    my $materialtype = &new_ontologyentry(
+        {   category => $OE_CAT_MATERIALTYPE,
+            value    => $args->{material_type}
+        }
+    );
+    $obj->setMaterialType($materialtype) unless $obj->getMaterialType;
+
+    # BioMaterialCharacteristics
+    foreach my $char ( @{ $args->{characteristics} } ) {
+
+        my $found;
+        if ( my $preexisting_chars = $obj->getCharacteristics ) {
+            my $new_category = $char->getCategory;
+            my $new_value    = $char->getValue;
+            $found = first {
+                $_->getCategory     eq $new_category
+                    && $_->getValue eq $new_value;
+                }
+                @$preexisting_chars;
+        }
+        $obj->addCharacteristics($char) unless $found;
+
+    }
+
+    # Add a free-text description, if included.
+    if ($args->{description}){
+	my $description = Bio::MAGE::Description::Description->new(
+	    text => $args->{description},
+	);
+	$obj->setDescriptions([$description]) unless $obj->getDescriptions;
+    }
+
+    return $obj;
+
+}
+
+sub update_factorvalues {
+
+    my ( $obj, $newfvlist ) = @_;
+
+    my $oldfvlist = $obj->getBioAssayFactorValues;
+
+    my %fvhash = map { $_->getIdentifier => $_ } @$oldfvlist, @$newfvlist;
+
+    # Note that we're using set rather than add here because apparently
+    # addBioAssayFactorValues has some really bizarre behaviour
+    # (i.e. updating FVs on a related PBA when DBA is passed).
+    $obj->setBioAssayFactorValues( [ values %fvhash ] );
+
+    return;
+}
+
+sub new_hybridization {
+
+    my ( $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::BioAssay::Hybridization->new unless $obj;
+
+    if ( $args->{identifier_template} ) {
+        $obj->setIdentifier("$args->{identifier_template}.Hybridization")
+            unless $obj->getIdentifier();
+    }
+    if ( $args->{name} ) {
+	$obj->setName($args->{name}) unless $obj->getName();
+    }
+    if ( $args->{physical_bioassay} ) {
+        $obj->setPhysicalBioAssayTarget( $args->{physical_bioassay} )
+            unless $obj->getPhysicalBioAssayTarget();
+    }
+
+    $obj->setArray( $args->{array} ) if $args->{array};
+
+    my $hyb_protocolapplication;
+    if ( $args->{hybridization_protocol} ) {
+
+        $hyb_protocolapplication
+            = Bio::MAGE::Protocol::ProtocolApplication->new(
+            protocol     => $args->{hybridization_protocol},
+            activityDate => 'n/a',
+
+            # FIXME, (also performers, hardware)
+	);
+
+        if ( $args->{hybridization_parameters} ) {
+
+            foreach my $param_id (
+                sort keys %{ $args->{hybridization_parameters} } ) {
+
+                my $parameter = $args->{parameter_bag}->( $param_id );
+
+		unless ( $parameter ) {
+		    croak(qq{Internal error: Parameter "$param_id" not defined.\n});
+		}
+
+                $hyb_protocolapplication->addParameterValues(
+                    Bio::MAGE::Protocol::ParameterValue->new(
+                        value => $args->{hybridization_parameters}{$param_id},
+                        parameterType => $parameter
+                    )
+                );
+
+            }
+        }
+
+        if ( $args->{hyb_hardware} ) {
+
+            my $hardwareapplication = &new_hardwareapplication(
+                {   hardware   => $args->{hyb_hardware},
+                    parameters => $args->{hyb_hardware_params}
+                }
+            );
+            $hyb_protocolapplication->addHardwareApplications(
+                $hardwareapplication);
+
+        }
+
+        $obj->setProtocolApplications( [$hyb_protocolapplication] )
+            if $hyb_protocolapplication;
+
+    }
+
+    # We can also accept a straight arrayref of ProtocolApplications
+    # (e.g. as in MAGE-TAB implementation).
+    if ( $args->{hyb_protocolapps} ) {
+	$obj->setProtocolApplications( $args->{hyb_protocolapps} );
+    }
+
+    return $obj;
+
+}
+
+sub _update_protocol_software {
+
+    my ( $protocol, $software ) = @_;
+
+    my $found;
+    if ( my $preexisting_softwares = $protocol->getSoftwares || [] ) {
+        my $new_identifier = $software->getIdentifier;
+        $found = first { $_->getIdentifier eq $new_identifier }
+            @$preexisting_softwares;
+    }
+    $protocol->addSoftwares($software) unless $found;
+
+    return;
+}
+
+sub _update_protocol_hardware {
+
+    my ( $protocol, $hardware ) = @_;
+
+    my $found;
+    if ( my $preexisting_hardwares = $protocol->getHardwares || [] ) {
+        my $new_identifier = $hardware->getIdentifier;
+        $found = first { $_->getIdentifier eq $new_identifier }
+            @$preexisting_hardwares;
+    }
+    $protocol->addHardwares($hardware) unless $found;
+
+    return;
+}
+
+sub new_scan_protoapp {
+
+    my ( $args, $obj ) = @_;
+
+    unless ( $obj ) {
+	$obj = Bio::MAGE::Protocol::ProtocolApplication->new(
+            protocol     => $args->{scanning_protocol},
+            activityDate => 'n/a',
+
+            # FIXME (also performers, hardware)
+	);
+    }
+
+    if ( $args->{software} ) {
+	my $softwareapplication
+	    = Bio::MAGE::Protocol::SoftwareApplication->new(
+		software => $args->{software},
+	    );
+	$softwareapplication->setVersion( $args->{software_version} )
+	    if $args->{software_version};
+	
+	$obj->setSoftwareApplications(
+	    [$softwareapplication],
+	);
+
+	_update_protocol_software(
+	    $args->{scanning_protocol},
+	    $args->{software},
+	);
+    }
+
+    if ( $args->{scanning_parameters} ) {
+	
+	foreach  my $param_id (
+	    sort keys %{ $args->{scanning_parameters} } ) {
+	    
+	    my $parameter = $args->{parameter_bag}->( $param_id );
+
+	    unless ( $parameter ) {
+		croak(qq{Internal error: Parameter "$param_id" not defined.\n});
+	    }
+
+	    $obj->addParameterValues(
+		Bio::MAGE::Protocol::ParameterValue->new(
+		    value => $args->{scanning_parameters}{$param_id},
+		    parameterType => $parameter
+		)
+	    );
+	}
+    }
+
+    if ( $args->{scan_hardware} ) {
+
+	my $hardwareapplication = new_hardwareapplication(
+	    {   hardware   => $args->{scan_hardware},
+		parameters => $args->{scan_hardware_params}
+	    }
+	);
+	$obj->addHardwareApplications(
+	    $hardwareapplication,
+	);
+    }
+
+    return $obj;
+}
+
+sub new_scan {
+
+    my ( $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::BioAssay::ImageAcquisition->new unless $obj;
+
+    $args->{identifier_template} && do {
+        $obj->setIdentifier("$args->{identifier_template}.ImageAcquisition")
+            unless $obj->getIdentifier;
+    };
+    $args->{physical_bioassay} && do {
+        $obj->setPhysicalBioAssay( $args->{physical_bioassay} )
+            unless $obj->getPhysicalBioAssay;
+        $obj->setTarget( $args->{physical_bioassay} ) unless $obj->getTarget;
+    };
+
+    # ProtocolApplication and Software
+    if ( $args->{scanning_protocol} ) {
+        $obj->setProtocolApplications( [ new_scan_protoapp( $args ) ] );
+    }
+
+    # We can also accept a straight arrayref of ProtocolApplications
+    # (e.g. as in MAGE-TAB implementation).
+    if ( $args->{scan_protocolapps} ) {
+	$obj->setProtocolApplications( $args->{scan_protocolapps} );
+    }
+
+    # Image URIs (NB currently assumes ony one image per hyb FIXME)
+    if ( $args->{image} ) {
+        my $pba = $args->{physical_bioassay};
+        $args->{image}->setChannels( $pba->getChannels ) if $pba->getChannels;
+        $obj->setImages(               [ $args->{image} ] );
+        $pba->setPhysicalBioAssayData( [ $args->{image} ] );
+    }
+
+    return $obj;
+
+}
+
+{
+    # Local cache for PBA data (FVs, labels attached to a given hyb).
+    my %pba_fv_cache;
+    
+sub new_physicalbioassay {
+
+    my ( $name, $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::BioAssay::PhysicalBioAssay->new unless $obj;
+
+    # Identifier
+    $args->{identifier_template} && do {
+        my $identifier = "$args->{identifier_template}.PhysicalBioAssay";
+        $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+    };
+
+    # Name; store this also in $args for passing to new_hybridization
+    unless ( defined( $args->{name} ) ) {
+	$args->{name} = $name;
+    }
+    $obj->setName($args->{name}) unless $obj->getName();
+
+    # Channels
+    # Construct hash listing of old label ids
+    my $old_label_identifiers;
+    if ( $obj->getChannels ) {
+        foreach my $old_channel ( @{ $obj->getChannels } ) {
+            foreach my $old_label ( @{ $old_channel->getLabels } ) {
+                $old_label_identifiers->{ $old_label->getIdentifier }++;
+            }
+        }
+    }
+
+    # N.B. only one label per LE supported here (although LEs can have
+    # more than one label, and new_labelledextract does support
+    # this). This is not likely to be a problem for tab2mage or
+    # mage-tab though, as the LEs are internally uniqued by dye name.
+
+    my $dyename;
+    if ( $args->{label} ) {
+
+        # Convert LabelCompound into Channel
+        my $dye_id = $args->{label}->getIdentifier();
+        unless ( ( $dyename )
+		     = ( $dye_id =~ m/^$AE_LABELCOMPOUND_PREFIX(.*)$/ ) ) {
+	    $dyename = 'Unknown';
+        }
+
+	# Don't add the channel if it's already present.
+	unless ( $old_label_identifiers->{ $dye_id } ) {
+	    my $channel = Bio::MAGE::BioAssay::Channel->new(
+		identifier => $AE_CHANNEL_PREFIX . $dyename,
+		name       => $dyename,
+		labels     => [ $args->{label} ],
+	    );
+	    $obj->addChannels($channel);
+
+	    # Cache the label object for later.
+	    $pba_fv_cache{$name}{$dyename}{label} = $args->{label};
+	}
+
+    }
+    else {
+
+	# We still need a default $dyename.
+	$dyename = 'Unknown';
+    }
+
+    my $merged_pba;
+    if ( $args->{derived_from} ) {
+
+	# i.e. this is a hyb-level PBA.
+
+	# Record the factor values in the cache. These will be used
+	# subsequently to rebuild two-colour hyb codings with the
+	# appropriate fvs and labels.
+	foreach my $fv ( @{ $args->{factor_values} } ) {
+	    $pba_fv_cache{$name}{$dyename}{fv}{$fv->getIdentifier()} = $fv;
+	}
+
+	# BioAssayCreation
+	my $hyb_args = { %$args };  # Make a copy.
+	$hyb_args->{physical_bioassay} = $obj;
+	$obj->setBioAssayCreation( new_hybridization( $hyb_args ) )
+	    unless $obj->getBioAssayCreation;
+
+	# SourceBioMaterialMeasurements - added where the BioMaterials
+	# differ (e.g. Cy3, Cy5 labeled extracts).
+        my $new_bm = $args->{derived_from};
+
+        my $preexisting_bmms
+            = $obj->getBioAssayCreation()->getSourceBioMaterialMeasurements;
+
+        my $new_identifier = $new_bm->getIdentifier;
+
+        my $found
+            = first { $_->getBioMaterial()->getIdentifier eq $new_identifier }
+            @$preexisting_bmms;
+
+        unless ($found) {
+            my $bmm = Bio::MAGE::BioMaterial::BioMaterialMeasurement->new(
+                bioMaterial => $new_bm );
+            $obj->getBioAssayCreation()
+                ->addSourceBioMaterialMeasurements($bmm);
+        }
+
+	# Best-practice PBA coding for two (or more) channels.
+	# Generate a channel PBA from the cached data for the original
+	# hyb-level PBA, and a merged PBA.
+	if ( scalar @{ $obj->getChannels() || [] } > 1 ) {
+
+	    # Delete any preexisting BATs from single-channel coding.
+	    $obj->setBioAssayTreatments([]);
+
+	    my (@channel_specific_pbas, %merged_fvs, %merged_labels);
+	    while ( my ( $dyename, $channeldata ) = each %{ $pba_fv_cache{$name} } ) {
+
+		my $new_id_template = "$args->{identifier_template}.$dyename";
+		my $label           = $channeldata->{label};
+		my $factor_values   = [ values %{ $channeldata->{fv} } ];
+
+		# We don't pass $args->{derived_from} down to the
+		# extended PBA generation; this is used as a flag to
+		# indicate hyb-level PBAs only.
+		my $channel_pba = $args->{extended_pba_bag}->(
+		    "$name.$dyename",
+		    {
+			identifier_template => $new_id_template,
+			label               => $label,
+			factor_values       => $factor_values,
+		    },
+		);
+
+		# Add a BioAssayTreatment pointing back to $obj here.
+		my %preexisting;
+		if ( my $old_treats = $obj->getBioAssayTreatments() ) {
+		    foreach my $treat ( @$old_treats ) {
+			if ( my $old_pba = $treat->getPhysicalBioAssay() ) {
+			    $preexisting{ $old_pba->getIdentifier() }++;
+			}
+		    }
+		}
+		unless ( $preexisting{ $channel_pba->getIdentifier() } ) {
+		    $obj->addBioAssayTreatments(
+			new_scan(
+			    {
+				identifier_template => $new_id_template,
+				physical_bioassay   => $channel_pba,
+			    }
+			)
+		    );
+		}
+
+		# Cache these for addition to the merged PBA below.
+		foreach my $fv (@$factor_values) {
+		    $merged_fvs{ $fv->getIdentifier() } = $fv;
+		}
+		$merged_labels{ $label->getIdentifier() } = $label;
+
+		push @channel_specific_pbas, $channel_pba;
+
+	    }
+
+	    # Create the merged PBA here. Note that the .merged suffix
+	    # convention is assumed in other modules for mapping the
+	    # hyb PBA to the merged PBA and vice versa.
+	    my $merged_id_template = "$args->{identifier_template}.merged";
+
+	    # This is a little clumsy; the alternative is proper
+	    # support for multiple labels FIXME.
+	    foreach my $label ( values %merged_labels ) {
+		$merged_pba = $args->{extended_pba_bag}->(
+		    "$name.merged",
+		    {
+			identifier_template => $merged_id_template,
+			label               => $label,
+			factor_values       => [ values %merged_fvs ],
+		    },
+		);
+	    }
+
+	    # Add an empty BAT so we can attach image_acquisition protocol.
+	    if ( $merged_pba ) {
+		my $merge_args = { %$args };  # Make a copy.
+		$merge_args->{identifier_template} = $merged_id_template;
+		$merge_args->{physical_bioassay}   = $merged_pba;
+		$merged_pba->addBioAssayTreatments( new_scan( $merge_args ) );
+	    }
+
+	    # Link the merged and channel-specific PBAs.
+	    foreach my $channel_pba ( @channel_specific_pbas ) {
+		my $scan_id_template
+		    = "$args->{identifier_template}." . unique_identifier();
+		$channel_pba->setBioAssayTreatments( [ new_scan(
+		    {
+			identifier_template => $scan_id_template,
+			physical_bioassay   => $merged_pba,
+		    },
+		) ] );
+	    }
+
+	    # Repoint any MBAs pointing at the hyb-level PBA. This is
+	    # particularly important for single-channel scans of
+	    # two-colour hybs.
+	    if ( $args->{mba_bag} ) {
+		foreach my $mba ( @{ $args->{mba_bag}->() } ) {
+		    if ( my $fext = $mba->getFeatureExtraction() ) {
+			if ( my $source = $fext->getPhysicalBioAssaySource() ) {
+			    if ( $obj->getIdentifier() eq $source->getIdentifier() ) {
+				$fext->setPhysicalBioAssaySource($merged_pba);
+			    }
+			}
+		    }
+		}
+	    }
+	}
+	else {
+
+	    # BioAssayTreatments
+	    $obj->setBioAssayTreatments( [ new_scan( $hyb_args ) ] )
+		unless $obj->getBioAssayTreatments;
+	}
+    }
+    else {
+
+	# This is a channel-specific or merging PBA (do nothing).
+    }
+
+    # BioAssayFactorValues
+    # Update if FVs already present
+    $args->{factor_values}
+        && do { update_factorvalues( $obj, $args->{factor_values} ) };
+
+    return $obj;
+
+}
+
+}
+
+sub new_image {
+
+    my ( $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::BioAssay::Image->new unless $obj;
+
+    my $identifier = "$args->{identifier_template}.Image";
+    $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+
+    $obj->setURI( $args->{uri} ) unless $obj->getURI;
+
+    # We assume the default image format is TIFF
+    my $format = new_ontologyentry(
+        {   category => "ImageFormat",
+            value    => ( $args->{format} || "TIFF" )
+        }
+    );
+
+    $obj->setFormat($format) unless $obj->getFormat;
+
+    return $obj;
+}
+
+sub new_featureextraction {
+
+    my ( $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::BioAssay::FeatureExtraction->new(
+        identifier => "$args->{identifier_template}.FeatureExtraction",
+        measuredBioAssayTarget => $args->{measured_bioassay},
+        )
+        unless $obj;
+    if ( $args->{physical_bioassay} ) {
+        $obj->setPhysicalBioAssaySource( $args->{physical_bioassay} )
+            unless $obj->getPhysicalBioAssaySource;
+    }
+
+    if ( $args->{protocol} ) {
+
+        my $protocolapplication
+            = Bio::MAGE::Protocol::ProtocolApplication->new(
+            protocol     => $args->{protocol},
+            activityDate => 'n/a',
+
+            # FIXME (also performers, hardware)
+            );
+        $args->{software} && do {
+            my $softwareapplication
+                = Bio::MAGE::Protocol::SoftwareApplication->new(
+                software => $args->{software} );
+            $softwareapplication->setVersion( $args->{software_version} )
+                if $args->{software_version};
+            $protocolapplication->setSoftwareApplications(
+                [$softwareapplication] )
+                if $softwareapplication;
+
+            _update_protocol_software( $args->{protocol}, $args->{software} );
+        };
+
+        $args->{parameters} && do {
+
+            foreach my $param_id ( sort keys %{ $args->{parameters} } ) {
+
+                my $parameter = $args->{parameter_bag}->( $param_id );
+
+		unless ( $parameter ) {
+		    croak(qq{Internal error: Parameter "$param_id" not defined.\n});
+		}
+
+                $protocolapplication->addParameterValues(
+                    Bio::MAGE::Protocol::ParameterValue->new(
+                        value         => $args->{parameters}{$param_id},
+                        parameterType => $parameter
+                    )
+                );
+            }
+        };
+        $obj->setProtocolApplications( [$protocolapplication] );
+    }
+
+    # For MAGE-TAB.
+    if ( $args->{protocolapps} ) {
+	$obj->setProtocolApplications( $args->{protocolapps} );
+    }
+
+    return $obj;
+
+}
+
+sub new_measuredbioassay {
+
+    my ( $name, $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::BioAssay::MeasuredBioAssay->new unless $obj;
+
+    # Identifier
+    if ( $args->{identifier_template} ) {
+	my $identifier = "$args->{identifier_template}.MeasuredBioAssay";
+	$obj->setIdentifier($identifier) unless $obj->getIdentifier;
+    }
+
+    # Name
+    $obj->setName($args->{name} || $name) unless $obj->getName;
+
+    # MBAD
+    if ( $args->{measured_bioassay_data} ) {
+
+        my $found;
+        if ( my $preexisting_mbads = $obj->getMeasuredBioAssayData ) {
+            my $new_identifier
+                = $args->{measured_bioassay_data}->getIdentifier;
+            $found = first { $_->getIdentifier eq $new_identifier }
+                @$preexisting_mbads;
+        }
+        $obj->addMeasuredBioAssayData( $args->{measured_bioassay_data} )
+            unless $found;
+    };
+
+    # Channels
+    if ( $args->{channelname} ) {
+
+	# Construct hash listing of old label ids
+	my $found;
+	if ( my $oldchannels = $obj->getChannels() ) {
+	    foreach my $old_ch ( @$oldchannels ) {
+		foreach my $old_label ( @{ $old_ch->getLabels() || [] } ) {
+		    $found++ if ( $old_label->getName() eq $args->{channelname} );
+		}
+	    }
+	}
+	unless ( $found ) {
+	    my $label = new_labelcompound({dye => $args->{channelname}});
+	    my $channel = Bio::MAGE::BioAssay::Channel->new(
+		identifier => $AE_CHANNEL_PREFIX . $args->{channelname},
+		name       => $args->{channelname},
+		labels     => [ $label ],
+	    );
+	    $obj->addChannels($channel);
+	}
+    }
+
+    # BioAssayFactorValues
+    # Update if FVs already present
+    if ( $args->{factor_values} ) {
+	update_factorvalues( $obj, $args->{factor_values} )
+    }
+
+    # FeatureExtraction
+    my %fext_args = %$args;
+    $fext_args{measured_bioassay} = $obj;
+
+    # Note that this needs to be able to overwrite old FEs, e.g. when
+    # switching to two-colour hyb PBA coding.
+    $obj->setFeatureExtraction( new_featureextraction( \%fext_args ) );
+
+    return $obj;
+
+}
+
+sub new_derivedbioassay {
+
+    my ( $name, $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::BioAssay::DerivedBioAssay->new unless $obj;
+
+    # Identifier
+    $args->{identifier_template} && do {
+	my $identifier = "$args->{identifier_template}.DerivedBioAssay";
+	$obj->setIdentifier($identifier) unless $obj->getIdentifier;
+    };
+
+    # Name
+    $obj->setName($args->{name} || $name) unless $obj->getName;
+
+    # DBAD
+    $args->{derived_bioassay_data} && do {
+
+        my $found;
+        if ( my $preexisting_dbads = $obj->getDerivedBioAssayData ) {
+            my $new_identifier
+                = $args->{derived_bioassay_data}->getIdentifier;
+            $found = first { $_->getIdentifier eq $new_identifier }
+                @$preexisting_dbads;
+        }
+        $obj->addDerivedBioAssayData( $args->{derived_bioassay_data} )
+            unless $found;
+    };
+
+    # BioAssayFactorValues
+    # Update if FVs already present
+    $args->{factor_values}
+        && do { update_factorvalues( $obj, $args->{factor_values} ) };
+
+    # DerivedBioAssayType
+    $args->{normalization_type} && do {
+        my $dbatype = &new_ontologyentry(
+            {   category => $OE_CAT_DERIVEDBIOASSAYTYPE,
+                value    => $args->{normalization_type},
+            }
+        );
+        $obj->setType($dbatype) unless $obj->getType;
+    };
+
+    # DerivedBioAssayMap
+    $args->{source_bioassays} && do {
+
+	unless ( $args->{bioassaymap_bag} ) {
+	    croak("No BioAssayMap container passed to new_derivedbioassay.");
+	}
+
+        # BioAssayMap and DBA both have the same $name internal identifier.
+        # new_bioassaymap updates source_bioassays.
+        my $bioassaymap = $args->{bioassaymap_bag}->(
+            $name,
+            {   identifier_template => $args->{identifier_template},
+                source_bioassays    => $args->{source_bioassays},
+                target_bioassay     => $obj
+            }
+        );
+        $obj->setDerivedBioAssayMap( [$bioassaymap] );
+    };
+
+    return $obj;
+
+}
+
+sub add_summary_stats {
+
+    my ( $obj, $stat_hash ) = @_;
+
+    if ($stat_hash) {
+        my @statistics;
+        foreach my $name ( sort keys %{$stat_hash} ) {
+            push(
+                @statistics,
+                Bio::MAGE::NameValueType->new(
+                    name  => $name,
+                    value => $stat_hash->{$name},
+                )
+            );
+        }
+        $obj->setSummaryStatistics( \@statistics )
+            unless $obj->getSummaryStatistics;
+
+    }
+    else {
+
+        return 0;
+
+    }
+
+    return 1;
+}
+
+sub new_measuredbioassaydata {
+
+    my ( $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::BioAssayData::MeasuredBioAssayData->new unless $obj;
+
+    # Identifier
+    my $identifier = "$args->{identifier_template}.MeasuredBioAssayData";
+    $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+
+    # Name
+    unless ( defined( $args->{name} ) ) {
+	$args->{name} = $args->{filename};
+    }
+    $obj->setName( $args->{name} ) unless $obj->getName();
+
+    # DED, QTD (BAD added separately at the moment FIXME)
+    $args->{feature_dimension} && do {
+	$obj->setDesignElementDimension( $args->{feature_dimension} )
+	    unless $obj->getDesignElementDimension;
+    };
+    $args->{quantitation_type_dimension} && do {
+	$obj->setQuantitationTypeDimension( $args->{quantitation_type_dimension} )
+	    unless $obj->getQuantitationTypeDimension;
+    };
+
+    # BioDataCube
+    my $biodatacube = Bio::MAGE::BioAssayData::BioDataCube->new(
+        order => $args->{order} || 'BDQ',
+        cube => $args->{filename}
+    );
+    $obj->setBioDataValues($biodatacube) unless $obj->getBioDataValues;
+
+    # Summary Statistics
+    &add_summary_stats( $obj, $args->{summary_statistics} )
+        if $args->{summary_statistics};
+
+    return $obj;
+
+}
+
+sub new_producertransformation {
+
+    my ( $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::BioAssayData::Transformation->new(
+        identifier => "$args->{identifier_template}.Transformation",
+        derivedBioAssayDataTarget => $args->{derived_bioassay_data},
+
+        # FIXME QTM
+    ) unless $obj;
+
+    if ( $args->{bioassay_map} && scalar @{ $args->{bioassay_map} } ) {
+        my $bioassay_mapping = Bio::MAGE::BioAssayData::BioAssayMapping->new(
+            bioAssayMaps => $args->{bioassay_map} );
+        $obj->setBioAssayMapping($bioassay_mapping)
+            unless $obj->getBioAssayMapping;
+    }
+
+    my $protocolapplication;
+    if ( $args->{protocol} ) {
+        $protocolapplication = Bio::MAGE::Protocol::ProtocolApplication->new(
+            protocol     => $args->{protocol},
+            activityDate => 'n/a',
+
+            # FIXME (also performers, hardware applications)
+        );
+        $args->{software} && do {
+            my $softwareapplication
+                = Bio::MAGE::Protocol::SoftwareApplication->new(
+                software => $args->{software} );
+            $softwareapplication->setVersion( $args->{software_version} )
+                if $args->{software_version};
+
+            $protocolapplication->setSoftwareApplications(
+                [$softwareapplication] );
+
+            &_update_protocol_software( $args->{protocol},
+                $args->{software} );
+        };
+
+        $args->{parameters} && do {
+
+            foreach my $param_id ( sort keys %{ $args->{parameters} } ) {
+
+                my $parameter = $args->{parameter_bag}->( $param_id );
+
+		unless ( $parameter ) {
+		    croak(qq{Internal error: Parameter "$param_id" not defined.\n});
+		}
+
+                $protocolapplication->addParameterValues(
+                    Bio::MAGE::Protocol::ParameterValue->new(
+                        value         => $args->{parameters}{$param_id},
+                        parameterType => $parameter
+                    )
+                );
+
+            }
+        };
+	$obj->setProtocolApplications( [$protocolapplication] );
+    }
+
+    # MAGE-TAB support.
+    if ( $args->{protocolapps} ) {
+	$obj->setProtocolApplications( $args->{protocolapps} );
+    }
+
+    $obj->setBioAssayDataSources( $args->{source_bioassay_dataset} )
+        if ($args->{source_bioassay_dataset}
+		&& scalar @{$args->{source_bioassay_dataset}});
+
+    $obj->setQuantitationTypeMapping( $args->{quantitationtype_mapping} )
+        if $args->{quantitationtype_mapping};
+
+    return $obj;
+
+}
+
+sub new_quantitationtypemap {
+
+    my ( $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::BioAssayData::QuantitationTypeMap->new unless $obj;
+
+    $args->{target_qt} && do {
+        my $identifier = "$args->{identifier_template}."
+            . $args->{target_qt}->getName
+            . '.QuantitationTypeMap';
+        $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+        $obj->setTargetQuantitationType( $args->{target_qt} )
+            unless $obj->getTargetQuantitationType;
+    };
+
+    $args->{source_qts} && do {
+        if ( my $old_source_qts = $obj->getSourcesQuantitationType ) {
+
+            # update a previous list
+            foreach my $new_qt ( @{ $args->{source_qts} } ) {
+                my $new_identifier = $new_qt->getIdentifier;
+                my $found = first { $_->getIdentifier eq $new_identifier }
+                    @$old_source_qts;
+                $obj->addSourcesQuantitationType($new_qt) unless $found;
+            }
+        }
+        else {
+            $obj->setSourcesQuantitationType( $args->{source_qts} );
+        }
+    };
+
+    return $obj;
+
+}
+
+sub new_derivedbioassaydata {
+
+    my ( $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::BioAssayData::DerivedBioAssayData->new unless $obj;
+
+    # Identifier
+    my $identifier = "$args->{identifier_template}.DerivedBioAssayData";
+    $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+
+    # Name
+    unless ( defined( $args->{name} ) ) {
+	$args->{name} = $args->{filename};
+    }
+    $obj->setName( $args->{name} ) unless $obj->getName();
+
+    # DED, QTD, BAD
+    $args->{de_dimension} && do {
+	$obj->setDesignElementDimension( $args->{de_dimension} )
+	    unless $obj->getDesignElementDimension;
+    };
+    $args->{ba_dimension} && do {
+	$obj->setBioAssayDimension( $args->{ba_dimension} )
+	    unless $obj->getBioAssayDimension;
+    };
+
+    my @qt_maps;
+    if ( my $qtd = $args->{qt_dimension} ) {
+        $obj->setQuantitationTypeDimension($qtd)
+            unless $obj->getQuantitationTypeDimension;
+        if (   $args->{source_qts}
+            && $args->{qtm_bag}
+            && ( my $qts = $qtd->getQuantitationTypes ) ) {
+            foreach my $qt (@$qts) {
+
+                # Note - we may need a proper container for QTMs FIXME
+                my %map_args = %$args;
+                $map_args{target_qt} = $qt;
+                my $map = $args->{qtm_bag}->( $qt->getName, \%map_args );
+                $qt->setQuantitationTypeMaps( [$map] );
+                push( @qt_maps, $map );
+            }
+        }
+    }
+    my $qt_mapping;
+    if (@qt_maps) {
+        $qt_mapping = Bio::MAGE::BioAssayData::QuantitationTypeMapping->new(
+            quantitationTypeMaps => \@qt_maps );
+    }
+
+    # BioDataCube
+    my $biodatacube = Bio::MAGE::BioAssayData::BioDataCube->new(
+        order => $args->{order} || 'DBQ',
+        cube => $args->{filename}
+    );
+    $obj->setBioDataValues($biodatacube) unless $obj->getBioDataValues;
+
+    # ProducerTransformation
+    my %txn_args = %$args;
+    $txn_args{derived_bioassay_data}    = $obj;
+    $txn_args{quantitationtype_mapping} = $qt_mapping;
+    $obj->setProducerTransformation(
+        new_producertransformation( \%txn_args ) )
+        unless $obj->getProducerTransformation;
+
+    # Summary Statistics
+    &add_summary_stats( $obj, $args->{summary_statistics} )
+        if $args->{summary_statistics};
+
+    return $obj;
+
+}
+
+sub new_measurement {
+
+    my ($args) = @_;
+
+    my $unit;
+
+    my $sighandler = $SIG{__DIE__};
+    delete $SIG{__DIE__};
+
+    eval {
+        $unit = ("Bio::MAGE::Measurement::$args->{class}")->new;
+
+        # unitNameCV is required, so we need some defaults here
+        if ( $args->{class} eq 'TemperatureUnit' ) {
+
+            # The overwhelmingly likely case.
+            $unit->setUnitNameCV( $args->{unit} || 'degree_C' );
+        }
+        else {
+
+            # all other classes have 'other'
+            $unit->setUnitNameCV( $args->{unit} || 'other' );
+        }
+    };
+
+    $SIG{__DIE__} = $sighandler if $sighandler;
+
+    if ($EVAL_ERROR) {
+
+        # Fall-through default is generic QuantityUnit
+        print STDERR (
+                  qq{Warning: Unit name "$args->{unit}" not in MAGE Unit }
+                . qq{controlled vocabulary. Creating generic QuantityUnit.\n}
+        );
+        $unit = Bio::MAGE::Measurement::QuantityUnit->new(
+            unitName => ( $args->{unit} || 'n/a' ),
+            unitNameCV => 'other',
+        );
+    }
+
+    my $measurement
+        = Bio::MAGE::Measurement::Measurement->new( unit => $unit );
+
+    $args->{kindcv} && $measurement->setKindCV( $args->{kindcv} );
+
+    $measurement->setValue( $args->{value} )
+        if ( $args->{value}
+        || ( defined( $args->{value} ) && $args->{value} eq '0' ) );
+
+    return $measurement;
+
+}
+
+sub _measurement_from_unit_name {
+
+    my ($unitname) = @_;
+
+    my $unitclass = q{};
+
+    # Populate a HoH with MAGE UnitNameCVs
+    my $mage_units;
+    foreach my $subclass ( keys %$MAGE_UNITS ) {
+        foreach my $unitNameCV ( @{ $MAGE_UNITS->{$subclass} } ) {
+            $mage_units->{$subclass}{$unitNameCV}++;
+        }
+    }
+
+    # Find our subclass
+    UNIT_SUBCLASS:
+    foreach my $subclass ( keys %$mage_units ) {
+        if ( $mage_units->{$subclass}{$unitname} ) {
+            $unitclass = $subclass;
+            last UNIT_SUBCLASS;
+        }
+    }
+
+    my ($kindCV) = ( $unitclass =~ m/^(.*)Unit$/ );
+    $kindCV = lc($kindCV);
+
+    my $measurement = &new_measurement(
+        {   class  => $unitclass,
+            unit   => $unitname,
+            kindcv => $kindCV,
+        }
+    );
+
+    return $measurement;
+}
+
+sub new_parameter {
+
+    my ( $identifier, $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::Protocol::Parameter->new unless $obj;
+
+    $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+
+    $args->{name}
+        && do { $obj->setName( $args->{name} ) unless $obj->getName; };
+
+    ( defined( $args->{unit} ) && ( $args->{unit} ne q{} ) ) && do {
+
+        my $measurement = _measurement_from_unit_name( $args->{unit} );
+        $obj->setDefaultValue($measurement) unless $obj->getDefaultValue;
+
+    };
+
+    return $obj;
+
+}
+
+sub new_hardwareapplication {
+
+    my ( $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::Protocol::HardwareApplication->new unless $obj;
+
+    my $hardware = Bio::MAGE::Protocol::Hardware->new(
+        identifier => $args->{hardware} );
+    $obj->setHardware($hardware) unless $obj->getHardware;
+
+    foreach my $param_id ( sort keys %{ $args->{parameters} } ) {
+
+        # No need to keep track of these yet; Affy only FIXME.
+        my $parameter = new_parameter( $param_id );
+
+	unless ( $parameter ) {
+	    croak(qq{Internal error: Parameter "$param_id" not defined.\n});
+	}
+
+        $obj->addParameterValues(
+            Bio::MAGE::Protocol::ParameterValue->new(
+                value         => $args->{parameters}{$param_id},
+                parameterType => $parameter
+            )
+        );
+    }
+
+    return $obj;
+
+}
+
+sub new_bioassaymap {
+
+    my ( $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::BioAssayData::BioAssayMap->new unless $obj;
+
+    # Identifier
+    $args->{identifier_template} && do {
+        my $identifier = "$args->{identifier_template}.BioAssayMap";
+        $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+    };
+
+    # TargetBioAssay
+    $args->{target_bioassay} && do {
+        $obj->setBioAssayMapTarget( $args->{target_bioassay} )
+            unless $obj->getBioAssayMapTarget;
+    };
+
+    # SourceBioAssays
+    $args->{source_bioassays} && do {
+        if ( my $preexisting_bioassays = $obj->getSourceBioAssays ) {
+            foreach my $bioassay ( @{ $args->{source_bioassays} } ) {
+                my $new_identifier = $bioassay->getIdentifier;
+                my $found = first { $_->getIdentifier eq $new_identifier }
+                    @$preexisting_bioassays;
+                $obj->addSourceBioAssays($bioassay) unless $found;
+            }
+        }
+        else {
+            $obj->setSourceBioAssays( $args->{source_bioassays} );
+        }
+    };
+
+    return $obj;
+
+}
+
+sub new_software {
+
+    my ( $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::Protocol::Software->new unless $obj;
+
+    # Identifier
+    my $identifier = "$args->{identifier_template}.Software";
+    $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+
+    # Type
+    if ( $args->{type} ) {
+	my $type = new_ontologyentry(
+	    {   category => $OE_CAT_SOFTWARETYPE,
+		value    => $args->{type}
+	    }
+	);
+	$obj->setType($type) unless $obj->getType;
+    }
+
+    # Name
+    $obj->setName( $args->{name} ) unless $obj->getName;
+
+    return $obj;
+
+}
+
+sub new_hardware {
+
+    my ( $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::Protocol::Hardware->new unless $obj;
+
+    # Identifier
+    my $identifier = "$args->{identifier_template}.Hardware";
+    $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+
+    # Type
+    if ( $args->{type} ) {
+	my $type = new_ontologyentry(
+	    {   category => $OE_CAT_HARDWARETYPE,
+		value    => $args->{type}
+	    }
+	);
+	$obj->setType($type) unless $obj->getType;
+    }
+
+    # Name
+    $obj->setName( $args->{name} ) unless $obj->getName;
+
+    return $obj;
+
+}
+
+1;
+
diff --git a/lib/ArrayExpress/Curator/MAGE/Definitions.pm b/lib/ArrayExpress/Curator/MAGE/Definitions.pm
new file mode 100644
index 0000000..3c02bc4
--- /dev/null
+++ b/lib/ArrayExpress/Curator/MAGE/Definitions.pm
@@ -0,0 +1,1181 @@
+#!/usr/bin/env perl
+#
+# Module to provide constant values for a local installation of the
+# ArrayExpress::Curator::MAGE module.
+#
+# Tim Rayner 2005, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Definitions.pm 1852 2007-12-13 10:14:27Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../../index.html">
+                <img src="../../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: Definitions.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Curator::MAGE::Definitions.pm - a module providing a
+central location for managing EDF column names, OntologyEntry
+categories and values.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Curator::MAGE::Definitions 
+ qw($EDF_EXPTACCESSION 
+    validate_hybridization_section
+    $OE_CAT_PROTOCOLTYPE 
+    $OE_VAL_GROW);
+
+=head1 DESCRIPTION
+
+This module provides a central location for various ontology terms and
+column heading definitions. In other words, if you want to change the
+parsing of your EDF or the output OntologyEntries, edit this
+module. Also provided is a series of optional validation subroutines for
+checking column or row names against the expected EDF headers.
+
+=head1 SUPPORTED HEADINGS
+
+=cut
+
+package ArrayExpress::Curator::MAGE::Definitions;
+
+use strict;
+use warnings;
+
+use Readonly;
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+use base 'Exporter';
+
+our @EXPORT_OK = qw(
+    validate_experiment_section
+    validate_protocol_section
+    validate_hybridization_section
+
+    $EDF_EXPTACCESSION
+    $EDF_EXPTDOMAIN
+    $EDF_EXPTNAME
+    $EDF_EXPTDESCRIPTION
+    $EDF_EXPTRELEASEDATE
+    $EDF_EXPTSUBMITTER
+    $EDF_EXPTDATACODER
+    $EDF_EXPTCURATOR
+    $EDF_EXPTINVESTIGATOR
+    $EDF_EXPTORGANIZATION
+    $EDF_EXPTADDRESS
+    $EDF_EXPTSUBMITTER_EMAIL
+    $EDF_EXPTCURATOR_EMAIL
+    $EDF_EXPTINVESTIGATOR_EMAIL
+    $EDF_EXPTDATACODER_EMAIL
+    $EDF_EXPTSUBMISSIONDATE
+    $EDF_EXPTDESIGNTYPE
+    $EDF_EXPTQUALITYCONTROL
+    $EDF_EXPTCURATEDNAME
+    $EDF_EXPTURI
+    $EDF_EXPTGEORELEASEDATE
+    $EDF_EXPTSECONDARYACCESSION
+
+    $EDF_PUBTITLE
+    $EDF_PUBPAGES
+    $EDF_PUBJOURNAL
+    $EDF_PUBVOLUME
+    $EDF_PUBISSUE
+    $EDF_PUBYEAR
+    $EDF_PUBAUTHORS
+    $EDF_PUBURI
+    $EDF_PUBMEDID
+
+    $EDF_PROTOCOLACCESSION
+    $EDF_PROTOCOLNAME
+    $EDF_PROTOCOLTYPE
+    $EDF_PROTOCOLTEXT
+    $EDF_PROTOCOLPARAMS
+
+    $EDF_BIOSOURCENAME
+    $EDF_BIOSOURCETYPE
+    $EDF_BIOSOURCEDESCRIPTION
+    $EDF_BIOSAMPLENAME
+    $EDF_BIOSAMPLETYPE
+    $EDF_EXTRACTNAME
+    $EDF_EXTRACTTYPE
+    $EDF_IMMUNOPRECIPITATENAME
+    $EDF_IMMUNOPRECIPITATETYPE
+    $EDF_LABELEDEXTRACTNAME
+    $EDF_LABELEDEXTRACTTYPE
+    $EDF_HYBRIDIZATIONNAME
+    $EDF_SCANNAME
+    $EDF_NORMALIZATIONNAME
+    $EDF_NORMALIZATIONTYPE
+    $EDF_TRANSFORMATIONNAME
+    $EDF_TRANSFORMATIONTYPE
+    $EDF_DYE
+
+    $EDF_GROW_PROTOCOL
+    $EDF_TREAT_PROTOCOL
+    $EDF_EXTRACT_PROTOCOL
+    $EDF_POOL_PROTOCOL
+    $EDF_LABEL_PROTOCOL
+    $EDF_IP_PROTOCOL
+    $EDF_HYB_PROTOCOL
+    $EDF_SCAN_PROTOCOL
+    $EDF_FEXT_PROTOCOL
+    $EDF_NORM_PROTOCOL
+    $EDF_TRXN_PROTOCOL
+
+    $EDF_SCAN_SOFTWARE
+    $EDF_FEXT_SOFTWARE
+    $EDF_NORM_SOFTWARE
+    $EDF_TRXN_SOFTWARE
+
+    $EDF_IMAGE_FORMAT
+
+    $EDF_ARRAYACCESSION
+    $EDF_ARRAYSERIAL
+
+    $EDF_FILE_PREFIX
+    $EDF_FILE_SUFFIX
+    $EDF_BMC_PREFIX
+    $EDF_BMC_SUFFIX
+    $EDF_FV_PREFIX
+    $EDF_FV_SUFFIX
+    $EDF_PARAM_PREFIX
+    $EDF_PARAM_SUFFIX
+
+    $EDF_SAMPLE_PARAMS
+    $EDF_TREAT_PARAMS
+    $EDF_EXTRACT_PARAMS
+    $EDF_IP_PARAMS
+    $EDF_LABEL_PARAMS
+    $EDF_HYB_PARAMS
+    $EDF_SCAN_PARAMS
+    $EDF_FEXT_PARAMS
+    $EDF_NORM_PARAMS
+    $EDF_TRXN_PARAMS
+
+    $EDF_NORM_STATS
+    $EDF_FEXT_STATS
+    $EDF_TRXN_STATS
+
+    $EDF_HYB_HARDWARE
+    $EDF_SCAN_HARDWARE
+    $EDF_HYB_HW_PARAMS
+    $EDF_SCAN_HW_PARAMS
+
+    $OE_CAT_ACTION
+    $OE_CAT_BIOSAMPLETYPE
+    $OE_CAT_MATERIALTYPE
+    $OE_CAT_PROTOCOLTYPE
+    $OE_CAT_SOFTWARETYPE
+    $OE_CAT_HARDWARETYPE
+    $OE_CAT_DERIVEDBIOASSAYTYPE
+    $OE_CAT_EXPERIMENTALFACTORCATEGORY
+    $OE_CAT_DATATYPE
+    $OE_CAT_SCALE
+    $OE_CAT_EXPERIMENTDESIGNTYPE
+    $OE_CAT_QUALITYCONTROLDESCRIPTIONTYPE
+    $OE_CAT_PUBLICATIONTYPE
+    $OE_CAT_RELEASEDATE
+    $OE_CAT_SUBMISSIONDATE
+    $OE_CAT_ROLE
+
+    $OE_VAL_SYNTHETICDNA
+    $OE_VAL_SYNTHETICRNA
+    $OE_VAL_TOTALRNA
+    $OE_VAL_GENOMICDNA
+    $OE_VAL_WHOLEORGANISM
+    $OE_VAL_ORGANISMPART
+    $OE_VAL_EXTRACT
+    $OE_VAL_NOTEXTRACT
+    $OE_VAL_FLOAT
+    $OE_VAL_UNKNOWN
+    $OE_VAL_LINEARSCALE
+    $OE_VAL_JOURNALARTICLE
+    $OE_VAL_GROW
+    $OE_VAL_POOL
+    $OE_VAL_SPECIFIEDBIOMATERIALACTION
+    $OE_VAL_NUCLEICACIDEXTRACTION
+    $OE_VAL_IMMUNOPRECIPITATE
+    $OE_VAL_LABELING
+    $OE_VAL_NORMALIZATION
+    $OE_VAL_HYBRIDIZATION
+    $OE_VAL_SCANNING
+    $OE_VAL_FEATUREEXTRACTION
+    $OE_VAL_SCANNING_SOFTWARE
+    $OE_VAL_ANALYSIS_SOFTWARE
+    $OE_VAL_TRANSFORMATION_SOFTWARE
+    $OE_VAL_SUBMITTER
+    $OE_VAL_DATA_CODER
+    $OE_VAL_CURATOR
+    $OE_VAL_INVESTIGATOR
+
+    $OE_VAL_CHIP_CHIP
+
+    $AE_LABELCOMPOUND_PREFIX
+    $AE_CHANNEL_PREFIX
+
+    $MAGE_UNITS
+);
+
+#####################################################################
+# NOTE: all column headers are lowercased during parsing; therefore #
+# only lowercase tags can be used here.                             #
+#####################################################################
+
+#######################
+# EDF column headings #
+#######################
+
+# Top-level experiment information
+Readonly our $EDF_EXPTACCESSION          => 'accession';
+Readonly our $EDF_EXPTDOMAIN             => 'domain';
+Readonly our $EDF_EXPTNAME               => 'name';
+Readonly our $EDF_EXPTDESCRIPTION        => 'description';
+Readonly our $EDF_EXPTRELEASEDATE        => 'release_date';
+Readonly our $EDF_EXPTSUBMITTER          => 'submitter';
+Readonly our $EDF_EXPTDATACODER          => 'data_coder';
+Readonly our $EDF_EXPTCURATOR            => 'curator';
+Readonly our $EDF_EXPTINVESTIGATOR       => 'investigator';
+Readonly our $EDF_EXPTORGANIZATION       => 'organization';
+Readonly our $EDF_EXPTADDRESS            => 'address';
+Readonly our $EDF_EXPTSUBMITTER_EMAIL    => 'submitter_email';
+Readonly our $EDF_EXPTDATACODER_EMAIL    => 'data_coder_email';
+Readonly our $EDF_EXPTCURATOR_EMAIL      => 'curator_email';
+Readonly our $EDF_EXPTINVESTIGATOR_EMAIL => 'investigator_email';
+Readonly our $EDF_EXPTSUBMISSIONDATE     => 'submission_date';
+Readonly our $EDF_EXPTDESIGNTYPE         => 'experiment_design_type';
+Readonly our $EDF_EXPTQUALITYCONTROL     => 'quality_control';
+Readonly our $EDF_EXPTCURATEDNAME        => 'ae_display_name';
+Readonly our $EDF_EXPTURI                => 'uri';
+Readonly our $EDF_EXPTGEORELEASEDATE     => 'geo_release_date';
+Readonly our $EDF_EXPTSECONDARYACCESSION => 'secondary_accession';
+
+=pod
+
+=head2 Experiment section: row names
+
+=over 1
+
+=item accession
+
+A string unique to the experiment. Used in all identifiers, and as a
+top-level Experiment identifier. Synonymous with the ArrayExpress
+accession number for an experiment. Example: E-MEXP-100
+
+=item domain
+
+A string identifying the origin of the information provided. Typically
+this will be an internet domain name such as "ebi.ac.uk". This string
+is used to define the namespace of the MAGE identifiers created.
+
+=item name
+
+The name of the experiment. The forms the name attribute of the
+top-level Experiment object.
+
+=item description
+
+A short description of the experiment. This is inserted into a
+Description text attribute attached to the top-level Experiment
+object.
+
+=item release_date
+
+In the form YYYY-MM-DDB<T>hh:mm:ssB<Z>. This is currently used in a
+NameValueType (name ArrayExpressReleaseDate) in the top-level
+Experiment object.
+
+=item submission_date
+
+In the form YYYY-MM-DDB<T>hh:mm:ssB<Z>. This is currently used in a
+NameValueType (name ArrayExpressSubmissionDate) in the top-level
+Experiment object.
+
+=item experiment_design_type
+
+A comma-separated list of MO ExperimentDesignType terms used in the
+top-level ExperimentDesign object.
+
+=item quality_control
+
+A comma-separated list of MO QualityControlDescriptionType used in the
+top-level ExperimentDesign object.
+
+=item submitter
+
+The name of the person submitting the experiment. This is used to
+create a Person object in the AuditAndSecurity package with a
+Roles:submitter OntologyEntry. The Person object is referenced in the
+Experiment package.
+
+=item submitter_email
+
+The email address of the person submitting the experiment.
+
+=item data_coder
+
+The name of the person responsible for coding the experiment into
+MAGE-ML. This is used to create a Person object in the
+AuditAndSecurity package with a Roles:data_coder OntologyEntry. The
+Person object is referenced in the Experiment package.
+
+=item data_coder_email
+
+The email address of the person who coded the experiment in MAGE-ML.
+
+=item curator
+
+The name of the person who curated the experiment. This is used to
+create a Person object in the AuditAndSecurity package with a
+Roles:curator OntologyEntry. The Person object is referenced in the
+Experiment package.
+
+=item curator_email
+
+The email address of the person who curated the experiment.
+
+=item investigator
+
+The name of the primary investigator on the experiment. This is used
+to create a Person object in the AuditAndSecurity package with a
+Roles:curator OntologyEntry. The Person object is referenced in the
+Experiment package.
+
+=item investigator_email
+
+The email address of the primary investigator.
+
+=item organization
+
+The name of the organisation to which the submitter is
+affiliated. This is used to create an Organization MAGE object, which
+is then associated with each Person object.
+
+=item address
+
+The address of the organisation to which the submitter is
+affiliated.
+
+=item ae_display_name
+
+The name of the experiment to be displayed in the ArrayExpress
+repository interface. This will typically be added by the ArrayExpress
+curators after data submission.
+
+=item URI
+
+A URI pointing to an alternative location for the experimental data,
+e.g. in a public repository database.
+
+=item geo_release_date
+
+For processing of GEO experiments; this value is used to create a
+"GEOReleaseDate" NameValueType object associated with the top-level
+Experiment; this is used internally by the ArrayExpress database.
+
+=item secondary_accession
+
+This value is used to populate the "SecondaryAcession" NameValueType
+object associated with the top-level Experiment. This is used
+internally by ArrayExpress to represent e.g. GEO accession numbers.
+
+=back
+
+=cut
+
+# Publication info
+Readonly our $EDF_PUBTITLE   => 'publication_title';
+Readonly our $EDF_PUBPAGES   => 'pages';
+Readonly our $EDF_PUBJOURNAL => 'journal';
+Readonly our $EDF_PUBVOLUME  => 'volume';
+Readonly our $EDF_PUBISSUE   => 'issue';
+Readonly our $EDF_PUBYEAR    => 'year';
+Readonly our $EDF_PUBAUTHORS => 'authors';
+Readonly our $EDF_PUBURI     => 'publication_uri';
+Readonly our $EDF_PUBMEDID   => 'pubmed_id';
+
+=pod
+
+The following are used in a BibliographicReference associated with a
+second Description object in the top-level Experiment object:
+
+=over 1
+
+=item publication_title
+
+The free-text title of any associated publication. This is currently
+assumed to have PublicationType journal_article (MO), although more
+control over this may be added in future. Used in 
+
+=item authors
+
+A free-text list of publication authors.
+
+=item journal
+
+The name of the journal. This should be a standard Pubmed abbreviation.
+
+=item year
+
+Year of publication.
+
+=item volume
+
+Journal volume.
+
+=item issue
+
+Journal issue.
+
+=item pages
+
+Page range of journal article.
+
+=item publication_URI
+
+Any URI associated with the publication.
+
+=item pubmed_id
+
+The Pubmed ID associated with the publication.
+
+=back
+
+=cut
+
+# Protocol information
+Readonly our $EDF_PROTOCOLACCESSION => 'accession';
+Readonly our $EDF_PROTOCOLNAME      => 'name';
+Readonly our $EDF_PROTOCOLTYPE      => 'type';
+Readonly our $EDF_PROTOCOLTEXT      => 'text';
+Readonly our $EDF_PROTOCOLPARAMS    => 'parameters';
+
+=pod
+
+=head2 Protocol section: column names
+
+The following are all attached to individual protocols defined by
+successive lines in this section:
+
+=over 1
+
+=item accession
+
+Database (e.g. ArrayExpress) accession no.
+
+=item name
+
+Protocol name.
+
+=item type
+
+MO ProtocolType term; this is an optional field. In its absence,
+Tab2MAGE will use default ProtocolType terms based on how the protocol
+is used within the Hybridization section.
+
+=item text
+
+Protocol text.
+
+=item parameters
+
+Protocol parameters, listed in the following form:
+name1(unit1);name2(unit2);...
+
+=back
+
+=cut
+
+# BioMaterials
+Readonly our $EDF_BIOSOURCENAME         => 'biosource';
+Readonly our $EDF_BIOSOURCETYPE         => 'biosourcematerial';
+Readonly our $EDF_BIOSOURCEDESCRIPTION  => 'biosourcedescription';
+Readonly our $EDF_BIOSAMPLENAME         => 'sample';
+Readonly our $EDF_BIOSAMPLETYPE         => 'samplematerial';
+Readonly our $EDF_EXTRACTNAME           => 'extract';
+Readonly our $EDF_EXTRACTTYPE           => 'extractmaterial';
+Readonly our $EDF_IMMUNOPRECIPITATENAME => 'immunoprecipitate';
+Readonly our $EDF_IMMUNOPRECIPITATETYPE => 'immunoprecipitatematerial';
+Readonly our $EDF_LABELEDEXTRACTNAME    => 'labeledextract';
+Readonly our $EDF_LABELEDEXTRACTTYPE    => 'labeledextractmaterial';
+Readonly our $EDF_HYBRIDIZATIONNAME     => 'hybridization';
+Readonly our $EDF_SCANNAME              => 'scan';
+Readonly our $EDF_NORMALIZATIONNAME     => 'normalization';
+Readonly our $EDF_NORMALIZATIONTYPE     => 'normalizationtype';
+Readonly our $EDF_TRANSFORMATIONNAME    => 'transformation';
+Readonly our $EDF_TRANSFORMATIONTYPE    => 'transformationtype';
+Readonly our $EDF_DYE                   => 'dye';
+Readonly our $EDF_IMAGE_FORMAT          => 'ImageFormat';
+
+=pod
+
+=head2 Hybridization section: column names
+
+=over 1
+
+=item BioSource
+
+Arbitrary name for a BioSource. This term is used as a unique
+identifier within a Tab2MAGE run to determine correct linking between
+objects. It may however be omitted, in favour of using the set of
+BioMaterialCharacteristics associated with a BioMaterial as the sole
+indicator of BioSource identity.
+
+=item Sample
+
+Arbitrary name for a BioSample (associated with a
+BioSampleType:not_extract OntologyEntry). Used to control linking
+between objects; may be omitted if desired, in which case a BioSample
+name constructed from the raw data filename is used instead.
+
+=item Extract
+
+Arbitrary name for a BioSample (associated with a
+BioSampleType:extract OntologyEntry). Used to control linking
+between objects; may be omitted if desired, in which case a Extract
+name constructed from the raw data filename is used instead.
+
+=item Immunoprecipitate
+
+Arbitrary name for a BioSample (associated with a
+BioSampleType:extract OntologyEntry). Used to control linking between
+objects; may be omitted if desired, in which case an Immunoprecipitate
+name constructed from the raw data filename is used instead. These
+objects are used in ChIP experiments and may be ignored for expression
+or CGH studies.
+
+=item LabeledExtract
+
+Arbitrary name for a LabeledExtract. Used to control linking between
+objects; may be omitted if desired, in which case a LabeledExtract name
+constructed from the raw data filename and the label dye name is used
+instead.
+
+=item Dye
+
+Name of the dye linked to the labeled extract (e.g., Cy3, Cy5, biotin).
+
+=item BioSourceMaterial
+
+MO MaterialType term to be attached to the BioSource. Default: whole_organism
+
+=item SampleMaterial
+
+MO MaterialType term to be attached to the BioSample. Default: organism_part
+
+=item ExtractMaterial
+
+MO MaterialType term to be attached to the Extract. Default: total_RNA
+
+=item ImmunoprecipitateMaterial
+
+MO MaterialType term to be attached to the Immunoprecipitate. Default: genomic_DNA
+
+=item LabeledExtractMaterial
+
+MO MaterialType term to be attached to the LabeledExtract. Default: synthetic_DNA
+
+=item BioSourceDescription
+
+Free-text description to attached to the BioSource (this should be
+used sparingly, if at all).
+
+=item Hybridization
+
+Arbitrary name for a hybridization. Used to control linking between
+objects; may be omitted if desired, in which case a Hybridization name
+constructed from the raw data filename is used instead.
+
+=item Scan
+
+Arbitrary name for a scanning event. Used to control linking between
+objects; may be omitted if desired, in which case a Scan name
+constructed from the raw data filename is used instead.
+
+=item Normalization
+
+Arbitrary name for a normalization procedure. Used to control linking
+between objects; may be omitted if desired, in which case a
+Normalization name constructed from the normalized data filename is
+used instead.
+
+=item NormalizationType
+
+MO DerivedBioAssayType term, attached to the relevant
+DerivedBioAssayData object.
+
+=item Transformation
+
+Arbitrary name for a data transformation procedure. Used to control linking
+between objects; may be omitted if desired.
+
+=item TransformationType
+
+MO DerivedBioAssayType term, attached to the relevant
+DerivedBioAssayData object.
+
+=item ImageFormat           
+
+MO ImageFormat term. Only used if File[image] columns have been
+included in the spreadsheet. Used to create Image objects.
+
+=back
+
+=cut
+
+# Protocol accessions
+Readonly our $EDF_GROW_PROTOCOL    => 'protocol[grow]';
+Readonly our $EDF_TREAT_PROTOCOL   => 'protocol[treatment]';
+Readonly our $EDF_EXTRACT_PROTOCOL => 'protocol[extraction]';
+Readonly our $EDF_POOL_PROTOCOL    => 'protocol[pool]';
+Readonly our $EDF_LABEL_PROTOCOL   => 'protocol[labeling]';
+Readonly our $EDF_IP_PROTOCOL      => 'protocol[immunoprecipitate]';
+Readonly our $EDF_HYB_PROTOCOL     => 'protocol[hybridization]';
+Readonly our $EDF_SCAN_PROTOCOL    => 'protocol[scanning]';
+Readonly our $EDF_FEXT_PROTOCOL    => 'protocol[image_analysis]';
+Readonly our $EDF_NORM_PROTOCOL    => 'protocol[normalization]';
+Readonly our $EDF_TRXN_PROTOCOL    => 'protocol[transformation]';
+
+=pod
+
+=over 1
+
+=item Protocol[grow]
+
+Accession number for the "growth" protocol (BioSource->BioSample
+Treatment). The accession number should either be present in the
+protocol section of the spreadsheet, or pre-existing in
+ArrayExpress. Default ProtocolType: grow
+
+=item Protocol[treatment]
+
+Accession number for the "treatment" protocol (BioSource->BioSample
+Treatment). The accession number should either be present in the
+protocol section of the spreadsheet, or pre-existing in
+ArrayExpress. Default ProtocolType: specified_biomaterial_action
+
+=item Protocol[extraction]
+
+Accession number for the "extraction" protocol (BioSample->Extract
+Treatment). The accession number should either be present in the
+protocol section of the spreadsheet, or pre-existing in
+ArrayExpress. Default ProtocolType: nucleic_acid_extraction
+
+=item Protocol[pool]
+
+Accession number for the "pooling" protocol (BioSample->Extract
+Treatment). The accession number should either be present in the
+protocol section of the spreadsheet, or pre-existing in
+ArrayExpress. Default ProtocolType: pool
+
+=item Protocol[labeling]
+
+Accession number for the "labeling" protocol (Extract->LabeledExtract
+Treatment). The accession number should either be present in the
+protocol section of the spreadsheet, or pre-existing in
+ArrayExpress. Default ProtocolType: labeling
+
+=item Protocol[immunoprecipitate]
+
+Accession number for the "immunoprecipitation" protocol
+(Extract->Immunoprecipitate Treatment). The accession number should
+either be present in the protocol section of the spreadsheet, or
+pre-existing in ArrayExpress. Omit if Immunoprecipitates are not used
+in the experiment. Default ProtocolType: immunoprecipitate
+
+=item Protocol[hybridization]
+
+Accession number for the "hybridization" protocol (PhysicalBioAssay
+BioAssayCreation). The accession number should either be present in
+the protocol section of the spreadsheet, or pre-existing in
+ArrayExpress. Default ProtocolType: hybridization
+
+=item Protocol[scanning]
+
+Accession number for the "image acquisition" protocol
+(PhysicalBioAssay BioAssayTreatment). The accession number should
+either be present in the protocol section of the spreadsheet, or
+pre-existing in ArrayExpress. Default ProtocolType: image_acquisition;
+note however that if Protocol[image_analysis] is not specified then
+the scanning protocol defaults to feature_extraction. This is an
+ArrayExpress-specific behaviour and relates to the appearance of the
+experiment in the ArrayExpress web interface.
+
+=item Protocol[image_analysis]
+
+Accession number for the "feature extraction" protocol
+(MeasuredBioAssay FeatureExtraction). The accession number should
+either be present in the protocol section of the spreadsheet, or
+pre-existing in ArrayExpress. Default ProtocolType: feature_extraction
+
+=item Protocol[normalization]
+
+Accession number for the "normalization" protocol (DerivedBioAssayData
+ProducerTransformation). The accession number should either be present
+in the protocol section of the spreadsheet, or pre-existing in
+ArrayExpress. Default ProtocolType: bioassay_data_transformation
+
+=item Protocol[transformation]
+
+Accession number for the "transformation" protocol (DerivedBioAssayData
+ProducerTransformation). The accession number should either be present
+in the protocol section of the spreadsheet, or pre-existing in
+ArrayExpress. Default ProtocolType: bioassay_data_transformation
+
+=back
+
+=cut
+
+# Software
+
+Readonly our $EDF_SCAN_SOFTWARE => 'software[scanning]';
+Readonly our $EDF_FEXT_SOFTWARE => 'software[image_analysis]';
+Readonly our $EDF_NORM_SOFTWARE => 'software[normalization]';
+Readonly our $EDF_TRXN_SOFTWARE => 'software[transformation]';
+
+=pod
+
+Software is created in the Protocol package, and referenced as
+SoftwareApplication in the appropriate ProtocolApplication (see
+above).
+
+=over 1
+
+=item Software[scanning]
+
+Name of the scanning software, followed by its version in
+parentheses. The Software name is used to create a Software object in
+the Protocol package, and the version is inserted into the relevant
+SoftwareApplication objects.
+
+=item Software[image_analysis]
+
+Name of the feature extraction software, followed by its version in
+parentheses. The Software name is used to create a Software object in
+the Protocol package, and the version is inserted into the relevant
+SoftwareApplication objects.
+
+=item Software[normalization]
+
+Name of the normalization software, followed by its version in
+parentheses. The Software name is used to create a Software object in
+the Protocol package, and the version is inserted into the relevant
+SoftwareApplication objects.
+
+=item Software[transformation]
+
+Name of the data transformation software, followed by its version in
+parentheses. The Software name is used to create a Software object in
+the Protocol package, and the version is inserted into the relevant
+SoftwareApplication objects.
+
+=back
+
+=cut
+
+# Array information
+Readonly our $EDF_ARRAYACCESSION => 'array[accession]';
+Readonly our $EDF_ARRAYSERIAL    => 'array[serial]';
+
+=pod
+
+=over 1
+
+=item Array[accession]
+
+ArrayExpress array accession number (e.g., A-MEXP-1). This is used in
+the Array package, and also to create Feature and Reporter identifiers
+for the DesignElementDimensions.
+
+=item Array[serial]
+
+Serial or lot number of the array. This is used to define
+ArrayManufacture objects within the Array package.
+
+=back
+
+=cut
+
+# Compound headings
+Readonly our $EDF_FILE_PREFIX  => 'file[';
+Readonly our $EDF_FILE_SUFFIX  => ']';
+Readonly our $EDF_BMC_PREFIX   => 'biomaterialcharacteristics[';
+Readonly our $EDF_BMC_SUFFIX   => ']';
+Readonly our $EDF_FV_PREFIX    => 'factorvalue[';
+Readonly our $EDF_FV_SUFFIX    => ']';
+Readonly our $EDF_PARAM_PREFIX => 'parameter[';
+Readonly our $EDF_PARAM_SUFFIX => ']';
+
+=pod
+
+=over 1
+
+=item File[raw]
+
+Name of the raw data file for a given hybridization.
+
+=item File[normalized]
+
+Name of the normalized data file for a given hybridization/normalization.
+
+=item File[transformed]
+
+Name of the transformed final data matrix file for the experiment.
+
+=item File[image]
+
+URI locating the Image object associated with an ImageAcquisition
+(scanning) event. ArrayExpress does not store images, although we can
+link to images stored on external web sites, where desired.
+
+=item File[cdf]
+
+Affymetrix array library file pertaining to a given
+hybridization. Required for correct parsing of Affymetrix data files,
+although for standard Affymetrix arrays the actual CDF file does not
+need to be supplied.
+
+=item File[exp]
+
+Affymetrix experiment description file pertaining to a given
+hybridization. Required for correct parsing of Affymetrix data files.
+
+=item BioMaterialCharacteristics[<I<category>>]
+
+MO term describing some feature of the BioSource (e.g., Genotype, Sex,
+DiseaseState, etc.). As many BioMaterialCharacteristics columns may be
+used as are desired. Each column should contain values from a
+different B<category> within the MGED Ontology.
+
+=item FactorValue[<I<category>>]
+
+MO term describing a FactorValue associated with a given hybridization
+(e.g., Genotype, Sex, DiseaseState, etc.). As many FactorValue columns
+may be used as are desired. Each column should contain values from a
+different B<category> within the MGED Ontology.
+
+=item Parameter[<I<parameter name>>]
+
+Parameter values for the parameters declared in the Protocol section
+of the spreadsheet. Note that a protocol must be declared in the
+spreadsheet in order to be able to use parameters with it.
+
+=back
+
+=cut
+
+# These are for internal use only. These are name=>value hashes thoughout.
+Readonly our $EDF_SAMPLE_PARAMS  => 'TMparameters[sample]';
+Readonly our $EDF_TREAT_PARAMS   => 'TMparameters[treat]';
+Readonly our $EDF_EXTRACT_PARAMS => 'TMparameters[extract]';
+Readonly our $EDF_IP_PARAMS      => 'TMparameters[ip]';
+Readonly our $EDF_LABEL_PARAMS   => 'TMparameters[label]';
+Readonly our $EDF_HYB_PARAMS     => 'TMparameters[hybridization]';
+Readonly our $EDF_SCAN_PARAMS    => 'TMparameters[scan]';
+Readonly our $EDF_FEXT_PARAMS    => 'TMparameters[fext]';
+Readonly our $EDF_NORM_PARAMS    => 'TMparameters[norm]';
+Readonly our $EDF_TRXN_PARAMS    => 'TMparameters[trxn]';
+Readonly our $EDF_FEXT_STATS     => 'TMstatistics[fext]';
+Readonly our $EDF_NORM_STATS     => 'TMstatistics[norm]';
+Readonly our $EDF_TRXN_STATS     => 'TMstatistics[trxn]';
+
+# Internal use only - Affymetrix. These will be linked to fully-formed objects
+Readonly our $EDF_HYB_HARDWARE   => 'TMhardware[hybridization]';
+Readonly our $EDF_SCAN_HARDWARE  => 'TMhardware[scan]';
+Readonly our $EDF_HYB_HW_PARAMS  => 'TMhwparams[hybridization]';
+Readonly our $EDF_SCAN_HW_PARAMS => 'TMhwparams[scan]';
+
+##################
+# Ontology terms #
+##################
+
+# Categories
+Readonly our $OE_CAT_ACTION                     => 'Action';
+Readonly our $OE_CAT_BIOSAMPLETYPE              => 'BioSampleType';
+Readonly our $OE_CAT_MATERIALTYPE               => 'MaterialType';
+Readonly our $OE_CAT_PROTOCOLTYPE               => 'ProtocolType';
+Readonly our $OE_CAT_SOFTWARETYPE               => 'SoftwareType';
+Readonly our $OE_CAT_HARDWARETYPE               => 'HardwareType';
+Readonly our $OE_CAT_DERIVEDBIOASSAYTYPE        => 'DerivedBioAssayType';
+Readonly our $OE_CAT_EXPERIMENTALFACTORCATEGORY =>
+    'ExperimentalFactorCategory';
+Readonly our $OE_CAT_DATATYPE                      => 'DataType';
+Readonly our $OE_CAT_SCALE                         => 'Scale';
+Readonly our $OE_CAT_EXPERIMENTDESIGNTYPE          => 'ExperimentDesignType';
+Readonly our $OE_CAT_QUALITYCONTROLDESCRIPTIONTYPE =>
+    'QualityControlDescriptionType';
+Readonly our $OE_CAT_PUBLICATIONTYPE => 'PublicationType';
+Readonly our $OE_CAT_RELEASEDATE     => 'ReleaseDate';
+Readonly our $OE_CAT_SUBMISSIONDATE  => 'SubmissionDate';
+Readonly our $OE_CAT_ROLE            => 'Roles';
+
+# Values
+Readonly our $OE_VAL_SYNTHETICDNA               => 'synthetic_DNA';
+Readonly our $OE_VAL_SYNTHETICRNA               => 'synthetic_RNA';
+Readonly our $OE_VAL_TOTALRNA                   => 'total_RNA';
+Readonly our $OE_VAL_GENOMICDNA                 => 'genomic_DNA';
+Readonly our $OE_VAL_WHOLEORGANISM              => 'whole_organism';
+Readonly our $OE_VAL_ORGANISMPART               => 'organism_part';
+Readonly our $OE_VAL_EXTRACT                    => 'extract';
+Readonly our $OE_VAL_NOTEXTRACT                 => 'not_extract';
+Readonly our $OE_VAL_FLOAT                      => 'float';
+Readonly our $OE_VAL_UNKNOWN                    => 'unknown';
+Readonly our $OE_VAL_LINEARSCALE                => 'linear_scale';
+Readonly our $OE_VAL_JOURNALARTICLE             => 'journal_article';
+Readonly our $OE_VAL_GROW                       => 'grow';
+Readonly our $OE_VAL_POOL                       => 'pool';
+Readonly our $OE_VAL_SPECIFIEDBIOMATERIALACTION =>
+    'specified_biomaterial_action';
+Readonly our $OE_VAL_NUCLEICACIDEXTRACTION => 'nucleic_acid_extraction';
+Readonly our $OE_VAL_IMMUNOPRECIPITATE     => 'immunoprecipitate';
+Readonly our $OE_VAL_LABELING              => 'labeling';
+Readonly our $OE_VAL_NORMALIZATION         => 'bioassay_data_transformation';
+Readonly our $OE_VAL_HYBRIDIZATION         => 'hybridization';
+Readonly our $OE_VAL_SCANNING              => 'image_acquisition';
+Readonly our $OE_VAL_FEATUREEXTRACTION     => 'feature_extraction';
+Readonly our $OE_VAL_SCANNING_SOFTWARE     => 'image_acquisition_software';
+Readonly our $OE_VAL_ANALYSIS_SOFTWARE     => 'feature_extraction_software';
+Readonly our $OE_VAL_TRANSFORMATION_SOFTWARE =>
+    'bioassay_data_transformation_software';
+Readonly our $OE_VAL_SUBMITTER    => 'submitter';
+Readonly our $OE_VAL_CURATOR      => 'curator';
+Readonly our $OE_VAL_DATA_CODER   => 'data_coder';
+Readonly our $OE_VAL_INVESTIGATOR => 'investigator';
+
+Readonly our $OE_VAL_CHIP_CHIP    => 'binding_site_identification_design';
+
+###############################
+# MAGE identifier conventions #
+###############################
+Readonly our $AE_LABELCOMPOUND_PREFIX => 'ebi.ac.uk:LabelCompound:';
+Readonly our $AE_CHANNEL_PREFIX       => 'ebi.ac.uk:Channel:';
+
+########################
+# MAGE Unit subclasses #
+########################
+
+Readonly our $MAGE_UNITS => {
+    TemperatureUnit => [qw(degree_C degree_F K)],
+    MassUnit        => [qw(kg g mg ug ng pg fg)],
+    VolumeUnit      => [qw(mL cc dL L uL nL pL fL)],
+
+    # m (meters) not supported: it clashes with m (minutes).
+    DistanceUnit      => [qw(fm pm nm um mm cm)],
+    TimeUnit          => [qw(years months weeks d h m s us)],
+    QuantityUnit      => [qw(mol amol fmol pmol nmol umol mmol molecules)],
+    ConcentrationUnit => [
+        qw(M mM uM nM pM fM mg_per_mL mL_per_L g_per_L gram_percent mass_per_volume_percent mass_per_mass_percent)
+    ],
+};
+
+##########################
+# Validation subroutines #
+##########################
+
+=pod
+
+=head1 VALIDATION SUBROUTINES
+
+=over 1
+
+=item validate_expriment_section($test_arrayref, $error_fh)
+
+=item validate_protocol_section($test_arrayref, $error_fh)
+
+=item validate_hybridization_section($test_arrayref, $error_fh)
+
+Each of these subroutines simply takes a reference to an array
+containing the list of headings to be checked, and prints warnings on
+STDERR (and on the optional error filehandle) if it finds unrecognized
+headings. No further action is taken; these subroutines merely serve
+as a warning to the user.
+
+=back
+
+=cut
+
+sub _check_section {
+
+    my ( $testarray, $matcharray, $section, $error_fh ) = @_;
+
+    my $error = 0;
+
+    # Quote brackets and parentheses in each pattern to be matched
+    foreach my $entry (@$matcharray) {
+        $entry =~ s/([\[\]\(\)])/\\$1/g;
+    }
+
+    # Check each heading in the array to be tested
+    foreach my $header (@$testarray) {
+
+        # Print to STDERR and to the logfile
+        unless ( map { $header =~ m/^$_$/ } @$matcharray ) {
+            my $error_str
+                = qq{Warning: Unknown column/row name in $section section will be ignored: "$header"\n};
+            print STDERR $error_str;
+            print $error_fh ($error_str) if $error_fh;
+            $error |= $CONFIG->get_ERROR_PARSEBAD();
+        }
+    }
+
+    return $error;
+}
+
+sub validate_experiment_section {
+
+    my ( $testarray, $error_fh ) = @_;
+
+    my $matcharray = [
+        $EDF_EXPTACCESSION,       $EDF_EXPTDOMAIN,
+        $EDF_EXPTNAME,            $EDF_EXPTDESCRIPTION,
+        $EDF_EXPTRELEASEDATE,     $EDF_EXPTSUBMISSIONDATE,
+        $EDF_EXPTSUBMITTER,       $EDF_EXPTCURATOR,
+        $EDF_EXPTDATACODER,       $EDF_EXPTINVESTIGATOR,
+        $EDF_EXPTORGANIZATION,    $EDF_EXPTADDRESS,
+        $EDF_EXPTSUBMITTER_EMAIL, $EDF_EXPTCURATOR_EMAIL,
+        $EDF_EXPTDATACODER_EMAIL, $EDF_EXPTINVESTIGATOR_EMAIL,
+        $EDF_EXPTDESIGNTYPE,      $EDF_EXPTQUALITYCONTROL,
+        $EDF_EXPTCURATEDNAME,     $EDF_EXPTURI,
+	$EDF_EXPTGEORELEASEDATE,  $EDF_EXPTSECONDARYACCESSION,
+	$EDF_PUBTITLE,
+        $EDF_PUBPAGES,            $EDF_PUBJOURNAL,
+        $EDF_PUBVOLUME,           $EDF_PUBISSUE,
+        $EDF_PUBYEAR,             $EDF_PUBAUTHORS,
+        $EDF_PUBURI,              $EDF_PUBMEDID,
+    ];
+
+    my $error |= _check_section(
+	$testarray,
+	$matcharray,
+	'Experiment',
+	$error_fh,
+    );
+
+    return $error;
+
+}
+
+sub validate_protocol_section {
+
+    my ( $testarray, $error_fh ) = @_;
+
+    my $matcharray = [
+        $EDF_PROTOCOLACCESSION, $EDF_PROTOCOLNAME, $EDF_PROTOCOLTYPE,
+        $EDF_PROTOCOLTEXT,      $EDF_PROTOCOLPARAMS,
+    ];
+
+    my $error |= _check_section(
+	$testarray,
+	$matcharray,
+	'Protocol',
+	$error_fh,
+    );
+
+    return $error;
+
+}
+
+sub validate_hybridization_section {
+
+    my ( $testarray, $error_fh ) = @_;
+
+    my $matcharray = [
+        $EDF_BIOSOURCENAME,
+        $EDF_BIOSOURCETYPE,
+        $EDF_BIOSOURCEDESCRIPTION,
+        $EDF_BIOSAMPLENAME,
+        $EDF_BIOSAMPLETYPE,
+        $EDF_EXTRACTNAME,
+        $EDF_EXTRACTTYPE,
+        $EDF_IMMUNOPRECIPITATENAME,
+        $EDF_IMMUNOPRECIPITATETYPE,
+        $EDF_LABELEDEXTRACTNAME,
+        $EDF_LABELEDEXTRACTTYPE,
+        $EDF_HYBRIDIZATIONNAME,
+        $EDF_SCANNAME,
+        $EDF_NORMALIZATIONNAME,
+        $EDF_NORMALIZATIONTYPE,
+        $EDF_TRANSFORMATIONNAME,
+        $EDF_TRANSFORMATIONTYPE,
+        $EDF_DYE,
+
+        $EDF_GROW_PROTOCOL,
+        $EDF_TREAT_PROTOCOL,
+        $EDF_EXTRACT_PROTOCOL,
+        $EDF_POOL_PROTOCOL,
+        $EDF_LABEL_PROTOCOL,
+        $EDF_IP_PROTOCOL,
+        $EDF_HYB_PROTOCOL,
+        $EDF_SCAN_PROTOCOL,
+        $EDF_FEXT_PROTOCOL,
+        $EDF_NORM_PROTOCOL,
+        $EDF_TRXN_PROTOCOL,
+
+        $EDF_SCAN_SOFTWARE,
+        $EDF_FEXT_SOFTWARE,
+        $EDF_NORM_SOFTWARE,
+        $EDF_TRXN_SOFTWARE,
+
+        $EDF_IMAGE_FORMAT,
+
+        $EDF_ARRAYACCESSION,
+        $EDF_ARRAYSERIAL,
+
+        "$EDF_FILE_PREFIX.*$EDF_FILE_SUFFIX",
+        "$EDF_BMC_PREFIX.*$EDF_BMC_SUFFIX",
+        "$EDF_FV_PREFIX.*$EDF_FV_SUFFIX",
+        "$EDF_PARAM_PREFIX.*$EDF_PARAM_SUFFIX",
+    ];
+
+    my $error |= _check_section(
+	$testarray,
+	$matcharray,
+	'Hybridization',
+	$error_fh,
+    );
+
+    return $error;
+
+}
+
+=pod
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2004.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+1;
diff --git a/lib/ArrayExpress/Curator/MIAMExpress.pm b/lib/ArrayExpress/Curator/MIAMExpress.pm
new file mode 100644
index 0000000..43e9157
--- /dev/null
+++ b/lib/ArrayExpress/Curator/MIAMExpress.pm
@@ -0,0 +1,2509 @@
+#!/usr/bin/env perl
+#
+# MIAMExpress.pm - a module derived from and used in the experiment
+# checker script. Contains routines which might be useful elsewhere.
+#
+# Tim Rayner 2004 ArrayExpress team, EBI
+#
+# $Id: MIAMExpress.pm 2069 2008-06-04 14:33:52Z tfrayner $
+#
+
+package ArrayExpress::Curator::MIAMExpress;
+
+use strict;
+use warnings;
+
+require DBI;
+require DBD::mysql;
+use Tie::IxHash;
+use Carp;
+use IO::File;
+use Class::Std;
+use Readonly;
+use English qw( -no_match_vars );
+
+use ArrayExpress::Curator::Entrez_list qw(
+    parse_entrez_names
+);
+
+use ArrayExpress::Curator::Report qw(
+    datafile_consistency_table
+    biomaterials_report
+    format_description
+);
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+use ArrayExpress::Curator::Database qw(
+    parse_adf
+    get_ae_array_details
+);
+
+require ArrayExpress::Datafile;
+
+use base 'ArrayExpress::Curator::ExperimentChecker';
+
+my %mx_login             : ATTR( :get<mx_login>,                :init_arg<mx_login>,                :default<undef> );
+my %mx_title             : ATTR( :get<mx_title>,                :init_arg<mx_title>,                :default<undef> );
+my %tsubmis_sysuid       : ATTR( :default<undef> );
+my %submission_filebase  : ATTR( :default<undef> );
+my %mx_dbh               : ATTR( :default<undef> );
+
+# Map each of the know experimental factors (TCTLVCB_DESCR values) to
+# the relevant column(s) in the TSAMPLE table. This is used in
+# reporting both fv->hyb relationships and the full sample annotation
+# report.
+Readonly my %CATEGORY_SQL_MAP => (
+
+    # These are the core factors currently supported by MX.
+    age => [
+        qw(TSAMPLE_AGERANGE_MIN TSAMPLE_AGERANGE_MAX TSAMPLE_TIME_UNIT TSAMPLE_TIME_POINT)
+    ],
+    compound            => [],
+    disease_state       => [qw(TSAMPLE_DISEASE_STATE)],
+    cell_line           => [qw(TSAMPLE_CELL_LINE)],
+    cell_type           => [qw(TSAMPLE_TARGET_CELL_TYPE)],
+    developmental_stage => [qw(TSAMPLE_DEV_STAGE)],
+    dose                => [],
+    genotype            => [qw(TSAMPLE_INDIVIDUAL_GEN)],
+    organism_part       => [qw(TSAMPLE_ORGANISM_PART)],
+    sex                 => [qw(TSAMPLE_SEX)],
+    organism            => [qw(TSAMPLE_TAXID)],
+    strain_or_line      => [qw(TSAMPLE_STRAIN)],
+    temperature         => [],
+    time                => [],
+    other               => [],
+    clinical_information => [qw(TSAMPLE_ADDITIONAL)],
+    individual           => [qw(TSAMPLE_INDIVIDUAL)],
+    irradiate            => [],
+    'ChiP-antibody'      => [],    # typo in MIAMExpress;
+    'ChIP-antibody'      => [],    # the correct version, in case they ever fix it.
+
+    # The following are not currently factors, the keys may therefore change.
+    provider             => [qw(TSAMPLE_CELL_PROVIDER)],
+    type                 => [qw(TSAMPLE_SAMPLE_TYPE)],
+    genetic_modification => [qw(TSAMPLE_GENETIC_VARIATION)],
+    separation_technique => [qw(TSAMPLE_SEPARATION_TECH)],
+);
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    unless ( $mx_login{$id} && $mx_title{$id} ) {
+	croak("Error: Both mx_title and mx_login must be set for MIAMExpress checking.");
+    }
+
+    # Set the locations for the logfiles. N.B. get_tsubmis_sysuid()
+    # connects to the MX database to retrieve TSUBMIS_SYSUID here.
+    my $logfile_str = sprintf(
+	"%s_sub%s",
+	$self->get_mx_login(),
+	$self->get_tsubmis_sysuid(),
+    );
+    $self->localize_logfiles({
+	name      => $logfile_str,
+	directory => $self->get_submission_filebase(),
+    });
+
+    return;
+}
+
+sub get_files_and_annotation : RESTRICTED {
+
+    my ( $self ) = @_;
+
+    # Quick check to see what we've been passed.
+    unless ( $self->get_mx_login() && $self->get_mx_title() ) {
+        croak(<<'END_ERROR');
+Error: MIAMExpress checker must be passed both "mx_login" and "mx_title" options.
+END_ERROR
+    }
+
+    my $filelist;
+    my $hyb_ids;         # Used to map FGEM column headings to hybs.
+    my $norm_ids = {};   # Empty for MX; cannot map MX FGEM using Norm IDs.
+
+    print STDOUT ("Running in MIAMExpress mode...\n");
+
+    ( $filelist, $hyb_ids ) = $self->retrieve_MX_experiment();
+
+    return ( $filelist, $hyb_ids, $norm_ids );
+}
+
+sub get_mx_dbh : PRIVATE {
+
+    my ( $self ) = @_;
+
+    # Set up the DB connection, unless it's already been done.
+    unless ( $mx_dbh{ident $self} ) {
+
+	unless ( $CONFIG->get_MX_DSN() ) {
+	    croak(
+		"Error: MIAMExpress connection parameters"
+	      . " not correctly set in site config YAML file.\n"
+	    );
+	}
+
+	print STDOUT ("Connecting to MIAMExpress database...\n");
+	my $dbh = DBI->connect(
+	    $CONFIG->get_MX_DSN(),      $CONFIG->get_MX_USERNAME(),
+	    $CONFIG->get_MX_PASSWORD(), $CONFIG->get_MX_DBPARAMS()
+	)
+	    or croak("$DBI::errstr\n");
+
+	$mx_dbh{ident $self} = $dbh;
+    }
+
+    return $mx_dbh{ident $self};
+}
+
+sub get_tsubmis_sysuid {
+
+    my ( $self ) = @_;
+
+    unless ( defined $tsubmis_sysuid{ident $self} ) {
+	my ( $sysuid, $filebase ) = $self->query_mx_for_subs_info();
+	$tsubmis_sysuid{ident $self} = $sysuid;
+	$submission_filebase{ident $self} = $filebase;
+    }
+
+    return $tsubmis_sysuid{ident $self};
+}
+
+sub get_submission_filebase {
+
+    my ( $self ) = @_;
+
+    unless ( defined $submission_filebase{ident $self} ) {
+	my ( $sysuid, $filebase ) = $self->query_mx_for_subs_info();
+	$tsubmis_sysuid{ident $self} = $sysuid;
+	$submission_filebase{ident $self} = $filebase;
+    }
+
+    return $submission_filebase{ident $self};
+}
+
+sub _get_db_adf_features : PRIVATE {
+
+    my ( $self, $hyb_ids ) = @_;
+
+    # Connect to MX.
+    my $dbh = $self->get_mx_dbh();
+
+    # First, get a list of ARRAY_DESIGNIDs
+    my %array_designs;
+    foreach my $hyb_sysuid ( values %$hyb_ids ) {
+        my $design_id = $self->_get_array_designid( $hyb_sysuid );
+
+        # This rationalises the list so we only parse each ADF once
+        $array_designs{$design_id}++;
+    }
+
+    # Next, process that list.  Table retrieved from ArrayExpress, but
+    # only if we need to:
+    foreach my $design_id ( keys %array_designs ) {
+
+        if ( $design_id > 0 ) {
+
+            # ADF comes from MIAMExpress
+            my ( $adf_filename, $arrayname )
+                = $self->_get_adf_filename( $design_id );
+
+            unless ( $adf_filename && $arrayname ) {
+                $self->logprint( 'error',
+                    "DATABASE ERROR: Incomplete data returned by MIAMExpress for array design id $design_id.\n"
+                );
+
+                # This is an unlikely error; if this is tripped who knows how
+                # much else is broken?
+                $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+            }
+            else {
+                $self->logprint( 'error',
+                    "Warning: Using ADF from MIAMExpress: $arrayname. Array not yet loaded and public in ArrayExpress.\n"
+                );
+
+                # Loading cannot occur if the array is not loaded.
+                $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+            }
+
+	    if ( $self->get_skip_data_checks() ) {
+		print STDOUT ("Skipping ADF parsing.\n");
+	    }
+	    else {
+		my $db_adf_fh = IO::File->new( $adf_filename, '<' )
+		    or croak("Error: Unable to open $adf_filename: $!\n");
+		my $array = parse_adf(
+		    $db_adf_fh,
+		    undef,
+		    $self->get_reporter_prefix(),
+		    $self->get_compseq_prefix(),
+		);
+		close($db_adf_fh);
+		$self->set_cached_array_design( $design_id, $array );
+	    }
+        }
+
+        elsif ( $design_id < 0 ) {
+
+            # ADF comes from ArrayExpress.
+
+            # We actually want a positive database id.
+            my $arrayexpress_id = -$design_id;
+
+	    $self->get_ae_arraydesign({
+		database_id => $arrayexpress_id,
+	    });
+        }
+    }
+}
+
+sub _MX_populate_array_designs : PRIVATE {
+
+    my ( $self, $filelist, $hyb_ids ) = @_;
+
+    # Connect to MX.
+    my $dbh = $self->get_mx_dbh();
+
+    if ( $self->get_adf_filename || $self->get_array_accession ) {
+
+        $self->cache_user_supplied_arrays( $filelist );
+
+    }
+    else {
+
+        # Go to the database(s) and get as many ADFs as are linked to
+        # by the hybridizations in MIAMExpress.
+
+        # Get the associated ADF features from the database.  These
+        # are stored in an ExperimentChecker method wrapping Storable
+        # caches on disk.
+
+	$self->_get_db_adf_features( $hyb_ids );
+
+    }
+
+    $self->populate_file_arraydesigns( $filelist );
+
+    return;
+}
+
+sub _check_db_publication : PRIVATE {
+
+    # Checks the associated publication details,prints warnings and/or
+    # errors to error log.
+
+    my ( $self ) = @_;
+
+    # Connect to MX.
+    my $dbh = $self->get_mx_dbh();
+
+    my $sth = $dbh->prepare(<<'QUERY');
+select distinct TCTLVCB_VALUE, TPUBLIC_SYSUID from TPUBLIC, TCTLVCB
+where TPUBLIC_SUBID=?
+and TPUBLIC_PUBLICATION=TCTLVCB_SYSUID
+and TPUBLIC_DEL_STATUS='U' and TCTLVCB_DEL_STATUS='U'
+QUERY
+
+    $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+    my %publications;
+    while ( my $rowref = $sth->fetchrow_arrayref ) {
+
+        # publication sysuid => publication name
+        $publications{ $rowref->[1] } = $rowref->[0];
+    }
+
+    # dump the statement handler now that we are finished with it
+    $sth->finish();
+
+    # Check through for others, get the appropriate name from TOTHERS
+    # and replace the value in the hash
+    foreach my $pub_sysuid ( keys %publications ) {
+
+        if ( $publications{$pub_sysuid} =~ m/other/i ) {
+
+            $self->logprint(
+		'error',
+                qq{Warning: "Other" publication specified in submission.\n},
+	    );
+            $self->add_error($CONFIG->get_ERROR_INNOCENT());
+
+            my $sth = $dbh->prepare(<<'QUERY');
+select distinct TOTHERS_VALUE from TOTHERS
+where TOTHERS_PUBLICID=?
+and TOTHERS_DEL_STATUS='U'
+QUERY
+            $sth->execute($pub_sysuid) or croak($sth->errstr);
+
+            my $rowref = $sth->fetchrow_arrayref;
+            $publications{$pub_sysuid} = $rowref->[0];
+
+            # dump the statement handler now that we are finished with it
+            $sth->finish();
+        }
+    }
+
+    # Check through the publication names
+    my $entrez_approved = parse_entrez_names();
+    foreach my $name ( values %publications ) {
+        unless ( $entrez_approved->{$name} ) {
+            $self->logprint(
+		'error',
+		"Warning: Non-standard publication name: $name\n",
+	    );
+            $self->add_error($CONFIG->get_ERROR_INNOCENT());
+        }
+    }
+
+    return;
+}
+
+sub retrieve_MX_experiment : PRIVATE {
+
+    my ( $self ) = @_;
+
+    ###########################
+    # Connect to the database #
+    ###########################
+
+    print STDOUT ("Querying MIAMExpress for submission data... \n");
+
+    # Connect to MX.
+    my $dbh = $self->get_mx_dbh();
+
+    ##########################################
+    # Initialise top level dir and log files #
+    ##########################################
+
+    $self->logprint( 'error', "* MIAMExpress mode *\n\n" );
+    $self->logprint_line( 'error', 'DB query START' );
+
+    #######################
+    # Biomaterials report #
+    #######################
+
+    $self->_check_db_publication();
+
+    print STDOUT ("Writing BioMaterials report file... ");
+
+    # Get the associations between sample, extract, labeled extract, and
+    # hybs as a heirarchical hashref.
+    my ( $samples_to_hybs, $protocol_typemap, $protocol_others, $rc )
+        = $self->_get_db_objects();
+    $self->add_error($rc);
+
+    # Count the samples, extracts, LEs and hybs. FIXME to deal with NULLs
+    my %bm_summary;
+
+    # Sample
+    @{ $bm_summary{samples} }{ keys %$samples_to_hybs }
+        = values %$samples_to_hybs;
+    foreach my $sample ( values %$samples_to_hybs ) {
+
+        # Extract
+        @{ $bm_summary{extracts} }{ keys %$sample } = values %$sample;
+        foreach my $extract ( values %$sample ) {
+
+            # Label
+            @{ $bm_summary{labels} }{ keys %$extract } = values %$extract;
+            foreach my $label ( values %$extract ) {
+
+                # Hyb
+                @{ $bm_summary{hybs} }{ keys %$label } = values %$label;
+            }
+        }
+    }
+
+    # Count the contants of the hashrefs, print out the results.
+    my %bm_count;
+    foreach my $bm ( keys %bm_summary ) {
+        $bm_count{$bm} = scalar( grep { defined $_ } values %{ $bm_summary{$bm} } );
+    }
+
+    $self->logprint(
+        'workflow',
+        "$bm_count{samples} samples -> ",
+        "$bm_count{extracts} extracts -> ",
+        "$bm_count{labels} labeled extracts -> ",
+        "$bm_count{hybs} hybridizations\n"
+    );
+
+    # Print out the trees, forward and reverse:
+    biomaterials_report(
+        $samples_to_hybs,
+        'Samples to Hybridizations',
+        $self->log_fh('workflow')
+    );
+    my $hybs_to_samples = $self->_invert_heirarchy($samples_to_hybs);
+    biomaterials_report(
+        $hybs_to_samples,
+        'Hybridizations to Samples',
+        $self->log_fh('workflow')
+    );
+
+    # Get relationships between FactorValues, Hybs and data files.
+    # $hyb_factors is hyb_id => {fv_string1 => 1, etc.} for reporting;
+    # $hyb_category_fvs is hyb_id => {category1 => {value_string1 => 1} }
+    # for mapping files to FVs.
+    my $hyb_category_fvs;
+    if ( $CONFIG->get_MX_EXTENDED_REPORT() ) {
+	my $hyb_factors;
+	( $hyb_factors, $hyb_category_fvs ) = $self->_get_db_factor_assns();
+	$self->add_error($rc);
+	biomaterials_report(
+	    $hyb_factors,
+	    'Hybridizations to FactorValues',
+	    $self->log_fh('workflow')
+	);
+	my $reversed_factors = $self->_invert_heirarchy($hyb_factors);
+	biomaterials_report(
+	    $reversed_factors,
+	    'FactorValues to Hybridizations',
+	    $self->log_fh('workflow')
+	);
+    }
+
+    print STDOUT ("done.\n");
+
+    if ( $CONFIG->get_MX_EXTENDED_REPORT() ) {
+
+	print STDOUT ("Generating sample annotation report... ");
+
+	$self->_get_db_sample_annotation();
+
+	print STDOUT ("done.\n");
+    }
+
+    # Collect the design type info for later.
+    $self->add_expt_designs(
+	@{ $self->_get_db_expt_designs() }
+    );
+
+    #############################
+    # Get data file information #
+    #############################
+
+    # Get a list of files in the database submitted via MIAMExpress for
+    # this submission.  This is an array of
+    # ArrayExpress::Datafile objects.
+    my $filelist = $self->get_db_files();
+
+    # Add factor information to the datafile objects (raw and norm only).
+    if ( $hyb_category_fvs ) {
+
+	FILE:
+	foreach my $file (@$filelist) {
+
+	    # Skip FGEM - they have no hyb_identifier.
+	    next FILE
+		if ( $file->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE() );
+
+	    # We've mapped the FVs to HYB_ID, not HYB_SYSUID.
+	    my $hyb_id = $file->get_hyb_identifier();
+
+	    while ( my ($category, $values)
+			= each %{$hyb_category_fvs->{$hyb_id}} ) {
+
+		foreach my $value_string (keys %$values) {
+		    $file->add_factor_value( $category, $value_string );
+		}
+	    }
+	}
+    }
+
+    # get a list of hyb_ids pointing to a list of hyb_sysuids
+    my $hyb_ids = $self->_get_hyb_ids();
+
+    # Check for Qualifier Value Source entries
+    my $qvs = $self->_get_db_qvs();
+    if (@$qvs) {
+        $self->logprint( 'error', "Warning: QVS used in annotation:\n" );
+        $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+        foreach my $entry (@$qvs) {
+
+            if ( $entry->{sample_id} ) {    # Sample QVS
+                $self->logprint(
+                    'error',
+                    sprintf(
+                        "Sample qualifier      Name: %-22s Value: %s\n",
+                        $entry->{qualifier}, $entry->{value}
+                    )
+                );
+
+            }
+            else {                          # Experiment QVS
+                $self->logprint(
+                    'error',
+                    sprintf(
+                        "Experiment qualifier  Name: %-22s Value: %s\n",
+                        $entry->{qualifier}, $entry->{value}
+                    )
+                );
+            }
+        }
+    }
+
+    # Check for "Other" values
+    my $others = $self->_get_db_others();
+
+    # Skip empty values for deleted OTHERS - this seems to be a bug
+    # in MX (DEL_STATUS not set).
+    my @cleaned_others;
+    foreach my $other (@$others) {
+        if ( $other->{value} ) {
+            push( @cleaned_others, $other );
+        }
+    }
+
+    if (@cleaned_others) {
+        $self->logprint( 'error',
+            qq{Warning: "Other" values used in annotation:\n} );
+
+        OTHER_ENTRY:
+        foreach my $entry (@cleaned_others) {
+
+            if ( $entry->{sample_id} ) {    # Sample OTHER
+                $self->logprint(
+                    'error',
+                    sprintf(
+                        "Sample value      Type: %-22s Value: %s\n",
+                        $entry->{id}, $entry->{value}
+                    )
+                );
+
+            }
+            else {                          # Experiment OTHER
+                $self->logprint(
+                    'error',
+                    sprintf(
+                        "Experiment value  Type: %-22s Value: %s\n",
+                        $entry->{id}, $entry->{value}
+                    )
+                );
+            }
+            $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+        }
+    }
+
+    # Nested hash: {protocol_type => {protocol_sysuid => {others_id =>
+    #                                                        others_value}}}
+    while ( my ( $ptype, $others ) = each %$protocol_others ) {
+        foreach my $protocol ( values %$others ) {
+
+            OTHER_PROTOCOL:
+            while ( my ( $key, $value ) = each %$protocol ) {
+
+                # Skip empty values for deleted OTHERS - this seems to be a
+                # bug in MX (DEL_STATUS not set).
+                next OTHER_PROTOCOL unless ($value);
+
+                $self->logprint( 'error',
+                    qq{Warning: "Other" values used in $ptype protocol: $key => $value\n}
+                );
+                $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+            }
+        }
+    }
+
+    $self->logprint_line( 'error', 'DB query END' );
+
+    $self->logprint_line( 'error', 'Protocol check START' );
+
+    ##################
+    # Protocol check #
+    ##################
+
+    print STDOUT ("Generating protocol report... ");
+
+    # Count the number of combined (transformed) and normalized data files
+    my ( $combined_count, $normalized_count );
+    foreach my $file (@$filelist) {
+        $combined_count++
+            if ( $file->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE() );
+
+        $normalized_count++
+	    if ( $file->get_data_type() eq 'normalized' );
+    }
+
+    # Then we check the protocols reported by &_get_db_objects
+    my (%protocol_text, $norm_protocol_found);
+    foreach my $ptype ( keys %$protocol_typemap ) {
+
+        PROTOCOL_BY_TYPE:
+        foreach my $obj_id ( keys %{ $protocol_typemap->{$ptype} } ) {
+
+            # Count the protocols associated with a given object id
+            my $pcount = scalar @{ $protocol_typemap->{$ptype}{$obj_id} };
+
+            # Check for multiple protocols associated with an object
+            # (shouldn't happen).
+            if ( $pcount > 1 ) {
+                $self->logprint( 'error',
+                    "Warning: Multiple protocols of type \'$ptype\' attached to object \'$obj_id\'\n"
+                );
+                $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+            }
+
+	    # Check protocol lengths, warn if too short (<50 chars at
+	    # the moment)
+	    PROTOCOL_LENGTH:
+            foreach my $protocol ( @{ $protocol_typemap->{$ptype}{$obj_id} } ) {
+
+		# GLOB indicates an AE public protocol (e.g. Affymetrix).
+		next PROTOCOL_LENGTH if defined($protocol->{glob});
+
+                if ( !defined($protocol->{text})
+			 || ( length($protocol->{text}) < 50 ) ) {
+                    $self->logprint( 'error',
+                        qq{Warning: Description too short for $ptype protocol attached to '$obj_id'\n}
+                    );
+                    $self->add_error( $CONFIG->get_ERROR_MIAME() );
+                }
+
+		# Note the text for reporting below.
+		$protocol_text{$ptype}{$protocol->{id}} = $protocol;
+            }
+
+            # Check for pooling protocols
+            if ( $ptype eq 'pooling' ) {
+                if ( $pcount > 0 ) {
+                    $self->logprint( 'error',
+                        qq{Warning: Pooling protocol attached to extract '$obj_id'\n}
+                    );
+                    $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+                }
+                next PROTOCOL_BY_TYPE;
+            }
+
+            # Check for normalization protocols
+            if ( $ptype eq 'normalization' ) {
+		$norm_protocol_found = 1 if $pcount;
+                if ( $normalized_count && ( $pcount <= 0 ) ) {
+                    $self->logprint( 'error',
+                        qq{Warning: Normalized data for hybridization '$obj_id'}
+		      . qq{ supplied without normalization protocol.\n}
+                    );
+                    $self->add_error( $CONFIG->get_ERROR_MIAME() );
+                }
+                next PROTOCOL_BY_TYPE;
+            }
+
+            # Check for transformation protocols
+            if ( $ptype eq 'transformation' ) {
+		$norm_protocol_found = 1 if $pcount;
+                if ( $combined_count && ( $pcount <= 0 ) ) {
+                    $self->logprint( 'error',
+                        qq{Warning: Final data matrix file supplied}
+    	              . qq{ without transformation protocol.\n}
+                    );
+                    $self->add_error( $CONFIG->get_ERROR_MIAME() );
+                }
+                next PROTOCOL_BY_TYPE;
+            }
+
+            # Check for unbound protocols (pooling, normalization omitted at
+            # this stage).
+            if ( $pcount <= 0 ) {
+                $self->logprint( 'error',
+                    "Warning: Object \'$obj_id\' not bound to $ptype protocol.\n" );
+                $self->add_error( $CONFIG->get_ERROR_MIAME() );
+            }
+        }
+    }
+
+    # Note the results for MIAME compliance tracking.
+    if ($norm_protocol_found) {
+	$self->add_miame($CONFIG->get_MIAME_NORMPROTOCOL);
+    }
+    else {
+	$self->logprint(
+	    'miame',
+	    "Problem: Data transformation protocol not found.\n"
+	);
+    }
+
+    foreach my $ptype qw(
+			 growth
+			 treatment
+			 pooling
+			 extraction
+			 labeling
+			 hybridization
+			 scanning
+			 normalization
+			 transformation
+		     ) {
+	foreach my $protocol ( values %{$protocol_text{$ptype}} ) {
+	    $self->logprint_line(
+		'protocol',
+		"[$ptype protocol] $protocol->{name}");
+	    $self->logprint('protocol', "$protocol->{text}\n\n");
+	}
+    }
+    $self->logprint_line( 'error', 'Protocol check END' );
+    print STDOUT ("done.\n");
+
+    # Start the report log.
+    $self->logprint( 'report', "* MIAMExpress mode *\n\n" );
+    $self->logprint(
+        'report', "  User login: ",
+        $self->get_mx_login, "\n", "  Experiment: ",
+        $self->get_mx_title, "\n\n"
+    );
+
+    $self->_report_description();
+
+    ######################################
+    # Deal with Array design information #
+    ######################################
+
+    # Get the associated array designs on a per-hyb basis
+
+    $self->logprint_line( 'error', 'ADF parsing START' );
+
+    # Populate the $file->get_adf_features() hashrefs within @$filelist
+    $self->_MX_populate_array_designs( $filelist, $hyb_ids );
+
+    $self->logprint_line( 'error', 'ADF parsing END' );
+
+    # Data file check on consistency across hybs
+    $self->logprint( 'report',
+        "\nChecking for presence of raw and/or normalised data files:\n" );
+    $self->logprint( 'report', datafile_consistency_table($filelist) );
+
+    print STDOUT ("done.\n");
+
+    # We don't disconnect from the database, in case we're operating
+    # over a batch of experiments to be checked. The autosubs daemons
+    # fork before connecting, so we shouldn't have persistent
+    # connections to the database.
+    return ( $filelist, $hyb_ids );
+
+}
+
+sub query_mx_for_subs_info : PRIVATE {
+
+    my ( $self ) = @_;
+
+    my $login = $self->get_mx_login() or croak("Error: mx_login not set.");
+    my $title = $self->get_mx_title() or croak("Error: mx_title not set.");
+
+    # Connect to MX.
+    my $dbh = $self->get_mx_dbh();
+
+    # Retrieve data from the table.
+    my $sth = $dbh->prepare(<<'QUERY');
+select distinct TSUBMIS_SYSUID from TSUBMIS where TSUBMIS_LOGIN=?
+and TSUBMIS_SUB_DESCR=? and TSUBMIS_DEL_STATUS='U'
+QUERY
+    $sth->execute( $login, $title ) or croak($sth->errstr);
+
+    # Get all the TSUBMIS_UID values returned as an array of arrays.
+    # (arrayref=[\@row1 \@row2 \@row3] where e.g. @row1=($col1,$col2))
+    my $arrayref = $sth->fetchall_arrayref;
+
+    # Count up the @rows.
+    my $rowcount = scalar @$arrayref;
+
+    # Check that we have one and only one row returned, assign the first
+    # and only column value (TSUBMIS_SYSUID) to $tsubmis_sysuid.
+    my $tsubmis_sysuid;
+    if ( $rowcount == 1 ) {    # all is well
+        $tsubmis_sysuid = $arrayref->[0][0];
+
+    }
+    elsif ( $rowcount == 0 ) {
+
+        # Possible typo in login/title arguments
+        croak(
+            "Error: Login name \'$login\' with experiment title \'$title\' not found in MIAMExpress database.\n"
+        );
+    }
+    else {
+
+        # This should never happen
+        croak(
+            "ERROR: Login name \'$login\' with Experiment title \'$title\' has multiple MIAMExpress database entries.\n"
+        );
+    }
+
+    # dump the statement handler now that we are finished with it
+    $sth->finish();
+
+    # Get the experiment filebase, while we have a connection.
+    $sth = $dbh->prepare(<<'QUERY');
+select distinct TSYSOPT_VALUE from TSYSOPT
+where TSYSOPT_CODE='experiment_datafiles_path'
+QUERY
+
+    $sth->execute() or die($sth->errstr);
+
+    # FIXME we're assuming the query worked here.
+    my $MX_submissions_filebase = File::Spec->catdir(
+	$sth->fetchrow_arrayref()->[0], $login, "submission$tsubmis_sysuid",
+    );
+
+    return ($tsubmis_sysuid, $MX_submissions_filebase);
+}
+    
+sub get_db_files {
+    my ( $self ) = @_;
+
+    # Connect to MX.
+    my $dbh = $self->get_mx_dbh();
+
+    # Retrieve data from the table.
+
+    my $FGEM_ardesin;
+
+    # Get Raw data filenames associated directly with hybs
+    my $sth = $dbh->prepare(<<'QUERY');
+select distinct TARRAY_DESIGNID,THYBRID_SYSUID, THYBRID_ID, TFILE_PATH
+from TFILE,THYBRID,TLABHYB,TARRAY
+where TLABHYB_HYBRIDID=THYBRID_SYSUID and TFILE_HYBRIDID=THYBRID_SYSUID
+and THYBRID_ARRAYID=TARRAY_SYSUID and TLABHYB_SUBID=?
+and TLABHYB_DEL_STATUS='U' and THYBRID_DEL_STATUS='U'
+and TFILE_DEL_STATUS='U' and TARRAY_DEL_STATUS='U'
+QUERY
+    $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+    # Get the relevant stuff out of $sth.
+    # Array of file hashes of attributes:
+    my @file_list;
+
+    # Hash of hyb_sysuids pointing to {raw} or {normalized} arrays of
+    # filenames:
+    my %hyb_list;
+
+    # Array of FGEM filenames:
+    my @FGEM_list;
+
+    while ( my $rowref = $sth->fetchrow_hashref ) {
+
+	my $path = File::Spec->catfile(
+	    $self->get_submission_filebase(),
+	    "hybrid$rowref->{'THYBRID_SYSUID'}",
+	    $rowref->{'TFILE_PATH'}
+	);
+
+	# If we can, map the design ID to an accession.
+	my $design_id = $rowref->{'TARRAY_DESIGNID'};
+        if ( $design_id < 0 ) {
+	    my $result = get_ae_array_details({database_id => -$design_id});
+	    $design_id = $result if $result;
+	}
+
+        my $file = ArrayExpress::Datafile->new({
+	    path            => $path,
+	    name            => $rowref->{'TFILE_PATH'},
+	    data_type       => 'raw',
+	    hyb_identifier  => $rowref->{'THYBRID_ID'},
+	    hyb_sysuid      => $rowref->{'THYBRID_SYSUID'},
+	    array_design_id => $design_id,
+	    is_miamexpress  => 1,
+	});
+
+        push( @file_list, $file );
+        push(
+            @{ $hyb_list{ $rowref->{'THYBRID_SYSUID'} }{raw} },
+            $file->get_path()
+        );
+        $FGEM_ardesin ||= $design_id;
+    }
+
+    # dump the statement handler now that we are finished with it
+    $sth->finish();
+
+    # Get the normalized data file names
+    $sth = $dbh->prepare(<<'QUERY');
+select distinct TARRAY_DESIGNID,THYBRID_SYSUID,THYBRID_ID,TFILE_PATH
+from TFILE,THYBRID,TLABHYB,TARRAY
+where THYBRID_SYSUID=TLABHYB_HYBRIDID and THYBRID_SYSUID=TFILE_TRANSHYBRIDID
+and TARRAY_SYSUID=THYBRID_ARRAYID and TLABHYB_SUBID=?
+and TLABHYB_DEL_STATUS='U' and THYBRID_DEL_STATUS='U'
+and TFILE_DEL_STATUS='U' and TARRAY_DEL_STATUS='U'
+QUERY
+    $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+    # get the relevant stuff out of $sth
+    while ( my $rowref = $sth->fetchrow_hashref ) {
+
+	my $path = File::Spec->catfile(
+	    $self->get_submission_filebase(),
+	    "hybrid$rowref->{'THYBRID_SYSUID'}",
+	    $rowref->{'TFILE_PATH'}
+        );
+
+	# If we can, map the design ID to an accession.
+	my $design_id = $rowref->{'TARRAY_DESIGNID'};
+        if ( $design_id < 0 ) {
+	    my $result = get_ae_array_details({database_id => -$design_id});
+	    $design_id = $result if $result;
+	}
+
+        my $file = ArrayExpress::Datafile->new({
+	    path            => $path,
+	    name            => $rowref->{'TFILE_PATH'},
+	    data_type       => 'normalized',
+	    hyb_identifier  => $rowref->{'THYBRID_ID'},
+	    hyb_sysuid      => $rowref->{'THYBRID_SYSUID'},
+	    array_design_id => $design_id,
+	    is_miamexpress  => 1,
+	});
+
+        push( @file_list, $file );
+        push(
+            @{ $hyb_list{ $rowref->{'THYBRID_SYSUID'} }{normalized} },
+            $file->get_path()
+        );
+        $FGEM_ardesin ||= $design_id;
+    }
+
+    # dump the statement handler now that we are finished with it
+    $sth->finish();
+
+    # Deal with FGEM files here
+    $sth = $dbh->prepare(<<'QUERY');
+select distinct TFILE_PATH from TFILE,TEXPRFNL
+where TFILE_EXPRFNLID=TEXPRFNL_SYSUID and TEXPRFNL_SUBID=?
+and TFILE_DEL_STATUS='U'
+QUERY
+    $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+    while ( my $rowref = $sth->fetchrow_hashref ) {
+
+	my $path = File::Spec->catfile(
+	    $self->get_submission_filebase(),
+	    $rowref->{'TFILE_PATH'},
+	);
+	
+        # array_design_id here is a placeholder; MX does not associate
+        # FGEM with ARDESIN. We default to the first raw data ARDESIN
+        # here.
+        my $file = ArrayExpress::Datafile->new({
+	    path            => $path,
+	    name            => $rowref->{'TFILE_PATH'},
+	    data_type       => $CONFIG->get_FGEM_FILE_TYPE(),
+	    is_miamexpress  => 1,
+	    array_design_id => ( $FGEM_ardesin || 'UNKNOWN' ),
+	});
+
+        push( @file_list, $file );
+        push( @FGEM_list, $file->get_path() );
+    }
+
+    # dump the statement handler now that we are finished with it
+    $sth->finish();
+
+    return wantarray
+	? ( \@file_list, \%hyb_list, \@FGEM_list )
+	: \@file_list;
+
+}
+
+sub _get_db_expt_designs : PRIVATE {
+
+    my ( $self ) = @_;
+
+    # Connect to MX.
+    my $dbh = $self->get_mx_dbh();
+
+    my $sth = $dbh->prepare(<<'QUERY');
+select distinct TCTLVCB_DESCR from TCTLVCB, TEXPRTYP, TEXPRMNT
+where TEXPRMNT_EXPRID=TEXPRTYP_EXPRID
+and TEXPRTYP_ID=TCTLVCB_SYSUID
+and TEXPRMNT_SUBID=?
+and TCTLVCB_DEL_STATUS='U' and TEXPRMNT_DEL_STATUS='U' and TEXPRTYP_DEL_STATUS='U'
+QUERY
+
+    $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+    my @design_types;
+    while ( my $termlist = $sth->fetchrow_arrayref() ) {
+	push(@design_types, $termlist->[0]);
+    }
+
+    return \@design_types;
+}
+
+sub _get_hyb_ids : PRIVATE {
+
+    # Returns a list of hyb ids and a second list of hyb sysuids
+    # associated with the submission.
+
+    my ( $self ) = @_;
+
+    # Connect to MX.
+    my $dbh = $self->get_mx_dbh();
+
+    my $sth = $dbh->prepare(<<'QUERY');
+select distinct THYBRID_ID,THYBRID_SYSUID from THYBRID,TLABHYB
+where THYBRID_SYSUID=TLABHYB_HYBRIDID and TLABHYB_SUBID=?
+and TLABHYB_DEL_STATUS='U' and THYBRID_DEL_STATUS='U'
+QUERY
+    $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+    my %hyb_list;
+
+    # get the relevant stuff out of $sth
+    while ( my $rowref = $sth->fetchrow_hashref ) {
+        my $hyb = $rowref->{'THYBRID_ID'};
+
+        # hash, {hyb_id => hyb_sysuid}
+        $hyb_list{$hyb} = $rowref->{'THYBRID_SYSUID'};
+    }
+
+    # dump the statement handler now that we are finished with it
+    $sth->finish();
+
+    return ( \%hyb_list );
+}
+
+sub _protocol_length_and_others : PRIVATE {
+
+    my ( $self, $obj_id_protocol_text, $get_ptext, $get_others )
+        = @_;
+
+    my %others;
+
+    foreach my $obj_id ( keys %$obj_id_protocol_text ) {
+
+        $get_ptext->execute( $self->get_tsubmis_sysuid(), $obj_id )
+            or croak($get_ptext->errstr);
+
+	# DBI row arrayref points at array with protocol text,
+	# protocol sysuid.
+        foreach my $rowref ( @{ $get_ptext->fetchall_arrayref } ) {
+
+	    # Add the protocol text to the %protocol_typemap leaf for
+	    # this object.
+            push(
+		@{ $obj_id_protocol_text->{$obj_id} },
+		{
+		    text => $rowref->[0],
+		    id   => $rowref->[1],
+		    name => $rowref->[2],
+		    glob => $rowref->[3],
+		},
+	     );
+
+	    # Use the protocol sysuid to pull out OTHERS info, while
+	    # we're here.
+            my $tprotcls_sysuid = $rowref->[1];
+            $get_others->execute($tprotcls_sysuid)
+                or croak($get_others->errstr);
+            while ( my $rowref = $get_others->fetchrow_hashref ) {
+                $rowref->{'TOTHERS_ID'} =~ s/_/ /g;
+                $others{$tprotcls_sysuid}{ lc( $rowref->{'TOTHERS_ID'} ) }
+                    = $rowref->{'TOTHERS_VALUE'};
+            }
+        }
+    }
+
+    return \%others;
+
+}
+
+sub _get_db_protocols : PRIVATE {
+
+    my ( $self, $protocol_typemap ) = @_;
+
+    # Connect to MX.
+    my $dbh = $self->get_mx_dbh();
+
+    my $protocol_others;
+
+    foreach my $type ( keys %$protocol_typemap ) {
+
+        my $get_ptext;
+
+        PROTOCOLTYPE:
+        {
+
+            ( $type eq 'transformation' ) && do {
+                $get_ptext = $dbh->prepare(<<'QUERY');
+select distinct TPROTCLS_DESCR, TPROTCLS_SYSUID, TPROTCLS_ID, TPROTCLS_GLOBID
+from TPROTCLS, TEXPRFNL, TFILE
+where TEXPRFNL_PROTOCOLID = TPROTCLS_SYSUID
+and TFILE_EXPRFNLID = TEXPRFNL_SYSUID
+and TEXPRFNL_SUBID=? and TFILE_PATH=?
+and (TPROTCLS_GLOBID is not NULL or (TPROTCLS_DESCR is not NULL and TPROTCLS_ID is not NULL))
+and TEXPRFNL_DEL_STATUS='U' and TPROTCLS_DEL_STATUS='U' and TFILE_DEL_STATUS='U'
+QUERY
+                last PROTOCOLTYPE;
+            };
+
+            ( $type eq 'normalization' ) && do {
+                $get_ptext = $dbh->prepare(<<'QUERY');
+select distinct TPROTCLS_DESCR, TPROTCLS_SYSUID, TPROTCLS_ID, TPROTCLS_GLOBID
+from THYBRID,TLABHYB,TPROTCLS
+where THYBRID_SYSUID=TLABHYB_HYBRIDID and THYBRID_NORM_PROTOCOLID = TPROTCLS_SYSUID
+and TLABHYB_SUBID=? and THYBRID_ID=?
+and (TPROTCLS_GLOBID is not NULL or (TPROTCLS_DESCR is not NULL and TPROTCLS_ID is not NULL))
+and TLABHYB_DEL_STATUS='U' and THYBRID_DEL_STATUS='U' and TPROTCLS_DEL_STATUS='U'
+QUERY
+                last PROTOCOLTYPE;
+            };
+
+            ( $type eq 'hybridization' ) && do {
+                $get_ptext = $dbh->prepare(<<'QUERY');
+select distinct TPROTCLS_DESCR, TPROTCLS_SYSUID, TPROTCLS_ID, TPROTCLS_GLOBID
+from THYBRID,TLABHYB,TPROTCLS
+where THYBRID_SYSUID=TLABHYB_HYBRIDID and THYBRID_PROTOCOLID = TPROTCLS_SYSUID
+and TLABHYB_SUBID=? and THYBRID_ID=?
+and (TPROTCLS_GLOBID is not NULL or (TPROTCLS_DESCR is not NULL and TPROTCLS_ID is not NULL))
+and TLABHYB_DEL_STATUS='U' and THYBRID_DEL_STATUS='U' and TPROTCLS_DEL_STATUS='U'
+QUERY
+                last PROTOCOLTYPE;
+            };
+
+            ( $type eq 'scanning' ) && do {
+                $get_ptext = $dbh->prepare(<<'QUERY');
+select distinct TPROTCLS_DESCR, TPROTCLS_SYSUID, TPROTCLS_ID, TPROTCLS_GLOBID
+from THYBRID,TLABHYB,TPROTCLS
+where THYBRID_SYSUID=TLABHYB_HYBRIDID and THYBRID_SCAN_PROTOCOLID = TPROTCLS_SYSUID
+and TLABHYB_SUBID=? and THYBRID_ID=?
+and (TPROTCLS_GLOBID is not NULL or (TPROTCLS_DESCR is not NULL and TPROTCLS_ID is not NULL))
+and TLABHYB_DEL_STATUS='U' and THYBRID_DEL_STATUS='U' and TPROTCLS_DEL_STATUS='U'
+QUERY
+                last PROTOCOLTYPE;
+            };
+
+            ( $type eq 'labeling' ) && do {
+                $get_ptext = $dbh->prepare(<<'QUERY');
+select distinct TPROTCLS_DESCR, TPROTCLS_SYSUID, TPROTCLS_ID, TPROTCLS_GLOBID
+from TLABEL,TEXTRACT,TPOOLED,TPROTCLS
+where TLABEL_PROTOCOLID=TPROTCLS_SYSUID and TEXTRACT_SYSUID=TLABEL_EXTRACTID 
+and TEXTRACT_SYSUID=TPOOLED_EXTRACTID and TPOOLED_SUBID=?
+and TLABEL_ID=? and TLABEL_DEL_STATUS='U' and TEXTRACT_DEL_STATUS='U'
+and (TPROTCLS_GLOBID is not NULL or (TPROTCLS_DESCR is not NULL and TPROTCLS_ID is not NULL))
+and TPOOLED_DEL_STATUS='U' and TPROTCLS_DEL_STATUS='U'
+QUERY
+                last PROTOCOLTYPE;
+            };
+
+            ( $type eq 'extraction' ) && do {
+                $get_ptext = $dbh->prepare(<<'QUERY');
+select distinct TPROTCLS_DESCR, TPROTCLS_SYSUID, TPROTCLS_ID, TPROTCLS_GLOBID
+from TEXTRACT,TPOOLED,TPROTCLS
+where TEXTRACT_PROTOCOLID=TPROTCLS_SYSUID and TEXTRACT_SYSUID=TPOOLED_EXTRACTID
+and TPOOLED_SUBID=? and TEXTRACT_ID=?
+and (TPROTCLS_GLOBID is not NULL or (TPROTCLS_DESCR is not NULL and TPROTCLS_ID is not NULL))
+and TEXTRACT_DEL_STATUS='U' and TPOOLED_DEL_STATUS='U' and TPROTCLS_DEL_STATUS='U'
+QUERY
+                last PROTOCOLTYPE;
+            };
+
+            ( $type eq 'pooling' ) && do {
+                $get_ptext = $dbh->prepare(<<'QUERY');
+select distinct TPROTCLS_DESCR, TPROTCLS_SYSUID, TPROTCLS_ID, TPROTCLS_GLOBID
+from TEXTRACT,TPOOLED,TPROTCLS
+where TEXTRACT_POOL_PROTOCOLID=TPROTCLS_SYSUID and TEXTRACT_SYSUID=TPOOLED_EXTRACTID
+and TPOOLED_SUBID=? and TEXTRACT_ID=?
+and (TPROTCLS_GLOBID is not NULL or (TPROTCLS_DESCR is not NULL and TPROTCLS_ID is not NULL))
+and TEXTRACT_DEL_STATUS='U' and TPOOLED_DEL_STATUS='U' and TPROTCLS_DEL_STATUS='U'
+QUERY
+                last PROTOCOLTYPE;
+            };
+
+            ( $type eq 'treatment' ) && do {
+                $get_ptext = $dbh->prepare(<<'QUERY');
+select distinct TPROTCLS_DESCR, TPROTCLS_SYSUID, TPROTCLS_ID, TPROTCLS_GLOBID
+from TPROTCLS,TSAMPLE,TEXPRMNT
+where TSAMPLE_EXPRID=TEXPRMNT_EXPRID and TSAMPLE_PROTOCOLID=TPROTCLS_SYSUID
+and TEXPRMNT_SUBID=? and TSAMPLE_ID=?
+and (TPROTCLS_GLOBID is not NULL or (TPROTCLS_DESCR is not NULL and TPROTCLS_ID is not NULL))
+and TPROTCLS_DEL_STATUS='U' and TSAMPLE_DEL_STATUS='U' and TEXPRMNT_DEL_STATUS='U'
+QUERY
+                last PROTOCOLTYPE;
+            };
+
+            ( $type eq 'growth' ) && do {
+                $get_ptext = $dbh->prepare(<<'QUERY');
+select distinct TPROTCLS_DESCR, TPROTCLS_SYSUID, TPROTCLS_ID, TPROTCLS_GLOBID
+from TPROTCLS,TSAMPLE,TEXPRMNT
+where TSAMPLE_EXPRID=TEXPRMNT_EXPRID and TSAMPLE_GROWTH_PROTOCOLID=TPROTCLS_SYSUID
+and TEXPRMNT_SUBID=? and TSAMPLE_ID=?
+and (TPROTCLS_GLOBID is not NULL or (TPROTCLS_DESCR is not NULL and TPROTCLS_ID is not NULL))
+and TPROTCLS_DEL_STATUS='U' and TSAMPLE_DEL_STATUS='U' and TEXPRMNT_DEL_STATUS='U'
+QUERY
+                last PROTOCOLTYPE;
+            };
+
+            croak("INTERNAL SCRIPT ERROR: Unknown protocol type.\n");
+
+        }
+
+        my $get_others = $dbh->prepare(<<'QUERY');
+select distinct TOTHERS_ID, TOTHERS_VALUE from TOTHERS, TPROTCLS
+where TPROTCLS_SYSUID=TOTHERS_DETAIL_PRTCLID and TPROTCLS_SYSUID=?
+QUERY
+        my $others = $self->_protocol_length_and_others(
+	    $protocol_typemap->{$type},
+	    $get_ptext,
+	    $get_others,
+	);
+
+        $protocol_others->{$type} = $others;
+
+        $get_ptext->finish();
+        $get_others->finish();
+
+    }
+
+    return $protocol_others;
+
+}
+
+sub _tree_to_table : PRIVATE {
+
+    # Converts a hash heirarchy (key1 => {key2 => {key3 => 0}}, ...) into an
+    # array of arrays ([key1, key2, key3],[key1, key4, ...], ...); used to
+    # reverse a hash heirarchy.
+
+    my ( $self, $hash ) = @_;
+    my $AoA;
+
+    foreach my $key ( keys %$hash ) {
+        if ( ref( $hash->{$key} ) eq 'HASH' ) {
+            my $lower_AoA = $self->_tree_to_table( $hash->{$key} );
+            foreach my $row (@$lower_AoA) {
+                push( @$AoA, [ $key, @$row ] );
+            }
+        }
+        else {
+            push @$AoA, [$key];
+        }
+
+    }
+    return $AoA;
+}
+
+sub _invert_heirarchy : PRIVATE {
+
+    # Takes a hashref, inverts the heirarchy using an array of arrays,
+    # and returns a rebuilt inverted heirarchical hash.
+
+    my ( $self, $hash ) = @_;
+
+    my $AoA = $self->_tree_to_table($hash);
+
+    my $newhash;
+    foreach my $row (@$AoA) {
+        $newhash = $self->_build_heirarchy( $newhash, reverse @$row );
+    }
+
+    return $newhash;
+}
+
+sub _build_heirarchy : PRIVATE {
+
+    # Recurse through a passed list of terms, create a Tie::IxHash at
+    # every level.
+
+    # We tie these hashes to maintain database order (i.e. ordered by
+    # SYSUID). This order is then reflected in the biomaterials reports,
+    # which is extremely useful when hacking any output MAGE-ML.
+
+    my ( $self, $hash, @termlist ) = @_;
+
+    my $newterm = shift @termlist;
+
+    unless ($hash) {
+        tie( my %newhash, 'Tie::IxHash' );
+        $hash = \%newhash;
+    }
+
+    ref $hash eq 'HASH' or croak;    # Just in case
+
+    if ( scalar( @termlist ) ) {
+        unless ( $hash->{$newterm} ) {
+            tie( my %newhash, 'Tie::IxHash' );
+            $hash->{$newterm} = \%newhash;
+        }
+        $self->_build_heirarchy( $hash->{$newterm}, @termlist );
+    }
+    else {
+        $hash->{$newterm} = 0;
+    }
+
+    return $hash;
+}
+
+sub _mx_tsample_tntxsyn_name : PRIVATE {
+
+    # Get the TNTXSYN_NAME_TXT value attached to a given sample.
+
+    my ( $self, $tsample_sysuid ) = @_;
+
+    # Connect to MX.
+    my $dbh = $self->get_mx_dbh();
+
+    my $sth = $dbh->prepare(<<"QUERY");
+select distinct TNTXSYN_NAME_TXT
+from TSAMPLE, TNTXSYN
+where TSAMPLE_SYSUID=?
+and TSAMPLE_TAXID=TNTXSYN_TAX_ID
+and TNTXSYN_TAX_ID!='0'
+and TNTXSYN_NAME_TXT!='NULL'
+and TSAMPLE_DEL_STATUS='U'
+QUERY
+    $sth->execute( $tsample_sysuid )
+        or croak($sth->errstr);
+    my $values = $sth->fetchall_arrayref;
+    $sth->finish();
+
+    return $values;
+}
+
+sub _mx_tsample_column_value : PRIVATE {
+
+    # Get the actual column value from a TSAMPLE table column.
+
+    my ( $self, $column, $tsample_sysuid ) = @_;
+
+    # Connect to MX.
+    my $dbh = $self->get_mx_dbh();
+
+    my $sth = $dbh->prepare(<<"QUERY");
+select distinct $column
+from TSAMPLE
+where TSAMPLE_SYSUID=?
+and $column!='NULL'
+and TSAMPLE_DEL_STATUS='U'
+QUERY
+    $sth->execute( $tsample_sysuid )
+        or croak($sth->errstr);
+    my $values = $sth->fetchall_arrayref;
+    $sth->finish();
+
+    return $values;
+}
+
+sub _mx_tsample_tctlvcb_descr : PRIVATE {
+
+    # Get the value in TCTLVCB mapped to by the id in a TSAMPLE table
+    # column.
+
+    my ( $self, $sql_column, $tsample_sysuid ) = @_;
+
+    # Connect to MX.
+    my $dbh = $self->get_mx_dbh();
+
+    my $sth = $dbh->prepare(<<"QUERY");
+select distinct TCTLVCB_DESCR
+from TSAMPLE, TCTLVCB
+where TSAMPLE_SYSUID=?
+and $sql_column=TCTLVCB_SYSUID
+and TCTLVCB_SYSUID!='0'
+and TCTLVCB_DESCR!='NULL'
+and TSAMPLE_DEL_STATUS='U' and TCTLVCB_DEL_STATUS='U'
+QUERY
+    $sth->execute( $tsample_sysuid )
+        or croak($sth->errstr);
+    my $values = $sth->fetchall_arrayref;
+    $sth->finish();
+
+    return $values;
+}
+
+sub _mx_tsample_tothers_value : PRIVATE {
+
+    # Get the value in TOTHERS mapped to by the id in a TSAMPLE table
+    # column.
+
+    my ( $self, $mapped_other, $tsample_sysuid ) = @_;
+
+    # Connect to MX.
+    my $dbh = $self->get_mx_dbh();
+
+    my $sth = $dbh->prepare(<<'QUERY');
+select distinct TOTHERS_VALUE
+from TOTHERS, TSAMPLE
+where TOTHERS_SAMPLEID=?
+and TOTHERS_ID=?
+and TOTHERS_VALUE!='NULL'
+and TSAMPLE_DEL_STATUS='U' and TOTHERS_DEL_STATUS='U'
+QUERY
+    $sth->execute( $tsample_sysuid, $mapped_other )
+        or croak($sth->errstr);
+    my $value = [ "OTHER: " . $sth->fetchall_arrayref()->[0][0] ];
+    $sth->finish();
+
+    return $value;
+}
+
+sub _mx_tfctrval_text_and_unit : PRIVATE {
+
+    # Get the details from TFCTRVAL pertaining to a particular
+    # EXPFCTRID and linked to a given sample.
+
+    my ( $self, $tfctrval_expfctrid, $tsample_sysuid ) = @_;
+
+    # Connect to MX.
+    my $dbh = $self->get_mx_dbh();
+
+    my $sth = $dbh->prepare(<<'QUERY');
+select distinct TFCTRVAL_FREETEXT, TFCTRVAL_FREETEXTUNIT
+from TFCTRVAL
+where TFCTRVAL_SAMPLEID=? and TFCTRVAL_FREETEXT!='NULL'
+and TFCTRVAL_EXPFCTRID=?
+and TFCTRVAL_DEL_STATUS='U'
+QUERY
+    $sth->execute( $tsample_sysuid, $tfctrval_expfctrid )
+        or croak($sth->errstr);
+    my $values = $sth->fetchall_arrayref;
+    $sth->finish();
+
+    return $values;
+}
+
+sub _mx_tsample_annotation_values : PRIVATE {
+
+    # Retrieve a TSAMPLE annotation value based on the passed SQL
+    # column.
+
+    my ( $self, $sql_column, $tsample_sysuid ) = @_;
+
+    my $values;
+
+    # Special cases first.
+    if (grep {/^$sql_column$/i}
+        qw(TSAMPLE_AGERANGE_MIN
+        TSAMPLE_AGERANGE_MAX
+        TSAMPLE_DISEASE_STATE
+        TSAMPLE_CELL_LINE
+        TSAMPLE_TARGET_CELL_TYPE
+        TSAMPLE_INDIVIDUAL_GEN
+        TSAMPLE_STRAIN
+        TSAMPLE_CELL_PROVIDER
+        TSAMPLE_INDIVIDUAL
+        TSAMPLE_ADDITIONAL)
+        ) {
+
+        # AGERANGE columns just use the TSAMPLE table value.
+        # TSAMPLE_INDIVIDUAL, TSAMPLE_ADDITIONAL and several
+        # other columns (see above) also need this treatment.
+        $values = $self->_mx_tsample_column_value(
+	    $sql_column,
+	    $tsample_sysuid,
+	);
+
+    }
+    elsif ( $sql_column eq 'TSAMPLE_TAXID' ) {
+
+        # Organism is a special case, maps to TNXTSYN table.
+        $values = $self->_mx_tsample_tntxsyn_name($tsample_sysuid);
+
+    }
+    else {
+
+        # Next, check TCTLVCB via TSAMPLE; for "other" from a
+        # pulldown list add the value.
+        $values
+            = $self->_mx_tsample_tctlvcb_descr(
+		$sql_column,
+		$tsample_sysuid,
+	    );
+
+        # If returned value contains 'other', map to
+        # TOTHERS. TSAMPLE_DEV_STAGE can be text, so we must allow
+        # this check in that case also.
+        foreach my $value (@$values) {
+
+            # Substitute "other" values.
+            if ( $value->[0] eq 'other' ) {
+                my $mapped_other = $self->_map_samplesql_to_othertag(
+		    $sql_column,
+		);
+                $value = $self->_mx_tsample_tothers_value(
+		    $mapped_other,
+		    $tsample_sysuid,
+		);
+            }
+
+            # Handle the 'not applicable' pulldown menu item by nulling it.
+            elsif ( $value->[0] eq 'not applicable' ) {
+                $value = [];
+            }
+        }
+        unless ( scalar(@$values) ) {
+
+            # Fall back to the actual values in TSAMPLE.
+            $values = $self->_mx_tsample_column_value(
+		$sql_column,
+		$tsample_sysuid,
+	    );
+        }
+    }
+    return $values;
+}
+
+sub _mx_tsample_annotation_report : PRIVATE {
+
+    # Print out a report on annotation for a given sample using an
+    # ExperimentChecker object logprint method.
+
+    my ( $self, $tsample_sysuid ) = @_;
+
+    # Connect to MX.
+    my $dbh = $self->get_mx_dbh();
+
+    my $sth = $dbh->prepare(<<'QUERY');
+select distinct TSAMPLE_ID
+from TSAMPLE
+where TSAMPLE_SYSUID=?
+and TSAMPLE_DEL_STATUS='U'
+QUERY
+
+    $sth->execute( $tsample_sysuid )
+        or croak($sth->errstr);
+
+    # This should only ever return one value (check FIXME)
+    my $tsample_id = $sth->fetchall_arrayref()->[0][0];
+    $sth->finish();
+
+    my @categories          = sort keys %CATEGORY_SQL_MAP;
+    my $name_heading        = 'Sample Name';
+    my $max_category_length = 0;
+    foreach my $category ( @categories, $name_heading ) {
+        my $length = length($category);
+        $max_category_length = $length if ( $length > $max_category_length );
+    }
+
+    # Sample name (TSAMPLE_ID) heading.
+    my $printable = sprintf( "%${max_category_length}s: %s\n",
+        $name_heading, $tsample_id );
+    $self->logprint( 'sample', $printable );
+
+    foreach my $category (@categories) {
+        my $factorstring
+            = $self->_mx_tsample_annotation_string(
+		$category,
+		$tsample_sysuid,
+	    );
+
+        # Only print the fields containing meaningful information.
+        if ( defined($factorstring) && ( $factorstring ne q{} ) ) {
+            my $printable = sprintf( "%${max_category_length}s: %s\n",
+                $category, $factorstring );
+            $self->logprint( 'sample', $printable );
+
+	    if (lc($category) eq 'organism') {
+		$self->add_expt_organisms($factorstring);
+	    }
+        }
+    }
+
+    $self->logprint( 'sample', q{-} x $max_category_length, "-\n" );
+
+    return;
+}
+
+sub _mx_tsample_annotation_string : PRIVATE {
+
+    # For a given category in %CATEGORY_SQL_MAP, generate the
+    # sample-specific string appropriate to that factor.
+
+    my ( $self, $category, $tsample_sysuid ) = @_;
+
+    # Connect to MX.
+    my $dbh = $self->get_mx_dbh();
+
+    my @factor_values;    # This will get stringified later
+
+    # Get an arrayref of places to look for controlled vocabulary terms.
+    my $mapped_columns = $self->_map_efcategorytag_to_samplesql($category);
+
+    # Unless $mapped_columns is empty (get FREETEXT, FREETEXTUNIT),
+    # check for match between each mapped column and TCTLVCB; fall
+    # back to TOTHERS; then just use the TSAMPLE values.
+    foreach my $sql_column (@$mapped_columns) {
+
+        my $values
+            = $self->_mx_tsample_annotation_values(
+		$sql_column,
+		$tsample_sysuid,
+	    );
+
+        # Dump the returned values into our array of FVs.
+        if (@$values) {
+            push( @factor_values, map { @{$_} } @$values );
+        }
+        else {
+
+            # This is needed for correct ordering of complex results,
+            # e.g. age.
+            push( @factor_values, q{} );
+        }
+    }
+
+    # Final fallback to TFCTRVAL_FREETEXT ("other" measurement FVs)
+    unless ( scalar(@factor_values) ) {
+
+        my $sth = $dbh->prepare(<<'QUERY');
+select distinct TFCTRVAL_EXPFCTRID
+from TFCTRVAL, TSAMPLE, TCTLVCB
+where TFCTRVAL_SAMPLEID=TSAMPLE_SYSUID
+and TFCTRVAL_EXPFCTRID=TCTLVCB_SYSUID
+and TSAMPLE_SYSUID=?
+and TCTLVCB_DESCR=?
+and TFCTRVAL_DEL_STATUS='U' and TSAMPLE_DEL_STATUS='U'
+and TCTLVCB_DEL_STATUS='U'
+QUERY
+
+        $sth->execute( $tsample_sysuid, $category )
+            or croak($sth->errstr);
+
+        # This should only ever return one value (check FIXME)
+        my $tfctrval_expfctrid = $sth->fetchall_arrayref()->[0][0];
+        $sth->finish();
+
+        my $values = $self->_mx_tfctrval_text_and_unit(
+	    $tfctrval_expfctrid,
+	    $tsample_sysuid,
+	);
+
+        if (@$values) {
+
+            # Create a FREETEXT FREETEXTUNIT string (omit
+            # FREETEXTUNIT if undef).
+            push( @factor_values,
+                map { $_->[0] . ( defined( $_->[1] ) ? " $_->[1]" : q{} ) }
+                    @$values );
+        }
+    }
+
+    # Build a human-readable string from the factorvalue result.
+    my $factorstring;
+    if ( scalar @factor_values ) {
+        if ( $category eq 'age' ) {    # Age is a special case
+            $factorstring = $self->_mx_factorvalue_agestring(\@factor_values);
+        }
+        else {
+            $factorstring = join( ', ', @factor_values );
+        }
+    }
+    return $factorstring;
+}
+
+sub _get_db_factor_assns : PRIVATE {
+
+    # Get all the factorvalues for an experiment, return them in a
+    # simple {hyb=>fv} heirarchical hashref.
+
+    my ( $self ) = @_;
+
+    # Connect to MX.
+    my $dbh = $self->get_mx_dbh();
+
+    # Get all the categories. This is big because we want to map from
+    # sample to hyb.
+    my $sth = $dbh->prepare(<<'QUERY');
+select distinct THYBRID_ID, TSAMPLE_SYSUID, TCTLVCB_DESCR, TFCTRVAL_EXPFCTRID
+from THYBRID,TLABHYB,TLABEL,TEXTRACT,TSAMPLE,TPOOLED,TFCTRVAL,TCTLVCB
+where THYBRID_SYSUID=TLABHYB_HYBRIDID and TLABEL_SYSUID=TLABHYB_LABELID
+and TEXTRACT_SYSUID=TLABEL_EXTRACTID and TSAMPLE_SYSUID=TPOOLED_SAMPLEID
+and TEXTRACT_SYSUID=TPOOLED_EXTRACTID and TFCTRVAL_SAMPLEID=TSAMPLE_SYSUID
+and TFCTRVAL_EXPFCTRID=TCTLVCB_SYSUID
+and TLABHYB_SUBID=?
+and TLABHYB_DEL_STATUS='U' and THYBRID_DEL_STATUS='U' and TLABEL_DEL_STATUS='U'
+and TEXTRACT_DEL_STATUS='U' and TSAMPLE_DEL_STATUS='U' and TPOOLED_DEL_STATUS='U'
+and TFCTRVAL_DEL_STATUS='U' and TCTLVCB_DEL_STATUS='U'
+order by TCTLVCB_DESCR, THYBRID_ID, TSAMPLE_SYSUID
+QUERY
+
+    $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+    # Array of hashrefs, one per experimental factor category.
+    my @retrieved_factors;
+    while ( my $rowref = $sth->fetchrow_hashref ) {
+        push @retrieved_factors, $rowref;
+    }
+    $sth->finish();    # Overlapping prepares not good for portability
+
+    my ($hyb_factors, %hyb_category_fvs);
+
+    foreach my $factor (@retrieved_factors) {
+
+        my $factorstring =
+          $self->_mx_tsample_annotation_string(
+	      $factor->{'TCTLVCB_DESCR'},
+	      $factor->{'TSAMPLE_SYSUID'},
+	  );
+
+	# Construct the category-FV hash for mapping to files later. 
+        $hyb_category_fvs{ $factor->{'THYBRID_ID'} }
+          { $factor->{'TCTLVCB_DESCR'} }{$factorstring}++;
+
+        $factorstring .= " ($factor->{'TCTLVCB_DESCR'})";
+
+        # Add the factor string to the $hyb_factors heirarchy.
+        $hyb_factors = $self->_build_heirarchy(
+	    $hyb_factors,
+	    $factor->{'THYBRID_ID'},
+            $factorstring,
+	);
+    }
+    return ($hyb_factors, \%hyb_category_fvs);
+}
+
+sub _mx_factorvalue_agestring : PRIVATE {
+
+    # Sub takes an age fv arrayref as defined by
+    # _map_efcategorytag_to_samplesql, and formats it nicely for
+    # printing.
+
+    my ( $self, $factor_values ) = @_;
+
+    # Sort out the age (and optional range) to start with.
+    my $factorstring = $factor_values->[0];
+    $factorstring .= "-$factor_values->[1]"
+        if $factor_values->[1];
+
+    # Don't bother with units if there's no
+    # age or age range.
+    if ($factorstring) {
+        $factorstring .= " $factor_values->[2]"
+            if $factor_values->[2];
+    }
+
+    # We keep the initial time point regardless, as it may indicate a
+    # submitter error.
+    $factorstring .= " since $factor_values->[3]"
+        if $factor_values->[3];
+
+    return $factorstring;
+}
+
+sub _map_efcategorytag_to_samplesql : PRIVATE {
+
+    my ( $self, $category ) = @_;
+
+    # Empty arrayrefs typically signify that these are 'other' FVs, held
+    # in TFCTRVAL_FREETEXT and TFCTRVAL_FREETEXTUNIT. This is an
+    # egregious hack made necessary by the MIAMExpress schema.
+    unless ( $CATEGORY_SQL_MAP{$category} ) {
+        croak(
+            qq{ERROR: Unmapped experimental factor category "$category".\n});
+    }
+
+    return $CATEGORY_SQL_MAP{$category};
+}
+
+sub _map_samplesql_to_othertag : PRIVATE {
+
+    my ( $self, $sql_colname ) = @_;
+
+    # These map the TCTLVCB-linked TSAMPLE columns which also have
+    # "other" as an option to the relevant TOTHERS_ID. This is an
+    # egregious hack made necessary by the MIAMExpress schema.
+    my %mapping = (
+        TSAMPLE_SAMPLE_TYPE       => 'SAMPLE_TYPE',
+        TSAMPLE_DEV_STAGE         => 'DEVELOPMENTAL_STAGE',
+        TSAMPLE_TIME_POINT        => 'AGE_TIME_POINTS',
+        TSAMPLE_ORGANISM_PART     => 'ORGANISM_PART',
+        TSAMPLE_SEX               => 'SEX',
+        TSAMPLE_GENETIC_VARIATION => 'GENETIC_VARIATION',
+        TSAMPLE_TARGET_CELL_TYPE  => 'CELL_TYPE',
+        TSAMPLE_TREATMENT_TYPE    => 'TREATMENT_TYPE',
+        TSAMPLE_SEPARATION_TECH   => 'SEPARATION_TECHNIQUE',
+    );
+
+    unless ( $mapping{$sql_colname} ) {
+        croak(qq{ERROR: Unmapped TSAMPLE SQL column name "$sql_colname".\n});
+    }
+
+    return $mapping{$sql_colname};
+}
+
+sub _get_db_objects : PRIVATE {
+
+    # Gets samples, extracts, labelled extracts, hybs, and returns
+    # hashes describing how they map to each other. See
+    # &heirarchy_report for how these are used.
+
+    my ( $self ) = @_;
+
+    # Connect to MX.
+    my $dbh = $self->get_mx_dbh();
+
+    my ( $samples_to_hybs, %protocol_typemap );
+
+    my $error = 0;
+
+    # Get a table with sample, extract, labeled extract and hyb
+    # columns. We retrieve SYSUIDs as well, to guard against duplicate
+    # names. SYSUIDs are also used to sort the tied hashes.
+    my $sth = $dbh->prepare(<<'QUERY');
+select distinct TSAMPLE_ID,TEXTRACT_ID,TLABEL_ID,THYBRID_ID,TSAMPLE_SYSUID,TEXTRACT_SYSUID,TLABEL_SYSUID,THYBRID_SYSUID
+from THYBRID,TLABHYB,TLABEL,TEXTRACT,TSAMPLE,TPOOLED
+where THYBRID_SYSUID=TLABHYB_HYBRIDID and TLABEL_SYSUID=TLABHYB_LABELID
+and TEXTRACT_SYSUID=TLABEL_EXTRACTID and TSAMPLE_SYSUID=TPOOLED_SAMPLEID
+and TEXTRACT_SYSUID=TPOOLED_EXTRACTID and TLABHYB_SUBID=?
+and TLABHYB_DEL_STATUS='U' and THYBRID_DEL_STATUS='U' and TLABEL_DEL_STATUS='U'
+and TEXTRACT_DEL_STATUS='U' and TSAMPLE_DEL_STATUS='U' and TPOOLED_DEL_STATUS='U'
+order by TSAMPLE_SYSUID, TEXTRACT_SYSUID, TLABEL_SYSUID, THYBRID_SYSUID
+QUERY
+    $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+    while ( my $rowref = $sth->fetchrow_hashref ) {
+
+        # Use of THYBRID_ID instead of THYBRID_SYSUID here is a bug
+        # FIXME. See also below for other protocol types. Note that a
+        # check should now have been added to the MIAMExpress codebase to
+        # prevent this bug ever occurring.
+        $protocol_typemap{hybridization}{ $rowref->{'THYBRID_ID'} } = [];
+        $protocol_typemap{normalization}{ $rowref->{'THYBRID_ID'} } = [];
+        $protocol_typemap{scanning}{ $rowref->{'THYBRID_ID'} }      = [];
+
+        my @terms = (
+            $rowref->{'TSAMPLE_ID'}, $rowref->{'TEXTRACT_ID'},
+            $rowref->{'TLABEL_ID'},  $rowref->{'THYBRID_ID'}
+        );
+        $samples_to_hybs = $self->_build_heirarchy(
+	    $samples_to_hybs,
+	    @terms,
+	);
+    }
+
+    # dump the statement handler now that we are finished with it
+    $sth->finish();
+
+    # We need to check for unused labels, extracts, samples here.
+    # Set up our nulls:
+    my $null = '**{NULL}**';
+
+    # Labeled Extract check
+    $sth = $dbh->prepare(<<'QUERY');
+select distinct TSAMPLE_ID,TEXTRACT_ID,TLABEL_ID,TSAMPLE_SYSUID,TEXTRACT_SYSUID,TLABEL_SYSUID
+from TLABEL,TEXTRACT,TSAMPLE,TPOOLED
+where TEXTRACT_SYSUID=TLABEL_EXTRACTID and TSAMPLE_SYSUID=TPOOLED_SAMPLEID
+and TEXTRACT_SYSUID=TPOOLED_EXTRACTID and TPOOLED_SUBID=?
+and TLABEL_DEL_STATUS='U' and TEXTRACT_DEL_STATUS='U'
+and TPOOLED_DEL_STATUS='U' and TSAMPLE_DEL_STATUS='U'
+order by TSAMPLE_SYSUID, TEXTRACT_SYSUID, TLABEL_SYSUID
+QUERY
+    $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+    QUERY_RESULT:
+    while ( my $rowref = $sth->fetchrow_hashref ) {
+
+        $protocol_typemap{labeling}{ $rowref->{'TLABEL_ID'} } = [];
+
+        next QUERY_RESULT
+            if $samples_to_hybs->{ $rowref->{'TSAMPLE_ID'} }
+            { $rowref->{'TEXTRACT_ID'} }{ $rowref->{'TLABEL_ID'} };
+
+        my @terms = (
+            $rowref->{'TSAMPLE_ID'},
+            $rowref->{'TEXTRACT_ID'},
+            $rowref->{'TLABEL_ID'}, $null,
+        );
+        $samples_to_hybs = $self->_build_heirarchy(
+	    $samples_to_hybs,
+	    @terms,
+	);
+        $error |= $CONFIG->get_ERROR_PARSEFAIL();
+    }
+
+    # dump the statement handler now that we are finished with it
+    $sth->finish();
+
+    # Extract check
+    $sth = $dbh->prepare(<<'QUERY');
+select distinct TSAMPLE_ID,TEXTRACT_ID,TSAMPLE_SYSUID,TEXTRACT_SYSUID
+from TEXTRACT,TSAMPLE,TPOOLED
+where TSAMPLE_SYSUID=TPOOLED_SAMPLEID
+and TEXTRACT_SYSUID=TPOOLED_EXTRACTID and TPOOLED_SUBID=?
+and TEXTRACT_DEL_STATUS='U' and TPOOLED_DEL_STATUS='U' and TSAMPLE_DEL_STATUS='U'
+order by TSAMPLE_SYSUID, TEXTRACT_SYSUID
+QUERY
+    $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+    QUERY_RESULT:
+    while ( my $rowref = $sth->fetchrow_hashref ) {
+
+        $protocol_typemap{extraction}{ $rowref->{'TEXTRACT_ID'} } = [];
+        $protocol_typemap{pooling}{ $rowref->{'TEXTRACT_ID'} }    = [];
+
+        next QUERY_RESULT
+            if $samples_to_hybs->{ $rowref->{'TSAMPLE_ID'} }
+            { $rowref->{'TEXTRACT_ID'} };
+
+        my @terms = (
+            $rowref->{'TSAMPLE_ID'},
+            $rowref->{'TEXTRACT_ID'},
+            $null, $null,
+        );
+        $samples_to_hybs = $self->_build_heirarchy(
+	    $samples_to_hybs,
+	    @terms,
+	);
+        $error |= $CONFIG->get_ERROR_PARSEFAIL();
+    }
+
+    # dump the statement handler now that we are finished with it
+    $sth->finish();
+
+    # Sample check (we have to go in though TEXPRMNT here)
+    $sth = $dbh->prepare(<<'QUERY');
+select distinct TSAMPLE_ID,TSAMPLE_SYSUID
+from TSAMPLE,TEXPRMNT
+where TSAMPLE_EXPRID=TEXPRMNT_EXPRID
+and TEXPRMNT_SUBID=?
+and TSAMPLE_DEL_STATUS='U' and TEXPRMNT_DEL_STATUS='U'
+order by TSAMPLE_SYSUID
+QUERY
+    $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+    QUERY_RESULT:
+    while ( my $rowref = $sth->fetchrow_hashref ) {
+
+        $protocol_typemap{treatment}{ $rowref->{'TSAMPLE_ID'} } = [];
+        $protocol_typemap{growth}{ $rowref->{'TSAMPLE_ID'} }    = [];
+
+        next QUERY_RESULT
+            if $samples_to_hybs->{ $rowref->{'TSAMPLE_ID'} };
+
+        my @terms = ( $rowref->{'TSAMPLE_ID'}, $null, $null, $null, );
+        $samples_to_hybs = $self->_build_heirarchy(
+	    $samples_to_hybs,
+	    @terms,
+	);
+        $error |= $CONFIG->get_ERROR_PARSEFAIL();
+    }
+
+    # dump the statement handler now that we are finished with it
+    $sth->finish();
+
+    # Deal with FGEM files here
+    $sth = $dbh->prepare(<<'QUERY');
+select distinct TFILE_PATH from TFILE,TEXPRFNL
+where TFILE_EXPRFNLID=TEXPRFNL_SYSUID and TEXPRFNL_SUBID=?
+and TFILE_DEL_STATUS='U'
+QUERY
+    $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+    while ( my $rowref = $sth->fetchrow_hashref ) {
+        $protocol_typemap{transformation}{ $rowref->{'TFILE_PATH'} } = [];
+    }
+    $sth->finish();
+    
+    # Further populate our protocol hash
+    my $protocol_others = $self->_get_db_protocols( \%protocol_typemap );
+
+    # Return HoHoHoH for the associations between sample, extract,
+    # labeled extract and hyb (in both directions).
+    return ( $samples_to_hybs, \%protocol_typemap, $protocol_others, $error );
+}
+
+sub _get_db_expr_ids : PRIVATE {
+
+    my ( $self ) = @_;
+
+    # Connect to MX.
+    my $dbh = $self->get_mx_dbh();
+
+    # Use a hash to get a uniqued list of expr_ids
+    my %expr_ids;
+
+    my $sth = $dbh->prepare(<<'QUERY');
+select distinct TEXPRMNT_EXPRID
+from TEXPRMNT
+where TEXPRMNT_SUBID=?
+and TEXPRMNT_DEL_STATUS='U'
+QUERY
+    $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+    while ( my $rowref = $sth->fetchrow_hashref ) {
+        $expr_ids{ $rowref->{'TEXPRMNT_EXPRID'} }++;
+    }
+
+    # dump the statement handler now that we are finished with it
+    $sth->finish();
+
+    return \%expr_ids;
+
+}
+
+sub _get_db_sample_annotation : PRIVATE {
+
+    my ( $self ) = @_;
+
+    # Connect to MX.
+    my $dbh = $self->get_mx_dbh();
+
+    # Find all the TSAMPLE_SYSUIDs for the experiment.
+    my $sth = $dbh->prepare(<<'QUERY');
+select distinct TSAMPLE_SYSUID
+from TSAMPLE,TEXPRMNT
+where TSAMPLE_EXPRID=TEXPRMNT_EXPRID
+and TEXPRMNT_SUBID=?
+and TSAMPLE_DEL_STATUS='U'
+QUERY
+    $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+    my @sample_sysuids;
+    while ( my $sample_ref = $sth->fetchrow_arrayref() ) {
+        push( @sample_sysuids, @$sample_ref );
+    }
+    $sth->finish();
+
+    # Print a report on each sample.
+    foreach my $tsample_sysuid (@sample_sysuids) {
+        $self->_mx_tsample_annotation_report($tsample_sysuid);
+    }
+
+    return;
+}
+
+sub _get_db_others : PRIVATE {
+
+    my ( $self ) = @_;
+
+    # Connect to MX.
+    my $dbh = $self->get_mx_dbh();
+
+    my @others;    #AoH
+
+    my $expr_ids = $self->_get_db_expr_ids();
+
+    # Top-level experiment OTHERSs
+    my $sth = $dbh->prepare(<<'QUERY');
+select distinct TOTHERS_ID,TOTHERS_VALUE,TEXPRMNT_EXPRID
+from TOTHERS, TEXPRMNT, TEXPRQC
+where TEXPRQC_EXPRID=TOTHERS_EXPRID
+and TOTHERS_EXPRID=TEXPRMNT_EXPRID
+and TOTHERS_DEL_STATUS='U'
+and TEXPRQC_DEL_STATUS='U'
+and TEXPRMNT_DEL_STATUS='U'
+and TEXPRMNT_SUBID=?
+QUERY
+    $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+    while ( my $rowref = $sth->fetchrow_hashref ) {
+        $rowref->{'TOTHERS_ID'} =~ s/_/ /g;
+        push(
+            @others,
+            {   id      => lc( $rowref->{'TOTHERS_ID'} ),
+                value   => $rowref->{'TOTHERS_VALUE'},
+                expr_id => $rowref->{'TEXPRMNT_EXPRID'},
+            }
+        );
+    }
+
+    # If there are no QC others, we need to try again to get top-level others
+    unless (@others) {
+        $sth = $dbh->prepare(<<'QUERY');
+select distinct TOTHERS_ID,TOTHERS_VALUE,TEXPRMNT_EXPRID
+from TOTHERS, TEXPRMNT
+where TOTHERS_EXPRID=TEXPRMNT_EXPRID
+and TOTHERS_DEL_STATUS='U'
+and TEXPRMNT_DEL_STATUS='U'
+and TEXPRMNT_SUBID=?
+QUERY
+        $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+        while ( my $rowref = $sth->fetchrow_hashref ) {
+            $rowref->{'TOTHERS_ID'} =~ s/_/ /g;
+            push(
+                @others,
+                {   id      => lc( $rowref->{'TOTHERS_ID'} ),
+                    value   => $rowref->{'TOTHERS_VALUE'},
+                    expr_id => $rowref->{'TEXPRMNT_EXPRID'},
+                }
+            );
+        }
+    }
+
+    # dump the statement handler now that we are finished with it
+    $sth->finish();
+
+    # Get the OTHERs for samples associated with the experiment(s) - of
+    # course, there should be only one experiment, but we're making this
+    # extensible (maybe).
+    foreach my $expr_id ( keys %$expr_ids ) {
+
+        my $sth = $dbh->prepare(<<'QUERY');
+select distinct TOTHERS_ID,TOTHERS_VALUE,TSAMPLE_ID
+from TOTHERS,TSAMPLE
+where TSAMPLE_SYSUID = TOTHERS_SAMPLEID
+and TOTHERS_DEL_STATUS='U'
+and TSAMPLE_DEL_STATUS='U'
+and TSAMPLE_EXPRID=?
+QUERY
+        $sth->execute($expr_id) or croak($sth->errstr);
+
+        while ( my $rowref = $sth->fetchrow_hashref ) {
+            $rowref->{'TOTHERS_ID'} =~ s/_/ /g;
+            push(
+                @others,
+                {   id        => lc( $rowref->{'TOTHERS_ID'} ),
+                    value     => $rowref->{'TOTHERS_VALUE'},
+                    sample_id => $rowref->{'TSAMPLE_ID'},
+                    expr_id   => $expr_id,
+                }
+            );
+        }
+
+        # dump the statement handler now that we are finished with it
+        $sth->finish();
+    }
+    return \@others;
+
+}
+
+sub _get_db_qvs : PRIVATE {
+
+    my ( $self ) = @_;
+
+    # Connect to MX.
+    my $dbh = $self->get_mx_dbh();
+
+    my @qvs;    #AoH
+
+    my $expr_ids = $self->_get_db_expr_ids();
+
+    # Top-level experiment QVSs
+    my $sth = $dbh->prepare(<<'QUERY');
+select distinct TQUALIF_NAME,TQUALIF_VALUE,TQUALIF_SOURCE,TEXPRMNT_EXPRID
+from TQUALIF,TEXPRMNT
+where TEXPRMNT_EXPRID = TQUALIF_EXPRID
+and TQUALIF_DEL_STATUS='U'
+and TEXPRMNT_DEL_STATUS='U'
+and TEXPRMNT_SUBID=?
+QUERY
+    $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+    while ( my $rowref = $sth->fetchrow_hashref ) {
+        push(
+            @qvs,
+            {   qualifier => $rowref->{'TQUALIF_NAME'},
+                value     => $rowref->{'TQUALIF_VALUE'},
+                source    => $rowref->{'TQUALIF_SOURCE'},
+                expr_id   => $rowref->{'TEXPRMNT_EXPRID'},
+            }
+        );
+    }
+
+    # dump the statement handler now that we are finished with it
+    $sth->finish();
+
+    # Get the QVSs for samples associated with the experiment(s) - of
+    # course, there should be only one experiment, but we're making this
+    # extensible (maybe).
+    foreach my $expr_id ( keys %$expr_ids ) {
+
+        my $sth = $dbh->prepare(<<'QUERY');
+select distinct TQUALIF_NAME,TQUALIF_VALUE,TQUALIF_SOURCE,TSAMPLE_ID
+from TQUALIF,TSAMPLE
+where TSAMPLE_SYSUID = TQUALIF_SAMPLEID
+and TQUALIF_DEL_STATUS='U'
+and TSAMPLE_DEL_STATUS='U'
+and TSAMPLE_EXPRID=?
+QUERY
+        $sth->execute($expr_id) or croak($sth->errstr);
+
+        while ( my $rowref = $sth->fetchrow_hashref ) {
+            push(
+                @qvs,
+                {   qualifier => $rowref->{'TQUALIF_NAME'},
+                    value     => $rowref->{'TQUALIF_VALUE'},
+                    source    => $rowref->{'TQUALIF_SOURCE'},
+                    sample_id => $rowref->{'TSAMPLE_ID'},
+                    expr_id   => $expr_id,
+                }
+            );
+        }
+
+        # dump the statement handler now that we are finished with it
+        $sth->finish();
+    }
+    return \@qvs;
+}
+
+sub _get_array_designid : PRIVATE {
+
+    my ( $self, $hyb_sysuid ) = @_;
+
+    # Connect to MX.
+    my $dbh = $self->get_mx_dbh();
+
+    # Get a single cell containing the array design id, hopefully.
+    my $sth = $dbh->prepare(<<'QUERY');
+select distinct TARRAY_DESIGNID from TARRAY,THYBRID
+where TARRAY_SYSUID = THYBRID_ARRAYID
+and TARRAY_DEL_STATUS='U' and THYBRID_DEL_STATUS='U'
+and THYBRID_SYSUID=?
+QUERY
+    $sth->execute($hyb_sysuid) or croak($sth->errstr);
+
+    my $arrayref = $sth->fetchall_arrayref;
+
+    # dump the statement handler now that we are finished with it
+    $sth->finish();
+
+    # One entry only
+    if ( $#{$arrayref} > 0 ) {
+        croak(
+            "Error: MIAMExpress returns too many arrays for hyb sysuid $hyb_sysuid.\n"
+        );
+    }
+
+    my $array_designid = $arrayref->[0][0];
+
+    # No zeros allowed.
+    unless ($array_designid) {
+        croak(
+            "Error: MIAMExpress returned array design id of zero for hyb sysuid $hyb_sysuid.\n"
+        );
+    }
+
+    return ($array_designid);
+}
+
+sub _get_adf_filename : PRIVATE {
+
+    my ( $self, $array_designid ) = @_;
+
+    # Connect to MX.
+    my $dbh = $self->get_mx_dbh();
+
+    # Get a single row containing the ADF filename, submission id and
+    # array submitter login, hopefully.
+    my $sth = $dbh->prepare(<<'QUERY');
+select distinct TARDESIN_SYSUID,TFILE_PATH,TARDESIN_SUBID,TSUBMIS_LOGIN,TARDESIN_DESIGN_NAME
+from TARDESIN, TFILE, TSUBMIS
+where TFILE_ARDESINID=TARDESIN_SYSUID and TSUBMIS_SYSUID=TARDESIN_SUBID
+and TARDESIN_DEL_STATUS='U' and TFILE_DEL_STATUS='U' and TSUBMIS_DEL_STATUS='U'
+and TARDESIN_SYSUID=?
+QUERY
+    $sth->execute($array_designid) or croak($sth->errstr);
+
+    my $results = $sth->fetchall_hashref('TARDESIN_SYSUID')
+	or croak($sth->errstr);
+
+    my $arrayinfo = $results->{$array_designid}
+	or croak("Error retrieving MIAMExpress ADF filename for $array_designid.\n");
+
+    # dump the statement handler now that we are finished with it
+    $sth->finish();
+
+    # Get the array filebase, while we have a connection.
+    $sth = $dbh->prepare(<<'QUERY');
+select distinct TSYSOPT_VALUE from TSYSOPT
+where TSYSOPT_CODE='array_datafiles_path'
+QUERY
+
+    $sth->execute() or die($sth->errstr);
+
+    # FIXME we're assuming the query worked here.
+    my $adf_filename
+	= File::Spec->catfile(
+	    $sth->fetchrow_arrayref()->[0],
+	    $arrayinfo->{'TSUBMIS_LOGIN'},
+	    'array' . $arrayinfo->{'TARDESIN_SUBID'},
+	    $arrayinfo->{'TFILE_PATH'},
+	);
+
+    return (
+	$adf_filename,
+	$arrayinfo->{'TARDESIN_DESIGN_NAME'},
+    );
+}
+
+sub _report_description : PRIVATE {
+
+    my ( $self ) = @_;
+
+    my $report_fh = $self->log_fh('report');
+
+    # Connect to MX.
+    my $dbh = $self->get_mx_dbh();
+
+    my $sth = $dbh->prepare(<<'QUERY');
+select distinct TEXPRMNT_DESCR
+from TEXPRMNT
+where TEXPRMNT_DEL_STATUS='U'
+and TEXPRMNT_SUBID=?
+QUERY
+    $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+    my $arrayref = $sth->fetchall_arrayref or croak($sth->errstr);
+
+    foreach my $description (@$arrayref) {
+        format_description( $description->[0], $report_fh );
+        print $report_fh "\n";
+    }
+
+    return;
+}
+
+sub visualize_experiment : RESTRICTED {
+
+    my ( $self, $filelist ) = @_;
+
+    warn("Graph visualization not supported for MIAMExpress experiments.\n");
+
+    return;
+}
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../index.html">
+                <img src="../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: MIAMExpress.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Curator::MIAMExpress - a module used by expt_check.pl
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Curator::MIAMExpress;
+ 
+ my $checker = ArrayExpress::Curator::MIAMExpress->new({
+     mx_login => $login,
+     mx_title => $title,
+ });
+ 
+ $checker->check();
+
+=head1 DESCRIPTION
+
+This module provides a set of subroutines used by the experiment
+checker script to interact with a local MIAMExpress installation. It
+optionally exports just two (fairly heavyweight) subroutines. Please
+see the end of this document for the tests carried out by this module.
+
+=head1 OPTIONS
+
+The following options must be used in addition to those provided by the
+parent class (see L<ArrayExpress::Curator::ExperimentChecker>):
+
+=over 2
+
+=item C<mx_login>
+
+The submitter login name associated with a MIAMExpress submission. See
+C<mx_title>.
+
+=item C<mx_title>
+
+The title of a MIAMExpress submission. See C<mx_login>.
+
+=back
+
+=head1 TESTS
+
+The following tests are performed by this module, with output printed
+to the error and/or report filehandles (see
+L<ArrayExpress::Curator::ExperimentChecker> for a list of more general
+tests which are also run):
+
+=over 4
+
+=item B<BioMaterials>
+
+Checks the linking between sample, extract, labeled extract and
+hybridization, and writes a biomaterials log file which attempts to
+portray these links in ASCII. Highlights BioMaterials which have not
+been properly linked through to hybridizations (biomaterials log).
+
+=item B<Experimental factor values>
+
+Generates a report showing how hybridizations have been linked to
+experimental factor values, and vice versa (biomaterials log).
+
+=item B<Sample annotation>
+
+Generates a report on all the annotation attached to the samples in
+the experiment (sample log).
+
+=item B<Publication>
+
+Checks whether the publication details include standard Pubmed
+abbreviations; also alerts the curator if a new publication ("other")
+has been indicated (error log).
+
+=item B<QualifierValueSource>
+
+Alerts the curator if QVS entries have been used under Experiment or
+Sample annotation (error log).
+
+=item B<Other>
+
+Alerts the curator if "Other" entries have been used under Experiment, Protocol or
+Sample annotation (error log).
+
+=item B<Transformation protocol>
+
+Alerts the curator if a final data matrix file has been supplied
+without a transformation protocol (error log).
+
+=item B<Protocol description length>
+
+Alerts the curator if protocols with fewer than 50 characters are
+submitted (error log).
+
+=item B<Pooling protocol>
+
+Alerts the curator if a pooling protocol has been linked to a
+submission (error log).
+
+=item B<Normalization protocol>
+
+Alerts the curator if normalized data files have been supplied
+without a normalization protocol (error log).
+
+=item B<Other unbound protocols>
+
+Alerts the curator if protocols other than pooling, normalization or
+transformation have not been bound to their appropriate objects (error
+log).
+
+=item B<Number of files submitted per hybridization>
+
+Prints a table indicating which kinds of raw and normalized data file
+have been submitted for each hybridization (report log).
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+1;
diff --git a/lib/ArrayExpress/Curator/Report.pm b/lib/ArrayExpress/Curator/Report.pm
new file mode 100644
index 0000000..b2f07d7
--- /dev/null
+++ b/lib/ArrayExpress/Curator/Report.pm
@@ -0,0 +1,438 @@
+#!/usr/bin/env perl
+#
+# Report.pm - a module derived from and used in the experiment
+# checker script. Contains routines which might be useful elsewhere.
+#
+# Tim Rayner 2004 ArrayExpress team, EBI
+#
+# $Id: Report.pm 1939 2008-02-10 23:29:47Z tfrayner $
+#
+
+package ArrayExpress::Curator::Report;
+
+use strict;
+use warnings;
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../index.html">
+                <img src="../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: Reporter.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Curator::Reporter.pm - a module providing some 
+functions useful in generating reports.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Curator::Report qw(datafile_consistency_table);
+ 
+ print datafile_consistency_table( \@filelist );
+ 
+=head1 DESCRIPTION
+
+This is a simple module providing utility functions and regexp
+patterns used elsewhere in the Tab2MAGE package.
+
+=head1 FUNCTIONS
+
+=over 2
+
+=item C<datafile_consistency_table( \@filelist )>
+
+Given an arrayref of Datafile objects (see L<ArrayExpress::Datafile>),
+returns a string representing a summary of files tabulated by
+hyb_identifier, reporting on presence or absence in the filesystem.
+
+=item C<biomaterials_report( $HoH, $title, $fh )>
+
+Given a nested hashref of hashrefs (of indeterminate depth), a report
+title and an output filehandle, prints a report giving links between
+nodes in the hashref structure (recursively).
+
+=item C<report_accumulated_errors( $HoH, $fh, $string )>
+
+Given a hashref of hashrefs keyed by filename and then by elements
+(e.g. duplicate feature identifiers), an output filehandle and an
+explanatory string, print a report.
+
+=item C<format_description( $text, $fh )>
+
+Takes a text string and an output filehandle, and prints the string in
+lines of less than 80 chars, splitting words on whitespace.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2008.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments. 
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+use base 'Exporter';
+our @EXPORT_OK = qw(
+    datafile_consistency_table
+    biomaterials_report
+    report_accumulated_errors
+    format_description
+);
+
+sub get_max_colwidth {
+
+    my ( $hashref, $previousmax, $lookup ) = @_;
+
+    my $col_width = 0;
+    foreach my $key ( keys %$hashref ) {
+        my $keylength = length( $lookup->{$key} );
+        $col_width = $keylength if ( $keylength > $col_width );
+    }
+
+    $col_width = $previousmax
+        if ( $previousmax && $previousmax > $col_width );
+
+    return ($col_width);
+
+}
+
+sub _recurse_heirarchy {
+
+    # Prints a report given a ref to a HoHoHoH of indeterminate
+    # depth. Deals only with the hash keys. The terminal hash values
+    # _must_ be zero or undef to finish the recursion properly. Note
+    # that this function is recursive, and as such is a little hairy in
+    # places.
+
+    my ( $HoH, $widths, $depth ) = @_;
+
+    # Start at depth 0, recurse to depth $#$widths.
+    $depth ||= 0;
+
+    my $col_width = $widths->[$depth];
+
+    # Define the column format.
+    my $col_format = "%" . "${col_width}.${col_width}s";
+
+    # Initialise $lines (must not be undefined).
+    my $lines = q{};
+
+    my @termlist = tied(%$HoH) ? keys %$HoH : sort keys %$HoH;
+
+    foreach my $term (@termlist) {
+
+        my $lines_ending = substr( $lines, length($lines) - 1 );
+
+        if ( $depth && $lines_ending eq "\n" ) {
+
+            # If we're at depth, and we've just finished a line;
+            # -- insert some spacers,
+            for ( my $i = 0; $i < $depth - 1; $i++ ) {
+                my $col_format
+                    = "%" . $widths->[$i] . "." . $widths->[$i] . "s";
+                my $spacer = sprintf( "$col_format     ", q{} );
+                $lines .= $spacer;
+            }
+
+            # -- and an arrow,
+            my $i          = $depth - 1;
+            my $col_format = "%" . $widths->[$i] . "." . $widths->[$i] . "s";
+            my $arrow      = sprintf( "$col_format \\-> ", q{} );
+            $lines .= $arrow;
+        }
+
+        $lines .= sprintf( $col_format, $term );
+
+        if ( $HoH->{$term} ) {
+
+            # If we haven't reached the bottom of the HoHoHoH...
+            # -- append a mapping arrow.
+            $lines .= " --> ";
+
+            # Recurse to the next depth in the HoHoHoH
+            $lines
+                .= _recurse_heirarchy( $HoH->{$term}, $widths, $depth + 1 );
+
+        }
+        else {
+
+            $lines .= "\n";
+
+        }
+    }
+    return ($lines);
+}
+
+sub _collect_widths {
+
+    my ( $HoH, $depth ) = @_;
+
+    $depth ||= 0;
+    my @width;
+
+    # Figure out column width at this depth
+    $width[$depth] ||= 0;
+    foreach my $key ( keys %$HoH ) {
+        my $keylength = length($key);
+        $width[$depth] = $keylength if ( $keylength > $width[$depth] );
+    }
+
+    foreach my $term ( keys %$HoH ) {
+        if ( $HoH->{$term} ) {
+
+            # If we haven't reached the bottom of the HoHoHoH, recurse to
+            # the next level down.
+            my $subwidth = _collect_widths( $HoH->{$term}, $depth + 1 );
+
+            RECURSION_DEPTH:
+            for ( my $i = 0; $i <= $#$subwidth; $i++ ) {
+
+                next RECURSION_DEPTH unless defined( $subwidth->[$i] );
+
+                # If this width is greater than that in the top-level array,
+                # modify the top-level array accordingly.
+                if ( !defined( $width[$i] )
+                    || $subwidth->[$i] > $width[$i] ) {
+                    $width[$i] = $subwidth->[$i];
+                }
+            }
+        }
+    }
+    return \@width;
+
+}
+
+sub format_report {
+
+    # NB. Normally this sub is passed a hashref tied to Tie::IxHash to
+    # preserve creation order (ordered by SYSUID). Untied hashes are
+    # sorted by their keys (typically ID). This is implemented in
+    # _recurse_heirarchy.
+
+    my ($HoH) = @_;
+
+    # Populate an arrayref:
+    # $widths->[$depth] = $column_width where $depth is (0..$levels-1)
+    my $widths = _collect_widths($HoH);
+
+    # Use the widths information to format a report string
+    my $lines = _recurse_heirarchy( $HoH, $widths );
+
+    return $lines;
+
+}
+
+sub biomaterials_report {
+
+    # Prints the workflow reports
+
+    my ( $report_data, $title, $workflow_fh ) = @_;
+
+    # if filehandle unset, use STDOUT
+    $workflow_fh =
+          $workflow_fh
+        ? $workflow_fh
+        : \*STDOUT;
+
+    print $workflow_fh ("\n");
+    print $workflow_fh ( "#" x ( length($title) + 4 ) . "\n" );
+    print $workflow_fh ("# $title #\n");
+    print $workflow_fh ( "#" x ( length($title) + 4 ) . "\n" );
+    print $workflow_fh ("\n");
+
+    print $workflow_fh ( format_report($report_data) );
+
+    print $workflow_fh ("\n");
+
+    return;
+}
+
+sub draw_table_line {
+
+    # Draws a horizontal table line given the width of the first column,
+    # the number of other columns, and their width.
+
+    my ( $first_colwidth, $colwidth, $datacolno ) = @_;
+    my $lines = q{};
+
+    $lines .= "+" . ( "-" x ( $first_colwidth + 2 ) ) . "+";
+    $lines .= ( ( "-" x ( $colwidth + 2 ) ) . "+" ) x $datacolno;
+    $lines .= "\n";
+
+    return $lines;
+}
+
+sub datafile_consistency_table {
+
+    # Takes an array of ArrayExpress::Datafile objects.
+    # Returns a string containing a table ready to print, listing the
+    # files associated with the experiment and any missing files.
+    my $filelist = shift;
+
+    my $lines = q{};
+
+    # Skip if no files in the list.
+    return $lines unless ( scalar @$filelist );
+
+    # Get a list of hybs mapped to file types;
+    # get a list of extensions.
+    my $filetypes;
+    my $extensions;
+
+    DATA_FILE:
+    foreach my $file (@$filelist) {
+
+        next DATA_FILE
+            if ( $file->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE() );
+
+        ( my $extension ) = ( $file->get_path() =~ m/\.([^\.]*)$/ );
+	$extension ||= 'UNKNOWN';
+        $extension =~ tr/a-z/A-Z/;
+        if ( -f $file->get_path() ) {
+
+            # everything's rosy
+            $filetypes->{ $file->get_hyb_identifier() }{$extension} = 1;
+        }
+        else {
+
+            # in the DB, not found on disk
+            $filetypes->{ $file->get_hyb_identifier() }{$extension} = -1;
+        }
+        $extensions->{$extension} = 1;
+    }
+
+    # Figure out our column widths
+    my $colwidth  = 12;
+    my $colformat = " %" . $colwidth . "." . $colwidth . "s |";
+
+    my $hybwidth = 0;
+    foreach my $hyb_id ( keys %$filetypes ) {
+        my $hyblength = length($hyb_id);
+        $hybwidth = $hyblength if ( $hyblength > $hybwidth );
+    }
+    my $hybformat = " %" . $hybwidth . "." . $hybwidth . "s |";
+
+    my $num_exts = scalar( grep { defined $_ } values %$extensions );
+
+    # Start drawing the table;
+    # header lines...
+    $lines .= &draw_table_line( $hybwidth, $colwidth, $num_exts );
+    $lines .= sprintf( "|$hybformat", "hyb \\ file extension" );
+    foreach my $ext ( sort keys %$extensions ) {
+        $lines .= sprintf( $colformat, $ext );
+    }
+    $lines .= "\n";
+    $lines .= &draw_table_line( $hybwidth, $colwidth, $num_exts );
+
+    # table content...
+    foreach my $hyb_id ( sort keys %$filetypes ) {
+        $lines .= "|" . sprintf( $hybformat, $hyb_id );
+        foreach my $extension ( sort keys %$extensions ) {
+
+            SWITCH_BY_FILE_EXTENSION:
+            {
+                !$filetypes->{$hyb_id}{$extension} && do {
+                    $lines .= sprintf( $colformat, "NOT UPLOADED" );
+                    last SWITCH_BY_FILE_EXTENSION;
+                };
+                $filetypes->{$hyb_id}{$extension} == 1 && do {
+                    $lines .= sprintf( $colformat, "ok" );
+                    last SWITCH_BY_FILE_EXTENSION;
+                };
+                $filetypes->{$hyb_id}{$extension} == -1 && do {
+                    $lines .= sprintf( $colformat, "FILE LOST" );
+                    last SWITCH_BY_FILE_EXTENSION;
+                };
+            }
+        }
+        $lines .= "\n";
+    }
+
+    # final line...
+    $lines .= &draw_table_line( $hybwidth, $colwidth, $num_exts );
+    $lines .= "\n";
+
+    return $lines;
+}
+
+sub report_accumulated_errors {
+
+    my ( $bad, $output_fh, $error_string ) = @_;
+    my $errorcols;
+
+    foreach my $filename ( keys %$bad ) {
+
+        my $arraystring = join( "\n", sort @{ $bad->{$filename} } );
+        push( @{ $errorcols->{$arraystring} }, $filename );
+
+    }
+
+    foreach my $arraystring ( keys %$errorcols ) {
+        print $output_fh (
+            "$error_string\n\n",
+            $arraystring,
+            "\n\n were found in files:\n\n\t",
+            join( "\n\t", @{ $errorcols->{$arraystring} } ),
+            "\n\n-----------------------------------------------\n\n"
+        );
+    }
+
+    return;
+}
+
+sub format_description {
+
+    # Takes a text string and a filehandle, and outputs the string in
+    # lines of less than 80 chars, splitting words on whitespace.
+
+    my ( $text, $fh ) = @_;
+
+    my $pagewidth = 80;
+
+    # Remove newlines and tabs, and split on spaces
+    $text =~ s/\s+/ /g;
+    my @words = split /[ ]+/, $text;
+
+    # Build lines of <= $pagewidth (80) chars and print them.
+    while (@words) {
+        my $line = shift(@words);
+        while ( @words && ( length( $line . $words[0] ) < $pagewidth ) ) {
+            $line .= " " . shift(@words);
+        }
+        print $fh "$line\n";
+    }
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Curator/Standalone.pm b/lib/ArrayExpress/Curator/Standalone.pm
new file mode 100644
index 0000000..3d83ed5
--- /dev/null
+++ b/lib/ArrayExpress/Curator/Standalone.pm
@@ -0,0 +1,167 @@
+#!/usr/bin/env perl
+#
+# Module to provide basic methods used by the expt_check script.
+#
+# Tim Rayner 2005, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Standalone.pm 1987 2008-03-06 11:29:22Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../index.html">
+                <img src="../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: Standalone.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Curator::Standalone - Standalone experiment checking.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Curator::Standalone;
+ 
+ my $checker =
+   ArrayExpress::Curator::Standalone->new({
+       data_files => \@ARGV,
+   });
+ 
+ $checker->check();
+
+=head1 DESCRIPTION
+
+This module provides the basic operations needed for checking a set of
+data files in full standalone mode. For MIAMExpress and Tab2MAGE
+experiment checking, see L<ArrayExpress::Curator::MIAMExpress> and
+L<ArrayExpress::Curator::Validate>, respectively.
+
+=head1 OPTIONS
+
+The following options must be used in addition to those provided by the
+parent class (see L<ArrayExpress::Curator::ExperimentChecker>):
+
+=over 2
+
+=item C<data_files>
+
+An array reference containing a list of data filenames to be checked.
+
+=back
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2007.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Curator::Standalone;
+
+use strict;
+use warnings;
+
+use Carp;
+use English qw( -no_match_vars );
+use Class::Std;
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+require ArrayExpress::Datafile;
+
+use base qw(ArrayExpress::Curator::ExperimentChecker);
+
+my %data_files  : ATTR( :get<data_files>, :init_arg<data_files>, :default<[]> );
+
+sub START {
+    my ( $self, $id, $args ) = @_;
+
+    unless ( scalar @{ $data_files{$id} } ) {
+	croak("Error: No data files passed to standalone checker.");
+    }
+
+    $self->set_is_standalone(1);
+
+    # Localise to CWD, or value of get_log_to_current_dir.
+    $self->localize_logfiles();
+
+    return;
+}
+
+sub get_files_and_annotation : RESTRICTED {
+
+    my ( $self ) = @_;
+
+    # Full standalone mode; MIAMExpress and Tab2MAGE checking are
+    # handled by overriding this method in their respective
+    # subclasses.
+
+    my $filelist = [];
+    my $hyb_ids  = {};   # Used to map FGEM column headings to hybs.
+    my $norm_ids = {};   # Empty for MX; cannot map MX FGEM using Norm IDs.
+
+    print STDOUT ("Running in Standalone mode...\n");
+
+    $self->logprint( 'error',  "* Standalone mode *\n\n" );
+    $self->logprint( 'report', "* Standalone mode *\n\n" );
+
+    # Set up our list of Datafile objects
+    foreach my $filename ( @{ $self->get_data_files() } ) {
+
+	# We have to set a default file type and array design
+	# id here.
+	my $file = ArrayExpress::Datafile->new({
+	    name            => $filename,
+	    data_type       => 'raw',
+	    array_design_id => $self->get_array_accession() || 'UNKNOWN',
+	});
+
+	push( @$filelist, $file );
+    }
+
+    $self->logprint_line( 'error', 'ADF parsing START' );
+    
+    # Populate the $file->get_adf_features() hashrefs within @$filelist
+    $self->cache_user_supplied_arrays( $filelist );
+    $self->populate_file_arraydesigns( $filelist );
+    
+    $self->logprint_line( 'error', 'ADF parsing END' );
+
+    return ( $filelist, $hyb_ids, $norm_ids );
+}
+
+sub visualize_experiment : RESTRICTED {
+
+    my ( $self ) = @_;
+
+    # Empty method; we can't visualize in such cases.
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Curator/Tab2MAGE.pm b/lib/ArrayExpress/Curator/Tab2MAGE.pm
new file mode 100644
index 0000000..68782e0
--- /dev/null
+++ b/lib/ArrayExpress/Curator/Tab2MAGE.pm
@@ -0,0 +1,2602 @@
+#!/usr/bin/env perl
+#
+# Module to provide basic methods used by the tab2mage script.
+#
+# Tim Rayner 2005, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Tab2MAGE.pm 2086 2008-06-23 10:48:42Z tfrayner $
+#
+
+package ArrayExpress::Curator::Tab2MAGE;
+
+use strict;
+use warnings;
+
+use Carp;
+use File::Spec;
+use File::Path;
+use File::Basename;
+use IO::File;
+use Text::CSV_XS;
+use List::Util qw(first);
+use Readonly;
+use Bio::MAGE qw(:ALL);
+use Class::Std;
+
+use ArrayExpress::Curator::Common qw(
+    date_now
+    find_cdf
+    get_filepath_from_uri
+);
+
+require ArrayExpress::Datafile;
+require ArrayExpress::Datafile::Parser;
+require ArrayExpress::Datafile::DataMatrix;
+
+use ArrayExpress::Curator::Common qw(
+    check_linebreaks
+    $RE_EMPTY_STRING
+    $RE_COMMENTED_STRING
+    $RE_SURROUNDED_BY_WHITESPACE
+    $RE_WITHIN_PARENTHESES
+    $RE_SQUARE_BRACKETS
+);
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+use ArrayExpress::Curator::MAGE qw(
+    write_mage
+    postprocess_mage
+    unique_identifier
+    make_container
+    new_biosource
+    new_biosample
+    new_extract
+    new_immunoprecipitate
+    new_labeledextract
+    new_labelcompound
+    new_image
+    new_physicalbioassay
+    new_measuredbioassay
+    new_measuredbioassaydata
+    new_derivedbioassay
+    new_derivedbioassaydata
+    new_bioassaymap
+    new_quantitationtypemap
+    new_protocol
+    new_parameter
+    new_software
+    new_armanuf
+    new_array
+    new_factorvalue
+    new_experimentalfactor
+    new_quantitationtype
+    new_qtd
+    new_ontologyentry
+    new_person
+);
+
+use ArrayExpress::Curator::MAGE::Definitions qw(
+    validate_experiment_section
+    validate_protocol_section
+    validate_hybridization_section
+
+    $EDF_EXPTACCESSION
+    $EDF_EXPTDOMAIN
+    $EDF_EXPTNAME
+    $EDF_EXPTSUBMITTER
+    $EDF_EXPTDATACODER
+    $EDF_EXPTCURATOR
+    $EDF_EXPTINVESTIGATOR
+    $EDF_EXPTORGANIZATION
+    $EDF_EXPTADDRESS
+    $EDF_PUBAUTHORS
+    $EDF_EXPTSUBMITTER_EMAIL
+    $EDF_EXPTCURATOR_EMAIL
+    $EDF_EXPTDATACODER_EMAIL
+    $EDF_EXPTINVESTIGATOR_EMAIL
+    $EDF_EXPTSECONDARYACCESSION
+    $EDF_EXPTDESCRIPTION
+    $EDF_EXPTRELEASEDATE
+    $EDF_EXPTSUBMISSIONDATE
+    $EDF_EXPTGEORELEASEDATE
+    $EDF_EXPTDESIGNTYPE
+    $EDF_EXPTQUALITYCONTROL
+    $EDF_EXPTCURATEDNAME
+    $EDF_EXPTURI
+
+    $EDF_PUBTITLE
+    $EDF_PUBPAGES
+    $EDF_PUBJOURNAL
+    $EDF_PUBVOLUME
+    $EDF_PUBISSUE
+    $EDF_PUBYEAR
+    $EDF_PUBAUTHORS
+    $EDF_PUBURI
+    $EDF_PUBMEDID
+
+    $EDF_PROTOCOLACCESSION
+    $EDF_PROTOCOLNAME
+    $EDF_PROTOCOLTYPE
+    $EDF_PROTOCOLTEXT
+    $EDF_PROTOCOLPARAMS
+
+    $EDF_PROTOCOLACCESSION
+    $EDF_PROTOCOLPARAMS
+    $EDF_BIOSOURCENAME
+    $EDF_BIOSOURCETYPE
+    $EDF_BIOSOURCEDESCRIPTION
+    $EDF_BIOSAMPLENAME
+    $EDF_BIOSAMPLETYPE
+    $EDF_EXTRACTNAME
+    $EDF_EXTRACTTYPE
+    $EDF_IMMUNOPRECIPITATENAME
+    $EDF_IMMUNOPRECIPITATETYPE
+    $EDF_LABELEDEXTRACTNAME
+    $EDF_LABELEDEXTRACTTYPE
+    $EDF_HYBRIDIZATIONNAME
+    $EDF_SCANNAME
+    $EDF_NORMALIZATIONNAME
+    $EDF_NORMALIZATIONTYPE
+    $EDF_DYE
+
+    $EDF_GROW_PROTOCOL
+    $EDF_TREAT_PROTOCOL
+    $EDF_EXTRACT_PROTOCOL
+    $EDF_POOL_PROTOCOL
+    $EDF_LABEL_PROTOCOL
+    $EDF_IP_PROTOCOL
+    $EDF_HYB_PROTOCOL
+    $EDF_SCAN_PROTOCOL
+    $EDF_FEXT_PROTOCOL
+    $EDF_NORM_PROTOCOL
+
+    $EDF_SCAN_SOFTWARE
+    $EDF_FEXT_SOFTWARE
+    $EDF_NORM_SOFTWARE
+
+    $EDF_IMAGE_FORMAT
+
+    $EDF_ARRAYACCESSION
+    $EDF_ARRAYSERIAL
+
+    $EDF_FILE_PREFIX
+    $EDF_FILE_SUFFIX
+    $EDF_PARAM_PREFIX
+    $EDF_PARAM_SUFFIX
+    $EDF_BMC_PREFIX
+    $EDF_BMC_SUFFIX
+    $EDF_FV_PREFIX
+    $EDF_FV_SUFFIX
+
+    $EDF_SAMPLE_PARAMS
+    $EDF_TREAT_PARAMS
+    $EDF_EXTRACT_PARAMS
+    $EDF_IP_PARAMS
+    $EDF_LABEL_PARAMS
+    $EDF_HYB_PARAMS
+    $EDF_SCAN_PARAMS
+    $EDF_FEXT_PARAMS
+    $EDF_NORM_PARAMS
+
+    $EDF_FEXT_STATS
+    $EDF_NORM_STATS
+
+    $EDF_HYB_HARDWARE
+    $EDF_SCAN_HARDWARE
+    $EDF_HYB_HW_PARAMS
+    $EDF_SCAN_HW_PARAMS
+
+    $OE_CAT_EXPERIMENTDESIGNTYPE
+    $OE_CAT_QUALITYCONTROLDESCRIPTIONTYPE
+    $OE_CAT_PUBLICATIONTYPE
+    $OE_VAL_JOURNALARTICLE
+
+    $OE_VAL_SUBMITTER
+    $OE_VAL_DATA_CODER
+    $OE_VAL_CURATOR
+    $OE_VAL_INVESTIGATOR
+
+    $OE_VAL_GROW
+    $OE_VAL_POOL
+    $OE_VAL_SPECIFIEDBIOMATERIALACTION
+    $OE_VAL_NUCLEICACIDEXTRACTION
+    $OE_VAL_IMMUNOPRECIPITATE
+    $OE_VAL_LABELING
+    $OE_VAL_NORMALIZATION
+    $OE_VAL_HYBRIDIZATION
+    $OE_VAL_SCANNING
+    $OE_VAL_FEATUREEXTRACTION
+    $OE_VAL_SCANNING_SOFTWARE
+    $OE_VAL_ANALYSIS_SOFTWARE
+    $OE_VAL_TRANSFORMATION_SOFTWARE
+
+    $OE_VAL_SYNTHETICRNA
+    $OE_VAL_TOTALRNA
+);
+
+use ArrayExpress::Curator::Logger;
+use base qw(ArrayExpress::Curator::Logger);
+
+# Class::Std attributes
+my %spreadsheet_filename : ATTR( :get<spreadsheet_filename>, :init_arg<spreadsheet_filename> );
+my %target_directory     : ATTR( :name<target_directory>,    :init_arg<target_directory>     );
+my %source_directory     : ATTR( :get<source_directory>,    :init_arg<source_directory>    :default<undef> );
+my %is_standalone        : ATTR( :get<is_standalone>,       :init_arg<is_standalone>,      :default<0> );
+my %qt_filename          : ATTR( :get<qt_filename>,         :init_arg<qt_filename>         :default<undef> );
+my %keep_all_qts         : ATTR( :get<keep_all_qts>,        :init_arg<keep_all_qts>        :default<undef> );
+my %keep_protocol_accns  : ATTR( :get<keep_protocol_accns>, :init_arg<keep_protocol_accns> :default<undef> );
+my %include_default_qts  : ATTR( :get<include_default_qts>, :init_arg<include_default_qts> :default<undef> );
+my %use_plain_text       : ATTR( :get<use_plain_text>,      :init_arg<use_plain_text>      :default<undef> );
+my %reporter_prefix      : ATTR( :get<reporter_prefix>,     :init_arg<reporter_prefix>,    :default<undef> );
+my %compseq_prefix       : ATTR( :get<compseq_prefix>,      :init_arg<compseq_prefix>,     :default<undef> );
+my %external_accession   : ATTR( :get<external_accession>,  :init_arg<external_accession>  :default<undef> );
+my %external_domain      : ATTR( :get<external_domain>,     :init_arg<external_domain>     :default<undef> );
+my %bags                 : ATTR( :name<bags>,         :default<{}>  );
+my %namespace            : ATTR( :name<namespace>,    :default<q{}> );
+my %expt_section         : ATTR( :name<expt_section>, :default<{}>  );
+my %prot_section         : ATTR( :name<prot_section>, :default<[]>  );
+my %hyb_section          : ATTR( :name<hyb_section>,  :default<[]>  );
+my %pool_protocol_assns  : ATTR( :default<{}> );
+my %pool_protocol_usage  : ATTR( :default<{}> );
+my %ignore_size_limits   : ATTR( :name<ignore_size_limits>,  :default<undef> );
+
+###########
+# METHODS #
+###########
+
+sub BUILD {
+    my ( $self, $id, $args ) = @_;
+
+    # Set up the MAGE container hash
+    $self->initialize_bags();
+
+    return;
+}
+
+sub START {
+    my ( $self, $id, $args ) = @_;
+
+    croak("Error: no target_directory set")
+	unless ( $target_directory{$id} );
+ 
+    croak("Error: no spreadsheet_filename set")
+	unless ( $spreadsheet_filename{$id} );
+
+    $self->set_progname( $CONFIG->get_TAB2MAGE_PROGNAME() );
+    $self->set_version( $CONFIG->get_TAB2MAGE_VERSION() );
+
+    # Sort out our log files. N.B. the relevant directory may not have
+    # been created yet, so beware of opening filehandle from these.
+    my $logfile_string = File::Spec->catfile(
+	$self->get_target_directory(),
+	basename($self->get_spreadsheet_filename()),
+    );
+    $logfile_string =~ s/\.\w{3,4}$//;     # strip off the extension
+    my ( $vol, $dir, $name ) = File::Spec->splitpath($logfile_string);
+    $self->localize_logfiles({
+	directory => $dir,
+	volume    => $vol,
+    });
+
+    return;
+}
+
+sub get_readablename {
+
+    my ($self, $datarow) = @_;
+
+    my $readablename = q{};
+
+    FILE_TYPE:
+    foreach my $filetype ( @{ $CONFIG->get_T2M_FILE_TYPES() } )
+    {    # order is important
+        my $key = $EDF_FILE_PREFIX . $filetype . $EDF_FILE_SUFFIX;
+        if ( $datarow->{$key} ) {
+            ($readablename)
+                = ( $datarow->{$key} =~ m{([^/]*?) (?: [.] \w{3})* \z}xms );
+            last FILE_TYPE;    # raw is the default
+        }
+    }
+
+    # Fall back to hyb id if there's no raw or normalized
+    # files. Return value must be defined but can be false.
+    return ($readablename || $datarow->{$EDF_HYBRIDIZATIONNAME} || q{});
+}
+
+sub initialize_bags : PRIVATE {
+    my $self = shift;
+    
+    my %bag_of;
+
+    $bag_of{protocol} = make_container( \&new_protocol, 1 );
+
+    # 1 here indicates that the &new_protocol sub requires pass-through
+    # of the identifier
+
+    $bag_of{parameter}          = make_container( \&new_parameter, 1 );
+    $bag_of{software}           = make_container( \&new_software );
+
+    $bag_of{armanuf}            = make_container( \&new_armanuf );
+    $bag_of{array}              = make_container( \&new_array );
+
+    $bag_of{factorvalue}        = make_container( \&new_factorvalue );
+    $bag_of{experimentalfactor} = make_container( \&new_experimentalfactor );
+
+    $bag_of{biosource}          = make_container( \&new_biosource );
+    $bag_of{biosample}          = make_container( \&new_biosample, 1 );
+    $bag_of{extract}            = make_container( \&new_extract, 1 );
+    $bag_of{immunoprecipitate} = make_container( \&new_immunoprecipitate, 1 );
+    $bag_of{labeledextract}    = make_container( \&new_labeledextract, 1 );
+    $bag_of{labelcompound}     = make_container( \&new_labelcompound );
+    $bag_of{image}             = make_container( \&new_image );
+
+    $bag_of{pba}               = make_container( \&new_physicalbioassay, 1 );
+    $bag_of{mba}               = make_container( \&new_measuredbioassay, 1 );
+    $bag_of{dba}               = make_container( \&new_derivedbioassay, 1 );
+    $bag_of{extended_pba}      = make_container( \&new_physicalbioassay, 1 );
+    $bag_of{datamatrix_mba}    = make_container( \&new_measuredbioassay, 1 );
+    $bag_of{datamatrix_dba}    = make_container( \&new_derivedbioassay, 1 );
+
+    $bag_of{mbad}              = make_container( \&new_measuredbioassaydata );
+    $bag_of{dbad}              = make_container( \&new_derivedbioassaydata );
+    $bag_of{datamatrix_mbad}   = make_container( \&new_measuredbioassaydata );
+    $bag_of{datamatrix_dbad}   = make_container( \&new_derivedbioassaydata );
+
+    $bag_of{bam}               = make_container( \&new_bioassaymap );
+    $bag_of{qtm}               = make_container( \&new_quantitationtypemap );
+
+    $bag_of{quantitationtype}  = make_container( \&new_quantitationtype );
+    $bag_of{qtd}               = make_container( \&new_qtd );
+
+    $bag_of{people}            = make_container( \&new_person );
+
+    $self->set_bags( \%bag_of );
+
+    return \%bag_of;
+}
+
+sub write_mageml {
+    my ($self) = @_;
+
+    # Get a hashref of containers for MAGE objects.
+    my $bag_of = $self->get_bags();
+
+    # Create the target directory.
+    mkpath( [ $self->get_target_directory() ], 0, oct(775) );
+
+    $self->logprint(
+	'tab2mage',
+	sprintf("Parsing spreadsheet %s...\n\n", $self->get_spreadsheet_filename() ),
+    );
+
+    # Make a note if standalone mode was used
+    if ( $self->get_is_standalone() ) {
+	$self->logprint(
+	    'tab2mage',
+	    "Standalone mode. Feature identifiers will be formatted without reference to ArrayExpress.\n"
+	);
+
+	# Potentially catastrophic - not allowed in automated pipelines
+	$self->add_error($CONFIG->get_ERROR_PARSEBAD());
+    }
+
+    # Parse the EDF
+    $self->read_edf();
+
+    #########################
+    # Add the new protocols #
+    #########################
+    $self->create_protocols();
+
+    # Set default experiment accession and name. This must be done after
+    # the protocol accessions have been reassigned, above, and before
+    # further processing.
+    my $exptinfo  = $self->get_expt_section();
+    my $namespace = $self->get_namespace();
+    $exptinfo->{$EDF_EXPTACCESSION} ||= '{UNASSIGNED}';
+    $exptinfo->{$EDF_EXPTNAME}      ||= '{NONE}';
+
+    # This sets up a handler object for DED and QTD-related processing
+    my $parser = ArrayExpress::Datafile::Parser->new({
+	namespace            => "$namespace:$exptinfo->{$EDF_EXPTACCESSION}",
+	include_known_qts    => $self->get_include_default_qts(),
+	quantitation_types   => $self->get_qt_filename(),
+	use_binary_datafiles => ! $self->get_use_plain_text(),
+	is_standalone        => $self->get_is_standalone(),
+	output_directory     => $self->get_target_directory(),
+	source_directory     => $self->get_source_directory(),
+	error_fh             => $self->log_fh('tab2mage'),
+	allow_undef_qts      => $self->get_keep_all_qts(),
+	protocol_bag         => $bag_of->{protocol},
+	parameter_bag        => $bag_of->{parameter},
+	reporter_prefix      => $self->get_reporter_prefix(),
+	compseq_prefix       => $self->get_compseq_prefix(),	
+	ignore_size_limits   => $self->get_ignore_size_limits(),
+    });
+
+    # Initial file  and sanity check
+    $self->_file_sanity_check();
+
+    $self->set_target_directory(
+	File::Spec->rel2abs( $self->get_target_directory() )
+    );
+    my $output_file = File::Spec->catfile(
+	$self->get_target_directory(),
+	"$exptinfo->{$EDF_EXPTACCESSION}.xml"
+    );
+
+    ###################################################
+    # Top-level MAGE object and the Experiment object #
+    ###################################################
+    my $mage = Bio::MAGE->new();
+
+    my ( $experiment, $experimentdesign )
+	= $self->top_level_experiment();
+
+    ###########################
+    # Start Main Datarow Loop #
+    ###########################
+
+    # Hashref for tracking processed files
+    my $file_seen = {};
+
+    # Array of raw/norm data matrices (e.g. Illumina)
+    my @rewritten_data_matrices;
+
+    foreach my $datarow ( @{ $self->get_hyb_section() } ) {
+
+	##############
+	# Data Files #
+	##############
+
+	my @datarow_files;
+
+	# START FILE LOOP
+	# NB. the values determined here will be used in BioAssayData later.
+	FILETYPE:
+	foreach my $filetype ( @{ $CONFIG->get_T2M_FILE_TYPES() } ) {
+
+	    # Skip this file type if not found in data row. Set the
+	    # file name and path.
+	    my $filename
+		= $datarow->{ $EDF_FILE_PREFIX . $filetype . $EDF_FILE_SUFFIX }
+		    or next FILETYPE;
+
+	    my $path = get_filepath_from_uri(
+		$filename,
+		$self->get_source_directory(),
+	    );
+
+	    my $file = ArrayExpress::Datafile->new({
+		path            => $path,
+		name            => basename( $path ),
+		data_type       => $filetype,
+		array_design_id => $datarow->{$EDF_ARRAYACCESSION},
+	    });
+	    
+	    # Process each file just once
+	    next FILETYPE if $file_seen->{ $file->get_name() }++;
+
+	    # Sort out our DesignElementDimension and get some column heading
+	    # info; the $file hashref is populated.  This also generates the
+	    # stripped file for loading
+	    $self->add_error($parser->parse( $file, $datarow ));
+
+	    # Keep non-transformed data matrices for later
+	    if (   $file->get_data_type() eq $CONFIG->get_RAW_DM_FILE_TYPE()
+	        || $file->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE() ) {
+
+		# Process this later, after all the PBA objects have been created.
+		push( @rewritten_data_matrices, [ $file, $datarow ] );
+
+	    }
+	    else {
+
+		# QuantitationType and QuantitationTypeDimension for
+		# per-hyb data.
+		my ( $qtlist, $qtd_key ) = $self->qts_from_names( $file );
+	    
+		my $qtd = $bag_of->{qtd}->(
+		    $qtd_key,
+		    {   qt_list             => $qtlist,
+			identifier_template =>
+			    "$namespace.$exptinfo->{$EDF_EXPTACCESSION}."
+				. unique_identifier
+			    }
+		);
+		$file->set_mage_qtd($qtd);
+
+		# Add to the list of per-hyb data files for later
+		# BioAssay creation.
+		push( @datarow_files, $file );
+	    }
+
+	}
+
+	# END FILE LOOP
+
+	my $identifier_template
+	    = "$namespace:$exptinfo->{$EDF_EXPTACCESSION}." . unique_identifier;
+
+	$self->create_mage_objects(
+	    $datarow, $identifier_template, \@datarow_files
+	);
+
+    }
+
+    # Make sure all pooling protocols are linked to something.
+    my $identifier_template
+	= "$namespace:$exptinfo->{$EDF_EXPTACCESSION}";
+    $self->link_unused_pool_protocols($identifier_template);
+
+    # END MAIN DATAROW LOOP
+
+    ######################
+    # DataMatrix parsing #
+    ######################
+
+    # Measured DMs first.
+    foreach my $matrix (@rewritten_data_matrices) {
+
+	my ($file, $datarow) = @$matrix;
+
+	# Some raw files may be DataMatrix-style, e.g. Illumina.
+	my $identifier_template
+	    = "$namespace:$exptinfo->{$EDF_EXPTACCESSION}";
+	
+	# FIXME add support for MeasuredBioAssay creation here.
+	my $dm = ArrayExpress::Datafile::DataMatrix->new();
+	$dm->create_mage( $file, $datarow, $self, $identifier_template );
+    }
+
+    # Loop over the Hybridization section again, mapping DataMatrix bioassays to
+    # those already created in $bag_of.
+    DATAROW:
+    foreach my $datarow ( @{ $self->get_hyb_section() } ) {
+
+	my $filename = $datarow->{
+	    $EDF_FILE_PREFIX
+	  . $CONFIG->get_FGEM_FILE_TYPE()
+          . $EDF_FILE_SUFFIX
+        }
+	    or next DATAROW;
+
+	my $path;
+	if ( my $dir = $self->get_source_directory() ) {
+	    $path = File::Spec->catfile( $dir, $filename );
+	}
+	else {
+	    $path = File::Spec->rel2abs($filename);
+	}
+
+	my $file = ArrayExpress::Datafile->new({
+	    path            => $path,
+	    name            => ( File::Spec->splitpath($path) )[2],
+	    data_type       => $CONFIG->get_FGEM_FILE_TYPE(),
+	    array_design_id => $datarow->{$EDF_ARRAYACCESSION},
+	});
+
+	# Do each file just once
+	next DATAROW if $file_seen->{ $file->get_name() }++;
+
+	# Sort out our DesignElementDimension and get some column heading
+	# info; the $file hashref is populated.  This also generates the
+	# stripped file for loading.
+
+	$self->add_error($parser->parse( $file, $datarow ));
+
+	my $identifier_template = "$namespace:$exptinfo->{$EDF_EXPTACCESSION}";
+
+	my $dm = ArrayExpress::Datafile::DataMatrix->new();
+	$dm->create_mage( $file, $datarow, $self, $identifier_template );
+	
+    }
+
+    # END DataMatrix parsing
+
+    # Add references to all BioAssays and BioAssayData to the Experiment object
+    foreach my $ba_type qw(pba mba dba extended_pba datamatrix_mba datamatrix_dba) {
+	if ( @{ $bag_of->{$ba_type}->() } ) {
+	    $experiment->addBioAssays( @{ $bag_of->{$ba_type}->() } );
+	}
+    }
+    foreach my $ba_data_type qw(mbad dbad datamatrix_mbad datamatrix_dbad) {
+	if ( @{ $bag_of->{$ba_data_type}->() } ) {
+	    $experiment->addBioAssayData( @{ $bag_of->{$ba_data_type}->() } );
+	}
+    }
+
+    # Insert the FactorValues (may be redundant FIXME)
+    $experimentdesign->setExperimentalFactors( $bag_of->{experimentalfactor}->() );
+
+    # This next line uses MAGE1.1 features if available, otherwise 1.0
+    my $mage_version;    # used to set the correct PUBLIC ID in the output
+    if ( eval { $experiment->setExperimentDesigns( [$experimentdesign] ) } ) {
+	$mage_version = "1.1";
+    }
+    else {
+	$experiment->setExperimentDesign($experimentdesign);
+	$mage_version = "1.0";
+    }
+
+    # Insert the top-level Experiment object into our MAGE container
+    $mage->add_objects( [$experiment] );
+
+    # Insert all the objects stored in our containers
+    foreach my $bag ( values %$bag_of ) {
+	$mage->add_objects( $bag->() );
+    }
+
+    my ( $ded_fh, $dummy_id );
+    if ( $parser->get_ded_count() ) {
+
+	$ded_fh = $parser->get_feature_fh();
+
+	# Create a placeholder for the insertion of DEDs
+	$dummy_id = 'DUMMY';
+	my $dummy_FD = Bio::MAGE::BioAssayData::FeatureDimension->new(
+	    identifier => $dummy_id,
+	    name       => $dummy_id,
+	);
+	$mage->add_objects( [$dummy_FD] );
+    }
+    else {
+	print STDOUT ("No DesignElementDimension found.\n");
+    }
+
+    # Construct the MAGE structure
+    my $mage_identifier = sprintf(
+	"MAGE:%s:%s v%s:%s:%s",
+	$exptinfo->{$EDF_EXPTDOMAIN},
+	$CONFIG->get_TAB2MAGE_PROGNAME(),
+	$CONFIG->get_TAB2MAGE_VERSION(),
+	$exptinfo->{$EDF_EXPTACCESSION},
+	$exptinfo->{$EDF_EXPTNAME}
+    );
+    $mage->identifier( $mage_identifier );
+
+    # Write out everything but the DED
+    print STDOUT ("Writing out initial MAGE-ML...\n");
+    my $mage_fh = IO::File->new_tmpfile;
+    write_mage( $mage, $mage_fh, 'tab delimited', $mage_version );
+
+    # Rewind the temporary filehandle
+    seek( $mage_fh, 0, 0 );
+
+    # Fix the DED using classical text processing (lower memory overhead)
+    print STDOUT ("Inserting DesignElementDimension where found...\n");
+    my $output_fh = IO::File->new( $output_file, '>' )
+	or die("Error: cannot open output file $output_file: $!\n");
+
+    postprocess_mage(
+	$ded_fh,
+	$mage_fh,
+	$output_fh,
+	$dummy_id,
+	$parser->get_cel_types(),
+    );
+
+    return wantarray
+	? ( $mage, $exptinfo->{$EDF_EXPTACCESSION}, $self->get_error() )
+	: $mage ;
+}
+
+sub create_mage_objects {
+
+    my ( $self, $datarow, $identifier_template, $datarow_files )
+        = @_;
+
+    my $bag_of    = $self->get_bags();
+    my $namespace = $self->get_namespace();
+    my $exptinfo  = $self->get_expt_section();
+
+    # To be used in names. Raw filename minus extensions used as default
+    my $readablename = $self->get_readablename($datarow);
+
+    ################
+    # FactorValues #
+    ################
+    my @fvlist;
+
+    # Sort here is important for $readablename consistency
+    DATAROW_COLUMN:
+    foreach my $key ( sort keys %$datarow ) {
+
+        # Skip empty values (i.e. null factorvalues are not created;
+        # zeroes are allowed).
+        next DATAROW_COLUMN
+            if ( !defined( $datarow->{$key} )
+            || $datarow->{$key} =~ $RE_EMPTY_STRING );
+
+        my $regexp_str = "$EDF_FV_PREFIX(.*)$EDF_FV_SUFFIX";
+        $regexp_str =~ s{$RE_SQUARE_BRACKETS}{\\$1}gxms;
+
+        # Strict matching should be okay here; we've processed the
+        # headings in read_edf.
+        if ( ( my $category ) = ( $key =~ m/^$regexp_str$/ ) ) {
+
+            # Most of this next line is now non-functional, as we're no
+            # longer implicitly creating null FVs. Kept for reference
+            # purposes.
+            my $value =
+                ( defined( $datarow->{$key} ) && $datarow->{$key} ne q{} )
+                ? $datarow->{$key}
+                : 'null';    # This should take care of null factor values
+
+            my ( $fv, $ef ) = $bag_of->{factorvalue}->(
+
+		# This is used for tracking FVs internally.
+                "$category.$value",
+                {   category        => $category,
+                    value           => $value,
+                    namespace       => $namespace,
+                    exptaccession   => $exptinfo->{$EDF_EXPTACCESSION},
+		    expt_factor_bag => $bag_of->{experimentalfactor},
+                },
+            );
+
+            push( @fvlist, $fv );
+        }
+    }
+
+    ###############################
+    # Set up the pooling protocol #
+    ###############################
+    my $pooling_protocol;
+    if ( $datarow->{$EDF_POOL_PROTOCOL} ) {
+        $pooling_protocol = $bag_of->{protocol}->(
+            $datarow->{$EDF_POOL_PROTOCOL},
+            { protocol_type => $OE_VAL_POOL }
+        );
+    }
+
+    ################
+    ################
+    # BioMaterials #
+    ################
+    ################
+
+    #############
+    # BioSource #
+    #############
+    my ( $biosource, @characteristics, $tracker_key, $biosource_name );
+
+    # Sort here is important for BioSource tracking.
+    DATAROW_COLUMN:
+    foreach my $key ( sort keys %$datarow ) {
+
+        # Skip empty columns. Note that a zero is not empty.
+        next DATAROW_COLUMN
+            unless ( defined( $datarow->{$key} )
+            && ( $datarow->{$key} ne q{} ) );
+
+        my $regexp_str = "$EDF_BMC_PREFIX(.*)$EDF_BMC_SUFFIX";
+        $regexp_str =~ s{$RE_SQUARE_BRACKETS}{\\$1}gxms;
+
+        if ( ( my $category ) = ( $key =~ m/^$regexp_str$/ ) ) {
+
+            my $bmc = new_ontologyentry(
+                {   category => $category,
+                    value    => $datarow->{$key}
+                }
+            );
+            push( @characteristics, $bmc );
+            $tracker_key .= $datarow->{$key};
+        }
+    }
+
+    if ( $datarow->{$EDF_BIOSOURCENAME} ) { # If we're given a name, we use it
+
+        $biosource_name = $tracker_key = $datarow->{$EDF_BIOSOURCENAME};
+
+    }
+    else {    # Otherwise we assume that the combination of
+              # BioMaterialCharacteristics defines a unique BioSource,
+              # and make up a name for it.
+
+        $biosource_name = "BioSource: " . ( $readablename || q{} );
+
+    }
+
+    # Only actually define a biosource if it's given in this datarow
+    if (   $tracker_key
+        || $datarow->{$EDF_BIOSOURCETYPE} ) {
+        $biosource = $bag_of->{biosource}->(
+            ( $tracker_key || $biosource_name ), # Fall back to name if no BMC
+            {   material_type       => $datarow->{$EDF_BIOSOURCETYPE},
+                characteristics     => \@characteristics,
+                identifier_template => $identifier_template,
+                name                => $biosource_name,
+		description         => $datarow->{$EDF_BIOSOURCEDESCRIPTION},
+            }
+        );
+    }
+
+    ##########
+    # Sample #
+    ##########
+    my ( $biosample, $biosample_name );
+    if (   $datarow->{$EDF_BIOSAMPLENAME}
+        || $datarow->{$EDF_GROW_PROTOCOL}
+        || $datarow->{$EDF_TREAT_PROTOCOL}
+        || $datarow->{$EDF_POOL_PROTOCOL}
+        || $datarow->{$EDF_BIOSAMPLETYPE} ) {
+
+        # Protocol and Parameter arrays have to be kept in sync.
+        my $sample_protocols  = [];
+        my $sample_parameters = [];
+        $datarow->{$EDF_GROW_PROTOCOL} && do {
+            push(
+                @$sample_protocols,
+                $bag_of->{protocol}->(
+                    $datarow->{$EDF_GROW_PROTOCOL},
+                    { protocol_type => $OE_VAL_GROW }
+                )
+            );
+            push( @$sample_parameters,
+                ( $datarow->{$EDF_SAMPLE_PARAMS} || {} ) );
+        };
+        $datarow->{$EDF_TREAT_PROTOCOL} && do {
+            push(
+                @$sample_protocols,
+                $bag_of->{protocol}->(
+                    $datarow->{$EDF_TREAT_PROTOCOL},
+                    { protocol_type => $OE_VAL_SPECIFIEDBIOMATERIALACTION }
+                )
+            );
+            push( @$sample_parameters,
+                ( $datarow->{$EDF_TREAT_PARAMS} || {} ) );
+        };
+
+        $biosample_name = $datarow->{$EDF_BIOSAMPLENAME}
+            || $biosource_name
+            || "Sample: $readablename";
+        $biosample = $bag_of->{biosample}->(
+            $biosample_name,
+            {   derived_from        => $biosource,
+                material_type       => $datarow->{$EDF_BIOSAMPLETYPE},
+                protocols           => $sample_protocols,
+                parameters          => $sample_parameters,
+                parameter_bag       => $bag_of->{parameter},
+                identifier_template => $identifier_template,
+                pooling             => $pooling_protocol,
+            }
+        );
+
+	# Check whether we need to cache the pooling protocol.
+	if ( $pooling_protocol ) {
+	    $self->check_pool_protocol_usage(
+		$pooling_protocol,
+		$biosample,
+	    );
+	}
+    }
+
+    ###########
+    # Extract #
+    ###########
+    my ( $extract, $extract_name );
+    if (   $datarow->{$EDF_EXTRACTNAME}
+        || $datarow->{$EDF_EXTRACT_PROTOCOL}
+        || $datarow->{$EDF_EXTRACTTYPE} ) {
+        my $extract_protocols = [];
+        $datarow->{$EDF_EXTRACT_PROTOCOL} && do {
+            push(
+                @$extract_protocols,
+                $bag_of->{protocol}->(
+                    $datarow->{$EDF_EXTRACT_PROTOCOL},
+                    { protocol_type => $OE_VAL_NUCLEICACIDEXTRACTION }
+                )
+            );
+        };
+               $extract_name = $datarow->{$EDF_EXTRACTNAME}
+            || $biosample_name
+            || $biosource_name
+            || "Extract: $readablename";
+        $extract = $bag_of->{extract}->(
+            $extract_name,
+
+            # Degrade gracefully...
+            {   derived_from => ( $biosample || $biosource ),
+                material_type => $datarow->{$EDF_EXTRACTTYPE},
+                protocols     => $extract_protocols,
+                parameters    => [ $datarow->{$EDF_EXTRACT_PARAMS} || {} ],
+                parameter_bag => $bag_of->{parameter},
+                identifier_template => $identifier_template,
+                pooling             => $pooling_protocol
+            }
+        );
+
+	# Check whether we need to cache the pooling protocol.
+	if ( $pooling_protocol ) {
+	    $self->record_pool_protocol_assn(
+		$pooling_protocol,
+		$extract,
+	    );
+	    $self->check_pool_protocol_usage(
+		$pooling_protocol,
+		$extract,
+	    );
+	}
+    }
+
+    #####################################
+    # Immunoprecipitate (ChIP-specific) #
+    #####################################
+    my ( $labeled_extract, $ip );
+    if (   $datarow->{$EDF_IMMUNOPRECIPITATENAME}
+        || $datarow->{$EDF_IP_PROTOCOL}
+        || $datarow->{$EDF_IMMUNOPRECIPITATETYPE} ) {
+        my $ip_protocols = [];
+        $datarow->{$EDF_IP_PROTOCOL} && do {
+            push(
+                @$ip_protocols,
+                $bag_of->{protocol}->(
+                    $datarow->{$EDF_IP_PROTOCOL},
+                    { protocol_type => $OE_VAL_IMMUNOPRECIPITATE }
+                )
+            );
+        };
+
+        my $ip_name;
+        unless ( $ip_name = $datarow->{$EDF_IMMUNOPRECIPITATENAME} ) {
+                   $ip_name = $extract_name
+                || $biosample_name
+                || $biosource_name
+                || $readablename;
+            $ip_name .= ' IP';
+        }
+
+        $ip = $bag_of->{immunoprecipitate}->(
+            $ip_name,
+
+            # Degrade gracefully...
+            {   derived_from => ( $extract || $biosample || $biosource ),
+                material_type       => $datarow->{$EDF_IMMUNOPRECIPITATETYPE},
+                protocols           => $ip_protocols,
+                parameters          => [ $datarow->{$EDF_IP_PARAMS} || {} ],
+                parameter_bag       => $bag_of->{parameter},
+                identifier_template => $identifier_template,
+                pooling             => $pooling_protocol
+            }
+        );
+
+	# Check whether we need to cache the pooling protocol.
+	if ( $pooling_protocol ) {
+	    $self->check_pool_protocol_usage(
+		$pooling_protocol,
+		$ip,
+	    );
+	}
+
+        ########################
+        # LabeledExtracts (IP) #
+        ########################
+        if (   $datarow->{$EDF_LABELEDEXTRACTNAME}
+            || $datarow->{$EDF_LABEL_PROTOCOL}
+            || $datarow->{$EDF_LABELEDEXTRACTTYPE} ) {
+            my $labeled_extract_protocols = [];
+            $datarow->{$EDF_LABEL_PROTOCOL} && do {
+                push(
+                    @$labeled_extract_protocols,
+                    $bag_of->{protocol}->(
+                        $datarow->{$EDF_LABEL_PROTOCOL},
+                        { protocol_type => $OE_VAL_LABELING }
+                    )
+                );
+            };
+            my $labeled_extract_name = $datarow->{$EDF_LABELEDEXTRACTNAME}
+                || "$ip_name $datarow->{$EDF_DYE}";
+            $labeled_extract = $bag_of->{labeledextract}->(
+                $labeled_extract_name,
+
+                # We know we have $ip...
+                {   derived_from  => $ip,
+                    material_type => $datarow->{$EDF_LABELEDEXTRACTTYPE},
+                    protocols     => $labeled_extract_protocols,
+                    parameters    => [ $datarow->{$EDF_LABEL_PARAMS} || {} ],
+                    parameter_bag => $bag_of->{parameter},
+                    compound_bag  => $bag_of->{labelcompound},
+                    identifier_template => $identifier_template,
+                    dye                 => $datarow->{$EDF_DYE},
+                    pooling             => $pooling_protocol
+                }
+            );
+        }
+
+    }
+    else {
+
+        ############################
+        # LabeledExtracts (normal) #
+        ############################
+        if (   $datarow->{$EDF_LABELEDEXTRACTNAME}
+            || $datarow->{$EDF_LABEL_PROTOCOL}
+            || $datarow->{$EDF_LABELEDEXTRACTTYPE} ) {
+            my $labeled_extract_protocols = [];
+            $datarow->{$EDF_LABEL_PROTOCOL} && do {
+                push(
+                    @$labeled_extract_protocols,
+                    $bag_of->{protocol}->(
+                        $datarow->{$EDF_LABEL_PROTOCOL},
+                        { protocol_type => $OE_VAL_LABELING }
+                    )
+                );
+            };
+
+            my $labeled_extract_name;
+            unless ( $labeled_extract_name = $datarow->{$EDF_LABELEDEXTRACTNAME} ) {
+                       $labeled_extract_name = $extract_name
+                    || $biosample_name
+                    || $biosource_name
+                    || "Label: $readablename";
+                $labeled_extract_name .= " $datarow->{$EDF_DYE}"
+                    if $datarow->{$EDF_DYE};
+            }
+
+            $labeled_extract = $bag_of->{labeledextract}->(
+                $labeled_extract_name,
+                {   derived_from => ( $extract || $biosample || $biosource ),
+                    material_type => $datarow->{$EDF_LABELEDEXTRACTTYPE},
+                    protocols     => $labeled_extract_protocols,
+                    parameters    => [ $datarow->{$EDF_LABEL_PARAMS} || {} ],
+                    parameter_bag => $bag_of->{parameter},
+                    compound_bag  => $bag_of->{labelcompound},
+                    identifier_template => $identifier_template,
+                    dye                 => $datarow->{$EDF_DYE},
+                    pooling             => $pooling_protocol
+                }
+            );
+        }
+    }
+
+    # Check whether we need to cache the pooling protocol.
+    if ( $pooling_protocol ) {
+	$self->check_pool_protocol_usage(
+	    $pooling_protocol,
+	    $labeled_extract,
+	);
+    }
+
+    #########
+    # Array #
+    #########
+    my $array;
+    if ( $datarow->{$EDF_ARRAYACCESSION} ) {
+
+        $array = $bag_of->{array}->(
+            ($datarow->{$EDF_HYBRIDIZATIONNAME} || $readablename),
+            {   identifier_template => $identifier_template,
+                arrayaccession      => $datarow->{$EDF_ARRAYACCESSION},
+                serial              => $datarow->{$EDF_ARRAYSERIAL},
+		armanuf_bag         => $bag_of->{armanuf},
+            },
+        );
+    }
+
+    # END Array
+
+    #############################
+    #############################
+    # BioAssayData and BioAssay #
+    #############################
+    #############################
+
+    # NB uses information from the datafile parsing at the start of the
+    # datarow loop. Declared here so DBA[D] can map to MBA[D]
+    my $mbad;
+
+    # These default to raw filename if available. $hyb_name is used as
+    # both internal PBA id and PBA name attribute.
+    my $hyb_name = $datarow->{$EDF_HYBRIDIZATIONNAME} || $readablename;
+
+    # $scan_name is used as the internal id of the MBA object.
+    my $scan_name;
+    if ( $datarow->{$EDF_SCANNAME} ) {
+	$scan_name = $datarow->{$EDF_SCANNAME};
+    }
+    elsif ( my $hname = $datarow->{$EDF_HYBRIDIZATIONNAME} ) {
+
+	# These fallbacks are required for accurate raw datamatrix
+	# (e.g. Illumina) processing.
+	if ( my $fname
+	     = $datarow->{ $EDF_FILE_PREFIX . 'raw' . $EDF_FILE_SUFFIX } ) {
+	    $scan_name = "$hname $fname";
+	}
+	else {
+	    $scan_name = $hname;
+	}
+    }
+    else {
+	$scan_name = $datarow->{ $EDF_FILE_PREFIX . 'raw' . $EDF_FILE_SUFFIX };
+    }
+
+    # $mba_name is used separately as the MBA name attribute
+    # (displayed in AE web interface, so needs to be kept simple).
+    my $mba_name = $datarow->{$EDF_SCANNAME} || $hyb_name;
+
+    # $norm_name is both the internal ID of the DBA and the DBA name attribute.
+    my $norm_name = $datarow->{$EDF_NORMALIZATIONNAME}
+        || $datarow->{ $EDF_FILE_PREFIX . 'normalized' . $EDF_FILE_SUFFIX };
+
+    ####################################
+    # PhysicalBioAssay (Hybridization) #
+    ####################################
+
+    my $pba;
+
+    # Only do this if hybname available, otherwise we risk null
+    # container identifiers.
+    if ($hyb_name) {
+        my ( $hyb_protocol, $scan_protocol );
+        $datarow->{$EDF_HYB_PROTOCOL} && do {
+            $hyb_protocol = $bag_of->{protocol}->(
+                $datarow->{$EDF_HYB_PROTOCOL},
+                { protocol_type => $OE_VAL_HYBRIDIZATION }
+            );
+        };
+
+        # If both scan and fext protocols available, add scanning
+        # here. Otherwise attach scanning to feature_extraction below.
+        ( $datarow->{$EDF_SCAN_PROTOCOL} && $datarow->{$EDF_FEXT_PROTOCOL} )
+            && do {
+            my $ptype = $OE_VAL_SCANNING;
+            $scan_protocol = $bag_of->{protocol}->(
+                $datarow->{$EDF_SCAN_PROTOCOL},
+                { protocol_type => $ptype }
+            );
+            };
+        my $scan_software         = q{};
+        my $scan_software_version = q{};
+
+        # If we've got a FEXT protocol then all well and good. If not, we
+        # don't need a image_acquisition_software here since it will be
+        # added as feature_extraction_software below.
+        ( $datarow->{$EDF_SCAN_SOFTWARE} && $datarow->{$EDF_FEXT_PROTOCOL} )
+            && do {
+            my $name;
+            ( $name, $scan_software_version )
+                = $self->parse_software( $datarow->{$EDF_SCAN_SOFTWARE} );
+            $scan_software = $bag_of->{software}->(
+                "SCAN:$name"
+                    . (
+                    $scan_software_version ? ":$scan_software_version" : q{}
+                    ),
+                {   identifier_template =>
+                        "$identifier_template.ImageAcquisition",
+                    name => $name,
+                    type => $OE_VAL_SCANNING_SOFTWARE
+                }
+            );
+            };
+        my $image;
+        my $image_data
+            = $datarow->{ $EDF_FILE_PREFIX . 'image' . $EDF_FILE_SUFFIX };
+        if ($image_data) {
+            $image = $bag_of->{image}->(
+                $image_data,
+                {   identifier_template => $identifier_template,
+                    uri                 => $image_data,
+                    format              => $datarow->{$EDF_IMAGE_FORMAT}
+                }
+            );
+        }
+
+	my $label;
+	if ( $labeled_extract && $labeled_extract->getLabels() ) {
+
+	    # Assume only one dye per label (works for Tab2MAGE; not
+	    # so for MAGE-TAB).
+	    $label = $labeled_extract->getLabels()->[0];
+	}
+
+        $pba = $bag_of->{pba}->(
+            $hyb_name,
+            {   derived_from =>
+		    ( $labeled_extract || $ip || $extract || $biosample || $biosource ),
+		label                    => $label,
+                array                    => $array,
+                identifier_template      => $identifier_template,
+                factor_values            => \@fvlist,
+                hybridization_protocol   => $hyb_protocol,
+                hybridization_parameters => $datarow->{$EDF_HYB_PARAMS},
+                scanning_protocol        => $scan_protocol,
+                scanning_parameters      => $datarow->{$EDF_SCAN_PARAMS},
+                parameter_bag            => $bag_of->{parameter},
+                software                 => $scan_software,
+                software_version         => $scan_software_version,
+                image                    => $image,
+                hyb_hardware             => $datarow->{$EDF_HYB_HARDWARE},
+                hyb_hardware_params      => $datarow->{$EDF_HYB_HW_PARAMS},
+                scan_hardware            => $datarow->{$EDF_SCAN_HARDWARE},
+                scan_hardware_params     => $datarow->{$EDF_SCAN_HW_PARAMS},
+		extended_pba_bag         => $bag_of->{extended_pba},
+		mba_bag                  => $bag_of->{mba},
+            }
+        );
+    }    # end if $hybname
+
+    # Handle the two-colour hyb coding. Assumes that merged PBAs have
+    # a .merged name suffix.
+    if ( $pba && ( scalar @{ $pba->getChannels() || [] } > 1 ) ) {
+	my $pba_name = $pba->getName();
+	$pba = $bag_of->{extended_pba}->(
+	    "$hyb_name.merged"
+	);
+	unless ( $pba ) {
+	    die(sprintf(
+		"Internal error: Merged PBA undefined for two-color coding (%s).",
+		$pba_name)
+	    );
+	}
+    }
+
+    ####################
+    # MeasuredBioAssay #
+    ####################
+    my $mba;
+    if ($scan_name) {
+
+        my $fext_protocol;
+        my $fext_protocol_accession;
+        (          $fext_protocol_accession = $datarow->{$EDF_FEXT_PROTOCOL}
+                || $datarow->{$EDF_SCAN_PROTOCOL} )
+            && do {
+            $fext_protocol = $bag_of->{protocol}->(
+                $fext_protocol_accession,
+                { protocol_type => $OE_VAL_FEATUREEXTRACTION }
+            );
+            };
+        my $fext_software         = q{};
+        my $fext_software_version = q{};
+        my $fext_sw_string        = q{};
+
+        # If FEXT software not available, we use SCAN software (see above
+        # for scanning software treatment).
+        (          $fext_sw_string = $datarow->{$EDF_FEXT_SOFTWARE}
+                || $datarow->{$EDF_SCAN_SOFTWARE} )
+            && do {
+            my $name;
+            ( $name, $fext_software_version )
+                = $self->parse_software($fext_sw_string);
+            $fext_software = $bag_of->{software}->(
+                "FEXT:$name"
+                    . (
+                    $fext_software_version ? ":$fext_software_version" : q{}
+                    ),
+                {   identifier_template =>
+                        "$identifier_template.FeatureExtraction",
+                    name => $name,
+                    type => $OE_VAL_ANALYSIS_SOFTWARE
+                }
+            );
+            };
+        $mba = $bag_of->{mba}->(
+            $scan_name,
+            {   name                => $mba_name,
+		physical_bioassay   => $pba,
+                factor_values       => \@fvlist,
+                identifier_template => $identifier_template,
+                protocol            => $fext_protocol,
+                parameters          => $datarow->{$EDF_FEXT_PARAMS},
+                parameter_bag       => $bag_of->{parameter},
+                software            => $fext_software,
+                software_version    => $fext_software_version
+            }
+        );
+
+    }    # end if $scan_name
+
+    ###################
+    # DerivedBioAssay #
+    ###################
+    my $dba;
+    if ($norm_name) {
+
+        # Allow norm without raw bioassay.
+        my $source_bioassays = $mba ? [$mba] : undef;
+
+        # BioAssayMap (below) and DBA both have the same $norm_name
+        # internal identifier.
+        $dba = $bag_of->{dba}->(
+            $norm_name,
+            {   factor_values       => \@fvlist,
+                identifier_template => $identifier_template,
+                normalization_type  => $datarow->{$EDF_NORMALIZATIONTYPE},
+                source_bioassays    => $source_bioassays,
+		bioassaymap_bag     => $bag_of->{bam},
+            },
+        );
+    }    # end if $norm_name (dbad added below)
+
+    ################
+    # BioAssayData #
+    ################
+
+    DATAROW_FILE:
+    foreach my $file ( @{ $datarow_files || [] } ) {
+
+        # Skip if there's no filename.
+        next DATAROW_FILE unless $file->get_name();
+
+        my $de_dimension;
+
+        DESIGN_ELEMENT_DIMENSION:
+        {
+
+            last DESIGN_ELEMENT_DIMENSION unless ( $file->get_ded_type() );
+
+            my $dim_class
+                = 'Bio::MAGE::BioAssayData::'
+		. $file->get_ded_type()
+		. 'Dimension';
+
+            eval {
+                $de_dimension = $dim_class->new(
+                    identifier => $file->get_ded_identifier() );
+                }
+                or croak(
+                "Error: Unrecognized DED type: "
+	        . $file->get_ded_type()
+		. "\n" );
+
+            # If we've already seen this file once, $de_dimension will end
+            # up empty and ignored.
+
+        }
+
+        if ( $file->get_data_type() eq 'raw' ) {
+
+            ########################
+            # MeasuredBioAssayData #
+            ########################
+            $mbad = $bag_of->{mbad}->(
+                $file->get_name(),
+                {   filename           => $file->get_target_filename(),
+                    feature_dimension  => $de_dimension,
+                    summary_statistics => $datarow->{$EDF_FEXT_STATS},
+                    quantitation_type_dimension => $file->get_mage_qtd(),
+                    identifier_template         => $identifier_template
+                }
+            );
+
+            # Add mbad to the mba we created earlier
+            my $found;
+            if ( my $preexisting_mbads = $mba->getMeasuredBioAssayData ) {
+                my $new_identifier = $mbad->getIdentifier;
+                $found = first { $_->getIdentifier eq $new_identifier }
+                    @$preexisting_mbads;
+            }
+            $mba->addMeasuredBioAssayData($mbad) unless $found;
+
+            #####################
+            # BioAssayDimension #
+            #####################
+            my $bad = Bio::MAGE::BioAssayData::BioAssayDimension->new(
+                identifier =>
+                    "$identifier_template.MeasuredBioAssayDimension",
+                bioAssays => [$mba],
+            );
+            $mbad->setBioAssayDimension($bad)
+                unless $mbad->getBioAssayDimension();
+
+        }
+        elsif ( $file->get_data_type() eq 'normalized' ) {
+
+            #######################
+            # DerivedBioAssayData #
+            #######################
+            my $protocol;
+            $datarow->{$EDF_NORM_PROTOCOL} && do {
+                $protocol = $bag_of->{protocol}->(
+                    $datarow->{$EDF_NORM_PROTOCOL},
+                    { protocol_type => $OE_VAL_NORMALIZATION }
+                );
+            };
+
+            my $norm_software    = q{};
+            my $software_version = q{};
+            $datarow->{$EDF_NORM_SOFTWARE} && do {
+                my $name;
+                ( $name, $software_version )
+                    = $self->parse_software( $datarow->{$EDF_NORM_SOFTWARE} );
+                $norm_software = $bag_of->{software}->(
+                    "NORM:$name"
+                        . ( $software_version ? ":$software_version" : q{} ),
+                    {   identifier_template =>
+                            "$identifier_template.Normalization",
+                        name => $name,
+                        type => $OE_VAL_TRANSFORMATION_SOFTWARE
+                    }
+                );
+            };
+
+	    # BioAssayMap and DBA both have the same $norm_name
+	    # internal identifier. Omit this if there are no MBAs (no
+	    # mapping necessary, and the BAMs will be empty and
+	    # dropped from the output MAGE-ML).
+            my @bams;
+	    my $bam = $bag_of->{bam}->($norm_name);
+	    if ( $mba && $bam ) {
+
+		# same name used here and for DBA above
+		push @bams, $bam;
+	    }
+
+            # BioAssayDimension
+            my $bad = Bio::MAGE::BioAssayData::BioAssayDimension->new(
+                identifier => "$identifier_template.DerivedBioAssayDimension",
+                bioAssays  => [$dba],
+            );
+
+            # Source QTs for QTMapping
+            my %source_qts;
+            if ( $mbad
+                && ( my $source_qtd = $mbad->getQuantitationTypeDimension ) )
+            {
+                foreach my $qt ( @{ $source_qtd->getQuantitationTypes } ) {
+                    $source_qts{ $qt->getIdentifier } = $qt;
+                }
+            }
+
+            my $source_bioassays
+                = $mbad ? [$mbad] : undef;    # Allow norm data with no raw
+            my $dbad = $bag_of->{dbad}->(
+                $file->get_name(),
+                {   filename                => $file->get_target_filename(),
+                    de_dimension            => $de_dimension,
+                    qt_dimension            => $file->get_mage_qtd(),
+                    ba_dimension            => $bad,
+                    identifier_template     => $identifier_template,
+                    protocol                => $protocol,
+                    parameters              => $datarow->{$EDF_NORM_PARAMS},
+                    parameter_bag           => $bag_of->{parameter},
+                    summary_statistics      => $datarow->{$EDF_NORM_STATS},
+                    source_bioassay_dataset => $source_bioassays,
+                    bioassay_map            => \@bams,
+                    software                => $norm_software,
+                    software_version        => $software_version,
+                    source_qts              => [ sort values %source_qts ],
+                    qtm_bag                 => $bag_of->{qtm},
+                }
+            );
+
+            # Add dbad to the dba we created earlier
+            $self->dba_add_data( $dba, $dbad );
+
+        }
+        else {
+
+            croak(    "Error: Unsupported file type: "
+                    . $file->get_data_type()
+                    . "\n" );
+
+        }
+    }
+
+    return $bag_of;
+}
+
+sub dba_add_data {
+
+    my ( $self, $dba, $dbad ) = @_;
+
+    # Check that the DBAD is not already present, add if not.
+    my $found;
+    if ( my $preexisting_dbads = $dba->getDerivedBioAssayData ) {
+        my $new_identifier = $dbad->getIdentifier;
+        $found = first { $_->getIdentifier eq $new_identifier }
+            @$preexisting_dbads;
+    }
+    $dba->addDerivedBioAssayData($dbad) unless $found;
+
+    return;
+}
+
+sub parse_software {
+    my ($self, $software_string) = @_;
+
+    # Software name is everthing up to the first parenthesis
+    my $name = ( split /\s*\(/, $software_string )[0];
+
+    # Software version is within parens
+    my ($software_version) = ( $software_string =~ $RE_WITHIN_PARENTHESES );
+
+    return ( $name, $software_version );
+}
+
+sub qts_from_names {
+
+    my ( $self, $file, $qt_names ) = @_;
+
+    my $bag_of = $self->get_bags();
+
+    $qt_names ||= $file->get_column_headings();
+
+    my ( @qtd_qtlist, $qtd_key );
+    my ( $QT_prefix, $software );
+
+    if ( $file->get_format_type() eq 'Affymetrix' ) {
+        $QT_prefix = 'Affymetrix:QuantitationType:';
+	$software  = 'Affymetrix';
+    }
+    elsif ( my ( $type, $qt_namespace )
+        = ( $file->get_qt_type =~ m{\A (.*?) \[ (.*) \] \z}xms ) ) {
+        $QT_prefix = "${qt_namespace}:${type}_QuantitationType:";
+	$software  = $type;
+    }
+    else {
+        $QT_prefix = sprintf('Unknown:%s_QuantitationType:', $file->get_qt_type());
+	$software  = $file->get_qt_type() eq 'Ambiguous'
+	           ? 'Unknown'
+		   : $file->get_qt_type();
+    }
+
+    foreach my $heading (@$qt_names) {
+
+        my $identifier       = $QT_prefix . $heading;
+        my $quantitationtype = $bag_of->{quantitationtype}->(
+            $identifier,
+            {   identifier   => $identifier,
+                prefix       => $QT_prefix,
+                name         => $heading,
+		software     => $software,
+                data_metrics => $file->get_data_metrics(),
+                container    => $bag_of->{quantitationtype},
+            }
+        );
+        push( @qtd_qtlist, $quantitationtype );
+
+        $qtd_key .= $heading;
+
+    }
+
+    # Quick check for sane QTs (This error should no longer be triggered
+    # by missing confidence indicator targets - see MAGE.pm).
+    foreach my $qt ( @{ $bag_of->{quantitationtype}->() } ) {
+        unless ( $qt->getIdentifier ) {
+            print STDERR (<<'END_ERROR');
+ERROR: At least one QuantitationType found lacking an identifier.
+Please check that the confidence indicator targets in your QT file 
+are represented in the data files.
+
+END_ERROR
+
+            croak(    qq{Error occurred at data file }
+                    . $file->get_name()
+                    . q{QT name: "}
+                    . $qt->getName()
+                    . q{"} );
+        }
+    }
+
+    $qtd_key ||= 'NULL';    # In case of no QTs, e.g. missing file
+
+    return ( \@qtd_qtlist, $qtd_key );
+}
+
+sub top_level_experiment : PRIVATE {
+
+    my ( $self ) = @_;
+
+    my $people_carrier = $self->get_bags()->{people};
+    my $namespace      = $self->get_namespace();
+    my $exptinfo       = $self->get_expt_section();
+
+    my $experiment       = $self->new_experiment();
+    my $experimentdesign = $self->new_experimentdesign();
+
+    # Just create objects for the submitter, data coder and/or curator.
+    my $submitter = $exptinfo->{$EDF_EXPTSUBMITTER}
+	|| $exptinfo->{$EDF_EXPTSUBMITTER_EMAIL};
+    $submitter && do {
+        $people_carrier->(
+            $submitter,
+            {   namespace    => $namespace,
+		name         => $exptinfo->{$EDF_EXPTSUBMITTER},
+                organization => $exptinfo->{$EDF_EXPTORGANIZATION},
+                address      => $exptinfo->{$EDF_EXPTADDRESS},
+                email        => $exptinfo->{$EDF_EXPTSUBMITTER_EMAIL},
+                role         => $OE_VAL_SUBMITTER,
+            }
+        );
+    };
+    my $curator = $exptinfo->{$EDF_EXPTCURATOR}
+	|| $exptinfo->{$EDF_EXPTCURATOR_EMAIL};
+    $curator && do {
+        $people_carrier->(
+            $curator,
+            {   namespace    => $namespace,
+		name         => $exptinfo->{$EDF_EXPTCURATOR},
+                organization => $exptinfo->{$EDF_EXPTORGANIZATION},
+                address      => $exptinfo->{$EDF_EXPTADDRESS},
+                email        => $exptinfo->{$EDF_EXPTCURATOR_EMAIL},
+                role         => $OE_VAL_CURATOR,
+            }
+        );
+    };
+    my $datacoder = $exptinfo->{$EDF_EXPTDATACODER}
+	|| $exptinfo->{$EDF_EXPTDATACODER_EMAIL};
+    $datacoder && do {
+        $people_carrier->(
+            $datacoder,
+            {   namespace    => $namespace,
+		name         => $exptinfo->{$EDF_EXPTDATACODER},
+                organization => $exptinfo->{$EDF_EXPTORGANIZATION},
+                address      => $exptinfo->{$EDF_EXPTADDRESS},
+                email        => $exptinfo->{$EDF_EXPTDATACODER_EMAIL},
+                role         => $OE_VAL_DATA_CODER,
+            }
+        );
+    };
+    my $investigator = $exptinfo->{$EDF_EXPTINVESTIGATOR}
+	|| $exptinfo->{$EDF_EXPTINVESTIGATOR_EMAIL};
+    $investigator && do {
+        $people_carrier->(
+            $investigator,
+            {   namespace    => $namespace,
+		name         => $exptinfo->{$EDF_EXPTINVESTIGATOR},
+                organization => $exptinfo->{$EDF_EXPTORGANIZATION},
+                address      => $exptinfo->{$EDF_EXPTADDRESS},
+                email        => $exptinfo->{$EDF_EXPTINVESTIGATOR_EMAIL},
+                role         => $OE_VAL_INVESTIGATOR,
+            }
+        );
+    };
+
+    # Everyone goes on the Providers list
+    $experiment->setProviders( $people_carrier->() );
+
+    return ( $experiment, $experimentdesign );
+}
+
+sub new_experiment : PRIVATE {
+
+    my ( $self ) = @_;
+
+    my $exptinfo = $self->get_expt_section();
+
+    my $experiment = Bio::MAGE::Experiment::Experiment->new(
+        identifier => $exptinfo->{$EDF_EXPTACCESSION} );
+
+    $exptinfo->{$EDF_EXPTNAME}
+        && do { $experiment->setName( $exptinfo->{$EDF_EXPTNAME} ); };
+
+    my $description = Bio::MAGE::Description::Description->new;
+
+    # make sure we get some kind of BQS object
+    $exptinfo->{$EDF_PUBPAGES} ||= "-";
+
+    my $bqs = Bio::MAGE::BQS::BibliographicReference->new(
+        title       => $exptinfo->{$EDF_PUBTITLE},
+        pages       => $exptinfo->{$EDF_PUBPAGES},
+        publication => $exptinfo->{$EDF_PUBJOURNAL},
+        volume      => $exptinfo->{$EDF_PUBVOLUME},
+        issue       => $exptinfo->{$EDF_PUBISSUE},
+        year        => $exptinfo->{$EDF_PUBYEAR},
+        authors     => $exptinfo->{$EDF_PUBAUTHORS},
+        URI         => $exptinfo->{$EDF_PUBURI},
+    );
+    $bqs->addParameters(
+        &new_ontologyentry(
+            {   category => $OE_CAT_PUBLICATIONTYPE,
+                value    => $OE_VAL_JOURNALARTICLE,
+            }
+        )
+    );
+    if ($exptinfo->{$EDF_PUBMEDID}) {
+	my $db = Bio::MAGE::Description::Database->new(
+	    identifier => 'ebi.ac.uk:Database:pubmed',
+	);
+	my $pubmed = Bio::MAGE::Description::DatabaseEntry->new(
+	    accession  => $exptinfo->{$EDF_PUBMEDID},
+	    database   => $db,
+	);
+	$bqs->addAccessions($pubmed);
+    }
+    $description->addBibliographicReferences($bqs);
+
+    $exptinfo->{$EDF_EXPTDESCRIPTION}
+        && do { $description->setText( $exptinfo->{$EDF_EXPTDESCRIPTION} ); };
+    $exptinfo->{$EDF_EXPTURI}
+	&& do { $description->setURI( $exptinfo->{$EDF_EXPTURI} ); };
+
+    $experiment->addDescriptions($description);
+
+    $exptinfo->{$EDF_EXPTRELEASEDATE} && do {
+        my $date = Bio::MAGE::NameValueType->new(
+            name  => 'ArrayExpressReleaseDate',
+            value => $exptinfo->{$EDF_EXPTRELEASEDATE},
+        );
+        $experiment->addPropertySets($date);
+    };
+    $exptinfo->{$EDF_EXPTSUBMISSIONDATE} && do {
+        my $date = Bio::MAGE::NameValueType->new(
+            name  => 'ArrayExpressSubmissionDate',
+            value => $exptinfo->{$EDF_EXPTSUBMISSIONDATE},
+        );
+        $experiment->addPropertySets($date);
+    };
+    $exptinfo->{$EDF_EXPTGEORELEASEDATE} && do {
+        my $date = Bio::MAGE::NameValueType->new(
+            name  => 'GEOReleaseDate',
+            value => $exptinfo->{$EDF_EXPTGEORELEASEDATE},
+        );
+        $experiment->addPropertySets($date);
+    };
+
+    # Either add the curated name, or a placeholder in MAGE.
+    my $arrayexpress_curated_name = Bio::MAGE::NameValueType->new(
+        name  => 'AEExperimentDisplayName',
+        value => ( $exptinfo->{$EDF_EXPTCURATEDNAME} || q{} ),
+    );
+
+    # We support multiple secondary accessions. This may need more
+    # finesse in future.
+    my @secondary_accessions;
+    my @accnos = split /\s*;\s*/,
+	( $exptinfo->{$EDF_EXPTSECONDARYACCESSION} || q{} );
+
+    # At least one secondary accession NVT is required.
+    $accnos[0] = q{} unless scalar @accnos;
+    foreach my $acc ( @accnos ) {
+	my $accession = Bio::MAGE::NameValueType->new(
+	    name  => 'SecondaryAccession',
+	    value => $acc,
+	);
+	push @secondary_accessions, $accession;
+    }
+    $experiment->addPropertySets(
+	$arrayexpress_curated_name,
+	@secondary_accessions,
+    );
+
+    return $experiment;
+
+}
+
+sub new_experimentdesign {
+
+    my ( $self ) = @_;
+
+    my $exptinfo = $self->get_expt_section();
+
+    # Sort out ExperimentDesign
+    my $experimentdesign = Bio::MAGE::Experiment::ExperimentDesign->new;
+
+    $exptinfo->{$EDF_EXPTDESIGNTYPE} && do {
+
+        # Split the string on commas, semicolons
+        foreach
+            my $type ( split /\s*[,;]\s*/, $exptinfo->{$EDF_EXPTDESIGNTYPE} )
+        {
+            my $experimentdesigntype = &new_ontologyentry(
+                {   category => $OE_CAT_EXPERIMENTDESIGNTYPE,
+                    value    => $type
+                }
+            );
+            $experimentdesign->addTypes($experimentdesigntype);
+        }
+    };
+    $exptinfo->{$EDF_EXPTQUALITYCONTROL} && do {
+
+        # Split the string on commas, semicolons
+        my @qualitycontrol;
+        foreach my $type ( split /\s*[,;]\s*/,
+            $exptinfo->{$EDF_EXPTQUALITYCONTROL} ) {
+            push(
+                @qualitycontrol,
+                &new_ontologyentry(
+                    {   category => $OE_CAT_QUALITYCONTROLDESCRIPTIONTYPE,
+                        value    => $type
+                    }
+                )
+            );
+        }
+        my $qcd = Bio::MAGE::Description::Description->new(
+            annotations => \@qualitycontrol );
+        $experimentdesign->setQualityControlDescription($qcd);
+    };
+
+    return $experimentdesign;
+
+}
+
+sub htmlify_protocoltext {
+
+    # Make some simple substitutions to correctly format text loaded
+    # into ArrayExpress. This subroutine is likely to be extended as
+    # more substitutions are found.
+
+    my ($self, $text) = @_;
+
+    # The order of these substitutions is not currently important.
+
+    # 1. Lone ampersands should be &
+    $text =~ s/\&(?!#?\w+;)/\&/g;
+
+    # 2. Angle brackets (that are not part of HTML tags).
+    #    $text =~ s/<(?!\s*[a-z]+\s*\/?>)/\</gi;
+    # Negative look-behind is fixed width, this is not trivial to implement.
+    #    $text =~ s/>/\>/gi;
+
+    # 3. Newlines become <br>
+    $text =~ s/(\r\n?|\n)/<br>$1/g;
+
+    # 4. Various common non-ASCII characters:
+    #   a. degrees
+    $text =~ s/�/\°/g;
+    $text =~ s/�/\°/g;
+    $text =~ s/�X/\°/g;
+
+    #   b. mu
+    $text =~ s/�/\μ/g;
+    $text =~ s/��/\μ/g;
+
+    #   c. apostrophes, quotes
+    $text =~ s/�/\'/g;    
+    $text =~ s/�/\"/g;    
+    $text =~ s/�/\"/g;    
+    
+    return $text;
+
+}
+
+sub create_protocols {
+
+    my ( $self ) = @_;
+
+    my $bag_of = $self->get_bags();
+    foreach my $element (@{ $self->get_prot_section() } ) {
+
+	# Quick check before we try and generate an object without an
+	# internal identifier.
+	unless ( defined( $element->{$EDF_PROTOCOLACCESSION} ) ) {
+	    croak("Error: Protocol found without an accession");
+	}
+
+        $element->{$EDF_PROTOCOLTEXT}
+            = $self->htmlify_protocoltext(
+		$element->{$EDF_PROTOCOLTEXT}
+	    );
+
+        # Generate the MAGE protocol objects
+        my $protocol = $bag_of->{protocol}->(
+            $element->{$EDF_PROTOCOLACCESSION},
+            {   protocol_type => $element->{$EDF_PROTOCOLTYPE},
+                name          => $element->{$EDF_PROTOCOLNAME},
+                text          => $element->{$EDF_PROTOCOLTEXT},
+                parameters    => $element->{$EDF_PROTOCOLPARAMS},
+                parameter_bag => $bag_of->{parameter},
+            }
+        );
+    }
+
+    # Reassign protocol accessions, if cache file location is set in
+    # Config.yml. This works, because the protocols are still
+    # referenced by their original accession in the $bag_of
+    # container. Using the -K option to the script deactivates this so
+    # that other features can be tested using dummy submissions.
+    if ( $CONFIG->get_AUTOSUBS_DSN()
+	     && ! $self->get_keep_protocol_accns() ) {
+
+	print STDOUT ("Reassigning protocol accessions as necessary...\n");
+
+	require ArrayExpress::AutoSubmission::DB::Protocol;
+
+        # We require the experiment to have an accession number for
+        # protocol accession reassignment.
+	my $expt_accession;
+        unless ( $expt_accession = $self->get_expt_section()->{$EDF_EXPTACCESSION} ) {
+            croak("Error: Experiment accession number not defined.\n");
+        }
+
+        # Experiment accessions need to be in a defined format. We
+        # constrain that here.
+        my $t2m_expt_prefix = $CONFIG->get_T2M_EXPERIMENT_PREFIX();
+        unless ( $expt_accession
+            =~ m/\A $t2m_expt_prefix \d+ \z/xms ) {
+            croak(      qq{Error: Experiment accession not in the form }
+                      . qq{"${t2m_expt_prefix}N" (where N is an integer).\n} );
+        }
+
+	# Set the accession prefix.
+	ArrayExpress::AutoSubmission::DB::Protocol->accession_prefix(
+	    $CONFIG->get_T2M_PROTOCOL_PREFIX(),
+	);
+ 
+        # Schwarzian transform to sort by user-supplied accession
+        my @sorted = map { $_->[0] }
+            sort { $a->[1] cmp $b->[1] }
+            map { [ $_, $_->getIdentifier ] } @{ $bag_of->{protocol}->() };
+        foreach my $protocol (@sorted) {
+
+            my $user_accession = $protocol->getIdentifier;
+
+	    my $prot_accession
+		= ArrayExpress::AutoSubmission::DB::Protocol->reassign_protocol(
+		    $user_accession,
+		    $expt_accession,
+		    $protocol->getName(),
+		);
+	    
+            $protocol->setIdentifier($prot_accession);
+            if ( my $paramlist = $protocol->getParameterTypes ) {
+                foreach my $parameter (@$paramlist) {
+                    my $param_id = $parameter->getIdentifier;
+
+		    # Note that this substitution will break if the
+		    # parameter id format is ever changed FIXME
+		    my $namespace = $self->get_namespace();
+                    $param_id
+                        =~ s/$namespace:$user_accession/"$namespace:$prot_accession"/e;
+                    $parameter->setIdentifier($param_id);
+                }
+            }
+        }
+    }
+
+    return;
+}
+
+sub _file_sanity_check : PRIVATE {
+
+    my ( $self ) = @_;
+
+    # NB FIXME so that predeclared files are not croaked upon when lacking
+    # required info.
+
+    my $datafile_rowlist = $self->get_hyb_section();
+
+    # Give up if we've go no hybs. Maybe FIXME later so we can have
+    # protocol-only subs?
+    croak("Error: No data in Hybridization section.\n")
+        unless ( scalar @$datafile_rowlist );
+
+    # Check for column dependencies. Data files all need an array
+    # accession number. This is used in both the DesignElementDimension
+    # and in creating the correct Array package, so we need it for Affy
+    # submissions as well.
+    foreach my $datarow (@$datafile_rowlist) {
+        foreach my $filetype (
+            @{ $CONFIG->get_T2M_FILE_TYPES() },
+            $CONFIG->get_FGEM_FILE_TYPE()
+            ) {
+            my $heading = $EDF_FILE_PREFIX . $filetype . $EDF_FILE_SUFFIX;
+            if ( $datarow->{$heading} && !$datarow->{$EDF_ARRAYACCESSION} ) {
+                croak(
+                    "Error: Data file $datarow->{$heading} provided without Array accession number\n"
+                );
+            }
+        }
+    }
+
+    # Check that all the files are present
+    my $errorcount;
+    foreach my $datarow (@$datafile_rowlist) {
+
+        DATAROW_COLUMN:
+        foreach my $column ( keys %$datarow ) {
+
+	    # General form File[type] e.g. File[raw],
+	    # File[normalized]; skip blanks
+
+            my $regexp_str = "$EDF_FILE_PREFIX(.*)$EDF_FILE_SUFFIX";
+            $regexp_str =~ s{$RE_SQUARE_BRACKETS}{\\$1}gxms;
+
+            if ( ( my ($filetype) = ( $column =~ m/\A$regexp_str\z/ ) )
+                && $datarow->{$column} ) {
+
+                next DATAROW_COLUMN
+                    if ( $filetype eq 'image' );    # Skip image files.
+
+		# CDF files can be found in the directory defined in
+		# Config.pm
+                if ( $filetype eq 'cdf' ) {
+
+		    # Correct the CDF name on the fly.
+		    my $path;
+		    my $cdffile = $datarow->{$column};
+		    ($datarow->{$column}, $path) = find_cdf(
+			$datarow->{$column},
+			$self->get_source_directory(),
+		    );
+
+                    if ( !-e $path ) {
+                        print STDERR ("Error: CDF file not found: $cdffile\n");
+			$self->logprint(
+			    'tab2mage',
+			    "Error: CDF file not found: $cdffile\n"
+			);
+                        $errorcount++;
+                    }
+                }
+
+                # All other files must be in the current working
+                # directory or the source dir set using the -d option.
+                else {
+
+		    my $dir = $self->get_source_directory();
+                    my $path = $dir
+			? File::Spec->catfile( $dir, $datarow->{$column} )
+                        : $datarow->{$column};
+		    
+		    if ( !-e $path ) {
+			print STDERR (
+			    "Error: Missing file of type $filetype: $datarow->{$column}\n"
+			);
+			$self->logprint(
+			    'tab2mage',
+			    "Error: Missing file of type $filetype: $datarow->{$column}\n"
+			);
+			$errorcount++;
+		    }
+                }
+            }
+        }
+    }
+
+    if ( $errorcount ) {
+	croak(
+	    "Files missing. See log file for details. This script has terminated.\n");
+    }
+
+    return;
+}
+
+sub read_edf {
+
+    my ( $self, $error_fh ) = @_;
+
+    $error_fh ||= $self->log_fh('tab2mage');
+
+    my $exptinfo         = {};
+    my $datafile_rowlist = [];
+    my $protocollist     = [];
+
+    my $spreadsheet = $self->get_spreadsheet_filename();
+    if ( -B $spreadsheet ) {
+        croak(
+	    sprintf("Error: Spreadsheet file %s is a binary file format."
+		  . " This script supports plain text only.",
+		    $spreadsheet));
+    }
+
+    my $eol_char = check_linebreaks($spreadsheet)
+        or croak(
+	    sprintf("Error: Cannot correctly parse linebreaks in file %s",
+		    $spreadsheet));
+    if (    ( $eol_char eq "\015" )
+         && ( $Text::CSV_XS::VERSION < 0.27 ) ) {
+
+	# Mac linebreaks not supported by older versions of Text::CSV_XS.
+	croak("Error: Mac linebreaks not supported by this version"
+	    . " of Text::CSV_XS. Please upgrade to version 0.27 or higher.\n");
+    }
+    local $/ = $eol_char;
+
+    my $fh = IO::File->new( $spreadsheet, '<' )
+        or croak(
+	    sprintf("Error opening spreadsheet file %s: %s",
+		    $spreadsheet, $!));
+
+    # Namespace:Authority string:
+    my ( $namespace, $domain );
+
+    EDF_LINE:
+    while ( my $line = <$fh> ) {
+
+        next EDF_LINE
+            if ( $line =~ $RE_COMMENTED_STRING );    # Allow hash comments
+
+        # Some default values, just in case.
+        # We assign these between each section, as they are used in
+        # Protocol and Hybridization.
+
+        # This is the default as assigned by ArrayExpress:
+        $domain ||= 'ebi.ac.uk';
+        $namespace = "$domain:" . $CONFIG->get_TAB2MAGE_PROGNAME();
+
+        if ( $line =~ m/Experiment [ ]? section/ixms ) {
+            ( $exptinfo, $fh )
+                = $self->_parse_columnwise( $fh, $eol_char );
+
+            $self->add_error(
+		validate_experiment_section(
+		    [ sort keys %$exptinfo ],
+		    $error_fh,
+		)
+	    );
+
+	    # If the constructor was passed an accession number and/or
+	    # domain, use it/them in preference to the one(s) in the
+	    # spreadsheet.
+	    if ( my $accession = $self->get_external_accession() ) {
+		$exptinfo->{$EDF_EXPTACCESSION} = $accession;
+	    }
+	    if ( my $domain = $self->get_external_domain() ) {
+		$exptinfo->{$EDF_EXPTDOMAIN} = $domain;
+	    }
+
+            $domain = $exptinfo->{$EDF_EXPTDOMAIN} || $domain;
+
+            next EDF_LINE;
+        }
+
+        elsif ( $line =~ m/Protocol [ ]? section/ixms ) {
+            ( $protocollist, $fh )
+                = $self->_parse_rowwise( $fh, $eol_char );
+
+            # Check first line of section only; it is representative.
+	    if ( scalar @{ $protocollist || [] } ) {
+		$self->add_error(
+		    validate_protocol_section(
+			[ sort keys %{ $protocollist->[0] } ],
+			$error_fh,
+		    )
+		);
+	    }
+
+            # Fix parameters so they're a proper hash {name => unit}
+
+            PROTOCOL_LINE:
+            foreach my $row (@$protocollist) {
+                next PROTOCOL_LINE unless $row->{$EDF_PROTOCOLPARAMS};
+
+                # HoH: name => {identifier => q{}, unit => q{}}
+                my %param2unit;
+
+                # Split on semicolon; discard surrounding whitespace
+                my @parameters = split /\s*;\s*/, $row->{$EDF_PROTOCOLPARAMS};
+
+                foreach my $param (@parameters) {
+
+                  # Parameter name is everything before the first parenthesis.
+                    my $name = ( split /\s*\(/, $param )[0];
+
+                    # Unit is within parens.
+                    my ($unit) = ( $param =~ $RE_WITHIN_PARENTHESES );
+                    $unit ||= q{};    # In case the pattern match failed.
+
+                    my $param_id
+                        = "$namespace:$row->{$EDF_PROTOCOLACCESSION}.$name.Parameter";
+                    $param2unit{$name} = {
+                        identifier => $param_id,
+                        unit       => $unit,
+                        name       => $name,
+                    };
+                }
+
+                # Replace the original string with the new hash.
+                $row->{$EDF_PROTOCOLPARAMS} = \%param2unit if @parameters;
+            }
+            next EDF_LINE;
+        }
+
+        elsif ( $line =~ m/Hybridi[sz]ation [ ]? section/ixms ) {
+            ( $datafile_rowlist, $fh )
+                = $self->_parse_rowwise( $fh, $eol_char );
+
+            # Check first line of section only; it is representative.
+	    if ( scalar @{ $datafile_rowlist || [] } ) {
+		$self->add_error(
+		    validate_hybridization_section(
+			[ sort keys %{ $datafile_rowlist->[0] } ],
+			$error_fh,
+		    )
+		);
+	    }
+
+            # Parameter support - reorganize our parameters method-wise.
+            my %proto2param = (
+                $EDF_GROW_PROTOCOL    => $EDF_SAMPLE_PARAMS,
+                $EDF_TREAT_PROTOCOL   => $EDF_TREAT_PARAMS,
+                $EDF_EXTRACT_PROTOCOL => $EDF_EXTRACT_PARAMS,
+                $EDF_LABEL_PROTOCOL   => $EDF_LABEL_PARAMS,
+                $EDF_IP_PROTOCOL      => $EDF_IP_PARAMS,
+                $EDF_HYB_PROTOCOL     => $EDF_HYB_PARAMS,
+                $EDF_SCAN_PROTOCOL    => $EDF_SCAN_PARAMS,
+                $EDF_FEXT_PROTOCOL    => $EDF_FEXT_PARAMS,
+                $EDF_NORM_PROTOCOL    => $EDF_NORM_PARAMS,
+            );
+
+            foreach my $row (@$datafile_rowlist) {
+
+                # Correct common casing error for Cy-dyes
+                $row->{$EDF_DYE}
+                    && do { $row->{$EDF_DYE} =~ s{\A cy(\d+) \z}{Cy$1}ixms; };
+
+                # Map parameters to protocol applications. Scan through the
+                # protocoltype:
+
+                PARAMETER_MAPPING:
+                while ( my ( $protocoltype, $methodparams )
+                    = each %proto2param ) {
+
+                    next PARAMETER_MAPPING unless $row->{$protocoltype};
+
+                    # Scan through our list of defined protocols
+                    foreach my $protocol (@$protocollist) {
+
+                        if ( $protocol->{$EDF_PROTOCOLACCESSION} eq
+                               $row->{$protocoltype}
+                            && $protocol->{$EDF_PROTOCOLPARAMS} ) {
+
+                            # For each defined parameter:
+                            while ( my ( $param_name, $param_hash )
+                                = each %{ $protocol->{$EDF_PROTOCOLPARAMS} } )
+                            {
+                                my $value_column = $EDF_PARAM_PREFIX
+                                    . $param_name
+                                    . $EDF_PARAM_SUFFIX;
+
+                                # Incorporate the parameter into the
+                                # method-specific parameter hash.
+                                if ( defined( $row->{$value_column} )
+                                    && $row->{$value_column} ne q{} ) {
+                                    $row->{$methodparams}
+                                        { $param_hash->{identifier} }
+                                        = $row->{$value_column};
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            next EDF_LINE;
+        }
+
+        print STDERR (
+            "Warning: Line below is not associated with any section:\n\t$line\n"
+            )
+            if ( $line =~ m/\S/ );
+        $self->add_error($CONFIG->get_ERROR_INNOCENT());
+
+    }
+
+    # Check that we have some data parsed
+    unless ( scalar( grep { defined $_ } values %$exptinfo ) ) {
+        print STDERR "Warning: Experiment section not found.\n";
+        $self->add_error($CONFIG->get_ERROR_PARSEBAD());
+    }
+    unless ( scalar @{ $protocollist || [] } ) {
+
+	# Use STDOUT here as this is a common use case.
+        print STDOUT "Warning: Protocol section not found.\n";
+        $self->add_error($CONFIG->get_ERROR_MIAME());
+    }
+    unless ( scalar @{ $datafile_rowlist || [] } ) {
+        print STDERR "Warning: Hybridization section not found.\n";
+        $self->add_error($CONFIG->get_ERROR_PARSEFAIL());
+    }
+
+    # In case the domain is not set in the experiment section, set it
+    # here.
+    $exptinfo->{$EDF_EXPTDOMAIN} ||= $domain;
+
+    $self->set_expt_section($exptinfo);
+    $self->set_hyb_section($datafile_rowlist);
+    $self->set_prot_section($protocollist);
+    $self->set_namespace($namespace);
+
+    return ( $self->get_error() );
+}
+
+sub _parse_columnwise : PRIVATE {
+
+    my ( $self, $fh, $eol_char ) = @_;
+
+    my $summary = {};
+
+    # Set up a CSV parser object
+    my $csv_format = Text::CSV_XS->new(
+        {   sep_char    => qq{\t},
+            quote_char  => qq{"},                   # default
+            escape_char => qq{"},                   # default
+            binary      => 1,
+            eol         => ( $eol_char || "\n" ),
+	    allow_loose_quotes => 1,
+        }
+    );
+
+    my $larry;
+    FILE_LINE:
+    while ( $larry = $csv_format->getline($fh) ) {
+
+        # Section ends on empty line.
+        my $line = join( q{}, @$larry );
+        last FILE_LINE if ( $line =~ $RE_EMPTY_STRING );
+
+        # Allow hash comments.
+        next FILE_LINE if ( $line =~ $RE_COMMENTED_STRING );
+
+        # Strip all whitespace from row headings.
+        $larry->[0] =~ s/\s*//g;
+
+        # lc to allow case-insensitivity.
+        $larry->[0] = lc( $larry->[0] );
+
+        # Raise error on non-unique row tag; pass the error back, but
+        # don't let our sig handler see it.
+	{
+	    my $sighandler = $SIG{__DIE__};
+	    delete $SIG{__DIE__};
+	    if ( $summary->{ $larry->[0] } ) {
+		croak("ERROR: Row names must be unique: $larry->[0]\n");
+	    }
+	    $SIG{__DIE__} = $sighandler;
+	}
+
+        # Strip surrounding whitespace from value.
+        $larry->[1] && $larry->[1] =~ s{$RE_SURROUNDED_BY_WHITESPACE}{$1}gxms;
+
+        # Don't record empty values
+        $summary->{ $larry->[0] } = $larry->[1]
+            if ( defined( $larry->[1] ) && $larry->[1] !~ $RE_EMPTY_STRING );
+
+    }
+
+    # Check we've parsed to the end of the file.
+    my ( $error, $mess ) = $csv_format->error_diag();
+    unless ( $larry || $error == 2012 ) {    # 2012 is the Text::CSV_XS EOF code.
+	croak(
+	    sprintf(
+		"Error in tab-delimited format: %s. Bad input was:\n\n%s\n",
+		$mess,
+		$csv_format->error_input(),
+	    ),
+	);
+    }
+
+    return ( $summary, $fh );
+
+}
+
+sub _parse_rowwise : PRIVATE {
+
+    my ( $self, $fh, $eol_char ) = @_;
+
+    my $tablelist = [];
+
+    # Set up a CSV parser object
+    my $csv_format = Text::CSV_XS->new(
+        {   sep_char    => qq{\t},
+            quote_char  => qq{"},                   # default
+            escape_char => qq{"},                   # default
+            binary      => 1,
+            eol         => ( $eol_char || "\n" ),
+	    allow_loose_quotes => 1,
+        }
+    );
+
+    # Scan down to the first non-comment line
+    my ( $firstline, $firstlarry );
+    until ( $firstline && $firstline !~ $RE_COMMENTED_STRING ) {
+        $firstlarry = $csv_format->getline($fh);
+        $firstline = join( '', @{ $firstlarry || [] } );
+    }
+
+    # Check we've parsed to the end of the file.
+    my ( $error, $mess ) = $csv_format->error_diag();
+    unless ( $firstlarry || $error == 2012 ) {    # 2012 is the Text::CSV_XS EOF code.
+	croak(
+	    sprintf(
+		"Error in tab-delimited format: %s. Bad input was:\n\n%s\n",
+		$mess,
+		$csv_format->error_input(),
+	    ),
+	);
+    }
+
+    # Handle the special case where there's a section heading but no
+    # section.
+    return ( $tablelist, $fh ) if ( $firstline =~ $RE_EMPTY_STRING );
+
+    # Use only the headings which have a text label, check for
+    # uniqueness.
+    my ( @headings, %already_seen );
+    foreach my $heading (@$firstlarry) {
+	push( @headings, $heading );
+	if ( $heading && $already_seen{$heading}++ ) {
+	    croak("ERROR: Column headings must be unique: $heading\n");
+	}
+    }
+
+    # Strip whitespace from column headings, lc to allow case-insensitivity
+    foreach my $heading (@headings) {
+
+        # Strip whitespace surrounding term in brackets.  NB. don't strip
+        # parameter name internal whitespace - needed for mapping.
+        if ( my ( $superclass, $category )
+            = ( $heading =~ m{(.* \[) \s* (.*?) \s* \]}xms ) ) {
+            $superclass =~ s/\s*//g;
+            $superclass = lc($superclass);
+
+            # Normalize the file type, array and protocol type terms, just in case.
+            if (   $superclass eq $EDF_FILE_PREFIX
+                || $superclass =~ m/\A Protocol/ixms
+		|| $superclass =~ m/\A Array/ixms ) {
+                $category =~ s/\s*//g;
+                $category = lc($category);
+            }
+            $heading = "$superclass$category]";
+        }
+
+        else {
+            $heading = lc($heading);
+            $heading =~ s/\s*//g;
+        }
+    }
+
+    # Now process each row of the section
+    my $larry;
+    FILE_LINE:
+    while ( $larry = $csv_format->getline($fh) ) {
+
+        # Section ends on empty line.
+        my $line = join( q{}, @$larry );
+        last FILE_LINE if ( $line =~ $RE_EMPTY_STRING );
+
+        # Allow hash comments.
+        next FILE_LINE if ( $line =~ $RE_COMMENTED_STRING );
+
+        my $row          = {};
+        my $last_heading = scalar @headings;
+
+        COLUMN_VALUE:
+        for ( my $i = 0; $i < $last_heading; $i++ ) {
+
+            # Skip empty values.
+            next COLUMN_VALUE unless defined( $larry->[$i] );
+
+            # Strip whitespace.
+            $larry->[$i] =~ s{$RE_SURROUNDED_BY_WHITESPACE}{$1}gxms;
+            $row->{ $headings[$i] } = $larry->[$i];
+        }
+        push @$tablelist, $row;
+    }
+
+    # Check we've parsed to the end of the file.
+    ( $error, $mess ) = $csv_format->error_diag();
+    unless ( $larry || $error == 2012 ) {    # 2012 is the Text::CSV_XS EOF code.
+	croak(
+	    sprintf(
+		"Error in tab-delimited format: %s. Bad input was:\n\n%s\n",
+		$mess,
+		$csv_format->error_input(),
+	    ),
+	);
+    }
+
+    return ( $tablelist, $fh );
+}
+
+sub check_pool_protocol_usage : PRIVATE {
+
+    my ( $self, $protocol, $material ) = @_;
+
+    my $prot_id = $protocol->getIdentifier();
+    my $is_used;
+
+    TREATMENT:
+    foreach my $treatment ( @{ $material->getTreatments() || [] } ) {
+
+	# Pooling treatments are always order 0 for tab2mage.
+	next TREATMENT unless ( $treatment->getOrder() eq '0' );
+
+	foreach my $protapp (
+	    @{ $treatment->getProtocolApplications() || [] } ) {
+	    
+	    my $oldprot = $protapp->getProtocol();
+	    $is_used++ if ( $prot_id eq $oldprot->getIdentifier() );
+	}
+    }
+
+    if ( $is_used ) {
+	$pool_protocol_usage{ident $self}{$prot_id}++;
+    }
+
+    return;
+}
+
+sub record_pool_protocol_assn : PRIVATE {
+
+    my ( $self, $protocol, $biosample ) = @_;
+
+    my $prot_id   = $protocol->getIdentifier();
+    my $sample_id = $biosample->getIdentifier();
+
+    # This is overly complex because the protocol identifier could
+    # change, e.g. after protocol accession reassignment. So $prot_id
+    # may not correctly point to a protocol object in the protocol bag
+    # object.
+    $pool_protocol_assns{ident $self}{ $prot_id }{ $sample_id } = {
+	sample   => $biosample,
+	protocol => $protocol,
+    };
+
+    return;
+}
+
+sub link_unused_pool_protocols : PRIVATE {
+
+    my ( $self, $identifier_template ) = @_;
+
+    PROTOCOL:
+    foreach my $poolinghash ( values %{ $pool_protocol_assns{ident $self} } ) {
+
+	foreach my $assn ( values %{ $poolinghash } ) {
+
+	    my $protocol = $assn->{protocol};
+	    my $extract  = $assn->{sample};
+
+	    my $prot_id  = $protocol->getIdentifier();
+
+	    next PROTOCOL if $pool_protocol_usage{ident $self}{$prot_id};
+
+	    unless ( defined($protocol) && defined($extract) ) {
+		confess("Error: either protocol or extract undefined in pooling recheck.");
+	    }
+
+	    $self->link_pool_protocol( $protocol, $extract, $identifier_template );
+	}
+    }
+
+    return;
+}
+
+sub link_pool_protocol : PRIVATE {
+
+    my ( $self, $protocol, $extract, $identifier_template ) = @_;
+
+    my ( $treatment, $oldbmms );
+    TREATMENT:
+    foreach my $old ( @{ $extract->getTreatments() || [] } ) {
+
+	# Take the first set of BMMs as being representative.
+	$oldbmms ||= $old->getSourceBioMaterialMeasurements();
+
+	# Pooling is always order 0 for tab2mage.
+	if ( $old->getOrder() eq '0' ) {
+	    $treatment = $old;
+	    last TREATMENT;
+	}
+    }
+
+    my $action = Bio::MAGE::Description::OntologyEntry->new(
+        category => 'Action',
+        value    => 'pool',
+    );
+
+    unless ( $treatment ) {
+
+	my $identifier = "$identifier_template.pool." . unique_identifier() . '.Treatment';
+	$treatment = Bio::MAGE::BioMaterial::Treatment->new(
+	    identifier                    => $identifier,
+	    order                         => 0,
+	    action                        => $action,
+	    sourceBioMaterialMeasurements => $oldbmms,
+	);
+	$extract->addTreatments( $treatment );
+    }
+
+    my $found;
+    foreach my $old ( @{ $treatment->getProtocolApplications || [] } ) {
+	my $oldprot = $old->getProtocol();
+	$found++ if (   $protocol->getIdentifier()
+		     eq $oldprot->getIdentifier() );
+    }
+
+    unless ( $found ) {
+	my $pa = Bio::MAGE::Protocol::ProtocolApplication->new(
+            protocol     => $protocol,
+            activityDate => 'n/a',
+	);
+	$treatment->addProtocolApplications($pa);
+    }
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Curator/Validate.pm b/lib/ArrayExpress/Curator/Validate.pm
new file mode 100644
index 0000000..d68f9bf
--- /dev/null
+++ b/lib/ArrayExpress/Curator/Validate.pm
@@ -0,0 +1,1084 @@
+#!/usr/bin/env perl
+#
+# Module to provide basic methods used by the tab2mage and experiment
+# checker scripts.
+#
+# Tim Rayner 2005, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Validate.pm 2069 2008-06-04 14:33:52Z tfrayner $
+#
+
+package ArrayExpress::Curator::Validate;
+
+use strict;
+use warnings;
+use Class::Std;
+use English qw( -no_match_vars );
+use File::Basename;
+
+use ArrayExpress::Curator::Entrez_list qw(
+    parse_entrez_names
+);
+
+require ArrayExpress::Curator::Tab2MAGE;
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+use ArrayExpress::Curator::MAGE qw(
+    unique_identifier
+);
+
+use ArrayExpress::Curator::MAGE::Definitions qw(
+    $EDF_EXPTACCESSION
+    $EDF_EXPTDESCRIPTION
+    $EDF_EXPTRELEASEDATE
+    $EDF_EXPTSUBMISSIONDATE
+    $EDF_EXPTGEORELEASEDATE
+    $EDF_EXPTSUBMITTER
+    $EDF_EXPTSUBMITTER_EMAIL
+    $EDF_PUBJOURNAL
+    $EDF_PUBMEDID
+    $EDF_ARRAYACCESSION
+    $EDF_FILE_PREFIX
+    $EDF_FILE_SUFFIX
+    $EDF_HYBRIDIZATIONNAME
+    $EDF_FV_PREFIX
+    $EDF_FV_SUFFIX
+    $EDF_PROTOCOLACCESSION
+    $EDF_PROTOCOLNAME
+    $EDF_PROTOCOLTYPE
+    $EDF_PROTOCOLTEXT
+    $EDF_PROTOCOLPARAMS
+    $EDF_GROW_PROTOCOL
+    $EDF_TREAT_PROTOCOL
+    $EDF_EXTRACT_PROTOCOL
+    $EDF_POOL_PROTOCOL
+    $EDF_LABEL_PROTOCOL
+    $EDF_IP_PROTOCOL
+    $EDF_HYB_PROTOCOL
+    $EDF_SCAN_PROTOCOL
+    $EDF_FEXT_PROTOCOL
+    $EDF_NORM_PROTOCOL
+    $EDF_TRXN_PROTOCOL
+    $EDF_PARAM_PREFIX
+    $EDF_PARAM_SUFFIX
+);
+
+use ArrayExpress::Curator::Report qw(
+    datafile_consistency_table
+    format_description
+);
+
+use ArrayExpress::Curator::Common qw(
+    get_filepath_from_uri
+);
+
+require ArrayExpress::Datafile;
+
+use base 'ArrayExpress::Curator::ExperimentChecker';
+
+my %spreadsheet_filename : ATTR( :get<spreadsheet_filename>,    :init_arg<spreadsheet_filename>,    :default<undef> );
+my %mage_generator       : ATTR( :get<mage_generator>,          :set<mage_generator>,               :default<undef> );
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    unless ( $spreadsheet_filename{$id} ) {
+	croak("Error: spreadsheet_filename must be set for Tab2MAGE checking.");
+    }
+
+    my $logfile_string = File::Spec->rel2abs($self->get_spreadsheet_filename());
+    $logfile_string =~ s/\.\w{3,4}$//;     # strip off the extension
+    my ( $vol, $dir, $name ) = File::Spec->splitpath($logfile_string);
+    $self->localize_logfiles({
+	directory => $dir,
+	volume    => $vol,
+	name      => $name,
+    });
+
+    return;
+}
+
+sub get_files_and_annotation : RESTRICTED {
+
+    my ( $self ) = @_;
+
+    # Quick check to see what we've been passed.
+    unless ( $self->get_spreadsheet_filename() ) {
+        croak(<<'END_ERROR');
+Error: Tab2MAGE checker must be passed a spreadsheet_filename option.
+END_ERROR
+    }
+
+    my $filelist;
+    my $hyb_ids;         # Used to map FGEM column headings to hybs.
+    my $norm_ids;        # Empty for MX; cannot map MX FGEM using Norm IDs.
+    my $tab2mage;        # FIXME factor this out of here at some point.
+
+    print STDOUT ("Running in Tab2MAGE mode...\n");
+
+    $self->logprint( 'error',  "* Tab2MAGE mode *\n\n" );
+    $self->logprint( 'report', "* Tab2MAGE mode *\n\n" );
+
+    # Check the metadata from the spreadsheet, retrieve the
+    # hybridization section.
+    ( $filelist, $hyb_ids, $norm_ids ) = $self->validate_edf();
+
+    $self->logprint_line( 'error', 'ADF parsing START' );
+
+    # We need $file to have array info.
+    if ( $self->get_adf_filename || $self->get_array_accession ) {
+
+	$self->cache_user_supplied_arrays( $filelist || [] );
+
+    }
+    else {
+
+	# Just populate the keys for now.
+	my %array_designs;
+	foreach my $file ( @{ $filelist || [] } ) {
+	    unless ( $file->get_is_exp() ) {
+		$array_designs{ $file->get_array_design_id() }++;
+	    }
+	}
+
+	# populate the array designs hashref
+	unless ( $self->get_is_standalone() ) {
+
+	    my @accno_table;
+	    foreach my $accno ( keys %array_designs ) {
+
+		# ADF retrieval crashes when AE is down; we trap that here.
+		$self->get_ae_arraydesign({
+		    accession => $accno,
+		});
+	    }
+	}
+    }
+
+    $self->populate_file_arraydesigns( $filelist || [] );
+    
+    $self->logprint_line( 'error', 'ADF parsing END' );
+
+    return ( $filelist, $hyb_ids, $norm_ids );
+}
+
+sub check_bmchars_against_fvs : PRIVATE {
+
+    my ( $self, $bags ) = @_;
+
+    # Generate a list of FV OE category => value pairs
+    my @fv_catvals;
+    foreach my $ef ( @{ $bags->{'experimentalfactor'}->() } ) {
+	foreach my $fv ( @{ $ef->getFactorValues() || [] } ) {
+	    my ( $category, $value );
+	    if ( my $fvvalue = $fv->getValue() ) {
+
+		# Regular FV Value OE.
+		$category = $fvvalue->getCategory();
+		$value    = $fvvalue->getValue();
+	    }
+	    elsif ( my $measurement = $fv->getMeasurement() ) {
+
+		# FV Measurements.
+		my $efc = $ef->getCategory();
+		$category = $efc->getValue();
+
+		# CamelCase the term.
+		$category =~ s/^(.)/uc($1)/e;
+		$category =~ s/_(.)/uc($1)/ge;
+		$category =~ s/_//;
+		
+		$value    = $measurement->getValue();
+	    }
+	    push @fv_catvals, { $category => $value };
+	}
+    }
+
+    # Generate a list of BMC OE category => value pairs.
+    my @bmc_catvals;
+    foreach my $source ( @{ $bags->{'biosource'}->() || [] } ) {
+	foreach my $char ( @{ $source->getCharacteristics() || [] } ) {
+
+	    # We assume regular BMC OEs here (no nesting).
+	    push @bmc_catvals, { $char->getCategory => $char->getValue };
+	}
+    }
+
+    $self->compare_fvs_to_bmcs(\@fv_catvals, \@bmc_catvals);
+
+    return;
+}
+
+sub _check_hybridizations : PRIVATE {
+
+    my ( $self, $bag_of ) = @_;
+
+    my %pba_mapcount;
+    foreach my $mba ( @{ $bag_of->{mba}->() } ) {
+        my $pba = $mba->getFeatureExtraction()->getPhysicalBioAssaySource;
+        $pba_mapcount{ $pba->getName() }++;
+    }
+
+    foreach my $pba ( @{ $bag_of->{pba}->() } ) {
+
+        # FactorValues
+        my $fv_list = $pba->getBioAssayFactorValues();
+
+        # Must have at least one FV
+        unless ( $#$fv_list >= 0 ) {
+            $self->logprint( 'error',
+                      q{Warning: no FactorValue found for hybridization "}
+                    . $pba->getName()
+                    . qq{".\n} );
+            $self->add_error( $CONFIG->get_ERROR_MIAME() );
+        }
+
+	# Hyb-level PBA checks.
+	if ( my $bac = $pba->getBioAssayCreation() ) {
+
+	    # Arrays (required by the model)
+	    unless ( $bac->getArray() ) {
+		$self->logprint(
+		    'error',
+		    q{ERROR: Hybridization "}
+                    . $pba->getName()
+                    . qq{" has no associated array (needs Array[accession]).\n}
+		);
+		$self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+	    }
+
+	    # Labeled Extracts
+	    my @labels;
+	    my $source_bmms = $bac->getSourceBioMaterialMeasurements();
+	    foreach my $bmm (@{ $source_bmms || [] } ) {
+		push( @labels, $bmm->getBioMaterial() );
+	    }
+	    unless (@labels) {
+		$self->logprint(
+		    'error',
+		    q{Warning: no LabeledExtracts linked to hybridization "}
+                    . $pba->getName()
+                    . qq{".\n} );
+		$self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+	    }
+        }
+
+	# The next couple of tests check for mappings to MBAs;
+	# obviously this is made more complex when using the
+	# two-colour codings. Here we query for the merged PBA.
+	my $mappable_pba;
+	if ( scalar @{ $pba->getChannels() || [] } > 1 ) {
+
+	    # This assumes the convention established in the
+	    # AE::Curator::MAGE module is adhered to.
+	    $mappable_pba = $bag_of->{extended_pba}->(
+		$pba->getName() . ".merged"
+	    );
+	    unless ( $mappable_pba ) {
+		die(sprintf(
+		    "Internal error: Merged PBA undefined for two-color coding (%s).",
+		    $pba->getName())
+		);
+	    }
+	}
+	else {
+
+	    # Single-channel coding.
+	    $mappable_pba = $pba;
+	}
+
+        # MeasuredBioAssays; we maintain a strict 1:n PBA:MBA mapping;
+        # having n PBA map to one MBA results in dangling PBAs, which we
+        # detect here.
+	my $mappable_pba_name = $mappable_pba->getName();
+        if ( $pba_mapcount{ $mappable_pba_name }
+            && ( $pba_mapcount{ $mappable_pba_name } > 1 ) ) {
+            $self->logprint( 'error',
+                      q{Warning: Multiple scans map to hybridization "}
+                    . $mappable_pba_name
+                    . qq{".\n} );
+            $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+        }
+        elsif ( !$pba_mapcount{ $mappable_pba_name }
+            || ( $pba_mapcount{ $mappable_pba_name } < 1 ) ) {
+            $self->logprint( 'error',
+                      q{WARNING: Hybridization "}
+                    . $mappable_pba_name
+                    . q{" is not referenced by any MeasuredBioAssay: }
+                    . qq{possible duplicate filename in spreadsheet?\n} );
+            $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+        }
+    }
+
+    return;
+}
+
+sub _check_datatrans_protocol : PRIVATE {
+
+    # Returns true if any $protocoltype protocol is found attached to
+    # a normalized data file (this is used for MIAME compliance
+    # testing).
+    my ( $self,
+	 $tab2mage,
+	 $filetype,
+	 $protocoltype,
+	 $err_string,
+     ) = @_;
+
+    my $norm_data = $EDF_FILE_PREFIX . $filetype . $EDF_FILE_SUFFIX;
+    my ( %filenames, %has_protocol, $protocol_found );
+    foreach my $datarow ( @{ $tab2mage->get_hyb_section() || [] } ) {
+        if ( $datarow->{$norm_data} ) {
+            $filenames{ $datarow->{$norm_data} }++;
+            if ( $datarow->{$protocoltype} ) {
+		$has_protocol{ $datarow->{$norm_data} }++;
+		$protocol_found = 1;
+	    }
+        }
+    }
+    foreach my $norm_file ( keys %filenames ) {
+
+        # Affy CHP files without protocols are okay (we derive the
+        # protocol from their metadata).
+        unless ( $has_protocol{$norm_file} || ( $norm_file =~ m/\.CHP$/i ) ) {
+            $self->logprint( 'error',
+                qq{Warning: $err_string "$norm_file"\n} );
+            $self->add_error( $CONFIG->get_ERROR_MIAME() );
+        }
+    }
+
+    return $protocol_found;
+}
+
+sub _check_protocols : PRIVATE {
+
+    my ( $self, $tab2mage ) = @_;
+
+    my @protocol_headings = (
+        $EDF_GROW_PROTOCOL, $EDF_TREAT_PROTOCOL, $EDF_EXTRACT_PROTOCOL,
+        $EDF_POOL_PROTOCOL, $EDF_LABEL_PROTOCOL, $EDF_IP_PROTOCOL,
+        $EDF_HYB_PROTOCOL,  $EDF_SCAN_PROTOCOL,  $EDF_FEXT_PROTOCOL,
+        $EDF_NORM_PROTOCOL, $EDF_TRXN_PROTOCOL,
+    );
+
+    # Check that all declared protocols are referenced
+    my ( %unreferenced, %undeclared );
+
+    PROTOCOL:
+    foreach my $protocol ( @{ $tab2mage->get_prot_section() || [] } ) {
+
+       # Confirm that the protocol does actually have an accession (required).
+        unless ( $protocol->{$EDF_PROTOCOLACCESSION} ) {
+            $self->logprint(
+                'error',
+                "ERROR: Protocol found without an accession: ",
+                $protocol->{$EDF_PROTOCOLNAME}, "\n"
+            );
+            $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+            next PROTOCOL;
+        }
+
+        # Check on protocol text length.  Arbitrary limit; found to work
+        # quite well in practice, though.
+        unless ( length( $protocol->{$EDF_PROTOCOLTEXT} ) > 50 ) {
+            $self->logprint( 'error',
+                      q{Warning: Description for }
+                    . ( $protocol->{$EDF_PROTOCOLTYPE} || q{} )
+                    . q{ protocol "}
+                    . ( $protocol->{$EDF_PROTOCOLNAME} || q{} )
+                    . qq{" is too short.\n} );
+            $self->add_error( $CONFIG->get_ERROR_MIAME() );
+        }
+
+        my $protocol_referenced;
+        foreach my $datarow ( @{ $tab2mage->get_hyb_section() || [] } ) {
+            foreach my $heading (@protocol_headings) {
+                $protocol_referenced++
+                    if ( $datarow->{$heading}
+                    && $datarow->{$heading} eq
+                    $protocol->{$EDF_PROTOCOLACCESSION} );
+            }
+        }
+        $unreferenced{ $protocol->{$EDF_PROTOCOLACCESSION} }++
+            unless ($protocol_referenced);
+    }
+
+    # Check that all referenced protocols are declared
+    foreach my $datarow ( @{ $tab2mage->get_hyb_section() || [] } ) {
+
+        PROTOCOL_COLUMN:
+        foreach my $heading (@protocol_headings) {
+
+            # allow blanks in the spreadsheet
+            next PROTOCOL_COLUMN unless $datarow->{$heading};
+
+            my $protocol_declared;
+            foreach my $protocol ( @{ $tab2mage->get_prot_section() || [] } ) {
+                $protocol_declared++
+                    if ( $datarow->{$heading} eq
+                    $protocol->{$EDF_PROTOCOLACCESSION} );
+            }
+            $undeclared{ $datarow->{$heading} }++ unless ($protocol_declared);
+        }
+    }
+
+    while ( my ( $protocol, $count ) = each %unreferenced ) {
+        $self->logprint( 'error',
+            "Warning: Protocol $protocol is declared in the Protocol section but not referenced in the Hybridization section.\n"
+        );
+        $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+    }
+
+    while ( my ( $protocol, $count ) = each %undeclared ) {
+        $self->logprint( 'error',
+            "Warning: Protocol $protocol is referenced in the Hybridization section ($count times) but not declared in the Protocol section.\n"
+        );
+        $self->add_error( $CONFIG->get_ERROR_MIAME() );
+    }
+
+    # Normalization and transformation protocols have to be checked in
+    # the $datafile_rowlist construct as they aren't created in the MAGE
+    # objects until the data files have been fully processed.
+
+    my $protocol_found = $self->_check_datatrans_protocol(
+	$tab2mage,
+	'normalized',
+        $EDF_NORM_PROTOCOL,
+        'No normalization protocol associated with normalized file',
+    );
+    $protocol_found ||= $self->_check_datatrans_protocol(
+        $tab2mage,
+        $CONFIG->get_FGEM_FILE_TYPE(),
+        $EDF_TRXN_PROTOCOL,
+        'No transformation protocol associated with final data matrix file',
+    );
+
+    # Note the results for MIAME compliance tracking.
+    if ($protocol_found) {
+	$self->add_miame($CONFIG->get_MIAME_NORMPROTOCOL);
+    }
+    else {
+	$self->logprint(
+	    'miame',
+	    "Problem: Data transformation protocol not found.\n"
+	);
+    }
+
+    return;
+}
+
+sub _check_parsed_protocols : PRIVATE {
+
+    my ( $self, $bag_of ) = @_;
+
+    # Check through for biomaterial treatments lacking protocols
+    foreach my $bm qw(biosample extract immunoprecipitate labeledextract) {
+	$self->check_mage_treatments_protapps(
+	    $bag_of->{$bm}->(),
+	    $bm,
+	);
+    }
+
+    # Hybs lacking protocols.
+    $self->check_mage_hyb_protapps( $bag_of->{pba}->() );
+
+    # Check scan protocol (now defaults to MBA FeatureExtraction).
+    foreach my $mba ( @{ $bag_of->{mba}->() } ) {
+        my $fext = $mba->getFeatureExtraction();
+	my $found;
+	foreach my $pa ( @{ $fext->getProtocolApplications() || [] } ) {
+	    $found++ if $pa->getProtocol();
+	}
+	unless ( $found ) {
+	    $self->logprint(
+		'error',
+		sprintf(
+		    qq{Warning: Hybridization "%s" lacks a scanning protocol.\n},
+		    $mba->getName(),
+		),
+	    );
+	    $self->add_error( $CONFIG->get_ERROR_MIAME() );
+	}
+    }
+
+    # Normalization protocol can't be checked here, as dbad/mbad
+    # creation is coupled to file processing. See _check_protocols()
+
+    return;
+}
+
+sub _check_parameters : PRIVATE {
+
+    my ( $self, $tab2mage ) = @_;
+
+    # Check that all referenced parameters are declared, and vice versa
+    my %known_params;
+    foreach my $protocol ( @{ $tab2mage->get_prot_section() || [] } ) {
+        if ( $protocol->{$EDF_PROTOCOLPARAMS} ) {
+            foreach my $name ( keys %{ $protocol->{$EDF_PROTOCOLPARAMS} } ) {
+                $known_params{$name}++;
+            }
+        }
+    }
+
+    # Find the problem parameters
+    my ( %referenced, %undeclared );
+
+    my $regexp_str = "$EDF_PARAM_PREFIX(.*)$EDF_PARAM_SUFFIX";
+    $regexp_str =~ s/([\[\]])/\\$1/g;
+
+    foreach my $datarow ( @{ $tab2mage->get_hyb_section() || [] } ) {
+        foreach my $column ( keys %$datarow ) {
+            if ( my ($name) = ( $column =~ m/$regexp_str$/ ) ) {
+                $referenced{$name}++;
+                unless ( $known_params{$name} ) { $undeclared{$name}++; }
+            }
+        }
+    }
+
+    # Print out the errors
+    foreach my $name ( keys %undeclared ) {    # referenced but not declared
+        $self->logprint( 'error',
+            qq{Warning: Parameter "$name" is not declared in the Protocol spreadsheet section.\n}
+        );
+        $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+    }
+    foreach my $name ( keys %known_params ) {    # declared but not referenced
+        unless ( $referenced{$name} ) {
+            $self->logprint( 'error',
+                qq{Warning: Parameter "$name" is not referenced in the Hybridization spreadsheet section.\n}
+            );
+            $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+        }
+    }
+
+    return;
+}
+
+sub _datafile_to_filelist : PRIVATE {
+
+    my ( $self, $tab2mage ) = @_;
+
+    # Sort out our known file types
+    my $known_types;
+
+    # Standard file types
+    foreach my $filetype ( @{ $CONFIG->get_T2M_FILE_TYPES() || [] },
+        $CONFIG->get_FGEM_FILE_TYPE() ) {
+        $known_types->{$filetype}++;
+    }
+
+    # Other file types
+    my @other_known = qw(image cdf exp);
+    foreach my $type (@other_known) { $known_types->{$type}++; }
+
+    # hashref for tracking processed files.
+    my $filelist   = {};
+    my $regexp_str = "$EDF_FILE_PREFIX(.*)$EDF_FILE_SUFFIX";
+    $regexp_str =~ s/([\[\]])/\\$1/g;
+
+    foreach my $datarow ( @{ $tab2mage->get_hyb_section() || [] } ) {
+
+        # Check there are no unrecognized file types in the data row
+        HYB_COLUMN:
+        foreach my $column ( keys %$datarow ) {
+            my $matched_type;
+
+            next HYB_COLUMN
+                unless ( ($matched_type) = ( $column =~ m/$regexp_str$/ ) );
+
+            unless ( $known_types->{$matched_type} ) {
+                $self->logprint( 'error',
+                    "Warning: Unrecognized file type '$matched_type' will be ignored.\n"
+                );
+                $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+            }
+        }
+
+        my $readablename = $tab2mage->get_readablename($datarow);
+
+        # defaults to raw filename if available
+        my $hyb_name = $datarow->{$EDF_HYBRIDIZATIONNAME} || $readablename;
+
+        # populate $file_list
+        FILE_TYPE:
+        foreach my $filetype (
+            @{ $CONFIG->get_T2M_FILE_TYPES() || [] },
+            $CONFIG->get_FGEM_FILE_TYPE()
+            ) {
+
+            my $heading = $EDF_FILE_PREFIX . $filetype . $EDF_FILE_SUFFIX;
+
+            # Skip rows lacking referenced files.
+            next FILE_TYPE unless ( $datarow->{$heading} );
+
+            # Get a Datafile object.
+            my $file;
+	    unless ( $file = $filelist->{ $datarow->{$heading} } ) {
+		my $path;
+		if ( $self->get_skip_data_checks() ) {
+		    $path = $datarow->{$heading};
+		}
+		else {
+		    $path = get_filepath_from_uri(
+			$datarow->{$heading},
+			$self->get_source_directory(),
+		    );
+		}
+                $file = ArrayExpress::Datafile->new({
+		    path            => $path,
+		    name            => basename( $path ),
+		    data_type       => $filetype,
+		    hyb_identifier  => $hyb_name,
+		    array_design_id => $datarow->{$EDF_ARRAYACCESSION} || '{UNKNOWN}',
+		});
+	    }
+
+            # Sort out the FactorValues, adding new ones where we've
+            # seen the file before.
+            my $regexp_str = "$EDF_FV_PREFIX(.*)$EDF_FV_SUFFIX";
+            $regexp_str =~ s/([\[\]])/\\$1/g;
+
+	    # All files are labeled with their respective FVs. This is
+	    # used in DW flagging and (one day) in Pearson analysis
+	    # for QA purposes.
+            foreach my $fv_heading ( keys %$datarow ) {
+                if ( my ($category) = ( $fv_heading =~ m/$regexp_str$/ ) ) {
+                    $file->add_factor_value( $category, $datarow->{$fv_heading} );
+                }
+            }
+
+            # Do the full processing on each file just once.
+            next FILE_TYPE if ( $filelist->{ $datarow->{$heading} } );
+
+	    $filelist->{ $datarow->{$heading} } = $file;
+        }
+
+        # do EXP files here
+        my $exp_heading = $EDF_FILE_PREFIX . 'exp' . $EDF_FILE_SUFFIX;
+        if ( $datarow->{$exp_heading} ) {
+
+            my $file;
+	    unless ( $file = $filelist->{ $datarow->{$exp_heading} } ) {
+		my $path;
+		if ( $self->get_skip_data_checks() ) {
+		    $path = $datarow->{$exp_heading};
+		}
+		else {
+		    $path = get_filepath_from_uri(
+			$datarow->{$exp_heading},
+			$self->get_source_directory(),
+		    );
+		}
+
+		# N.B. we really don't care about the array_design_id
+		# for EXP files at this stage, but it's a required
+		# attribute.
+                $file = ArrayExpress::Datafile->new({
+		    path            => $path,
+		    name            => basename( $path ),
+		    data_type       => 'raw',
+		    hyb_identifier  => $hyb_name,
+		    array_design_id => $datarow->{$EDF_ARRAYACCESSION} || '{UNKNOWN}',
+		    is_exp          => 1,
+		});
+	    }
+
+	    $filelist->{ $datarow->{$exp_heading} } = $file;
+        }
+    }
+
+    # Return the list of files in filename order
+    return [ map { $filelist->{$_} } sort keys %$filelist ];
+}
+
+sub _print_edf_workflow : PRIVATE {
+
+    my ( $self, $bag_of ) = @_;
+
+    # keys are from Tab2MAGE::create_bags()
+    my @classes = (
+        { biosource         => 'BioSources'            },
+        { biosample         => 'Samples'               },
+        { extract           => 'Extracts'              },
+        { immunoprecipitate => 'IPs'                   },
+        { labeledextract    => 'Labeled extracts'      },
+        { pba               => 'Hybridizations'        },
+        { mbad              => 'Raw data files'        },
+        { dbad              => 'Normalized data files' },
+    );
+
+    $self->generate_workflow_from_bags( $bag_of, [ @classes ] );
+
+    return;
+}
+
+sub visualize_experiment : RESTRICTED {
+
+    my ( $self, $filelist ) = @_;
+
+    my $tab2mage = $self->get_mage_generator();
+
+    FILE:
+    foreach my $file ( @{ $filelist || [] } ) {
+	if (  $file->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE()
+	   || $file->get_data_type() eq $CONFIG->get_RAW_DM_FILE_TYPE() ) {
+
+	    # This can crash, e.g. if the data matrix is malformed.
+	    my $sighandler = $SIG{__DIE__};
+	    delete $SIG{__DIE__};
+	    local $EVAL_ERROR;
+	    eval{
+		my $dm = ArrayExpress::Datafile::DataMatrix->new();
+		$dm->create_mage($file, {}, $tab2mage, q{});
+	    };
+	    $SIG{__DIE__} = $sighandler if $sighandler;
+	    if ($EVAL_ERROR) {
+		warn(sprintf(
+		    "Error parsing data matrix %s (%s); graph visualization will omit this file.",
+		    $file->get_name(),
+		    $EVAL_ERROR,
+		));
+		last FILE;
+	    }
+	}
+    }
+
+    # Create the dotfile (and PNG if GraphViz installed)
+    my $output_base = $self->get_spreadsheet_filename();
+    $output_base =~ s/\.\w{3}$//;
+    $output_base ||= 'tab2mage';    # Just in case
+
+    if ( defined($self->get_log_to_current_dir) ) {
+        my $output_dir = $self->get_log_to_current_dir || q{.};
+	my ($vol, $dir, $name) = File::Spec->splitpath($output_base);
+	$output_base = File::Spec->catfile($output_dir, $name);
+    }
+
+    # we'll just use the default font here.
+    require ArrayExpress::Curator::Visualize;
+    ArrayExpress::Curator::Visualize->import(
+	qw(dot_and_png)
+    );
+    $self->set_clobber(
+        dot_and_png(
+	    $tab2mage->get_bags(),
+	    $output_base,
+	    undef,
+	    $self->get_clobber(),
+	)
+    );
+
+    return;
+}
+
+sub validate_edf : PRIVATE {
+
+    # Parses MAGE objects out of a spreadsheet, reports on any problems
+    # found.  This does not touch the data files (they are checked later
+    # in Datafile.pm).
+
+    my ( $self ) = @_;
+
+    # Use dummy accession, domain for internal MAGE generation. Don't
+    # reassign protocol accessions yet.
+    my $tab2mage = ArrayExpress::Curator::Tab2MAGE->new({
+	spreadsheet_filename => $self->get_spreadsheet_filename(),
+	target_directory     => q{.},
+	external_accession   => '{ACCESSION}',
+	external_domain      => '{DOMAIN}',
+	keep_protocol_accns  => 1,
+    });
+
+    $self->logprint_line( 'error', 'Spreadsheet parsing START' );
+
+    # Parse the EDF
+    my $rc = $tab2mage->read_edf( $self->log_fh('error') );
+    $self->add_error($rc);
+    my $exptinfo = $tab2mage->get_expt_section();
+    my $bag_of   = $tab2mage->get_bags();
+
+    # Create and link the mage objects
+    $tab2mage->create_protocols();
+    foreach my $datarow ( @{ $tab2mage->get_hyb_section() || [] } ) {
+        $tab2mage->create_mage_objects(
+	    $datarow,
+            unique_identifier(),
+	);
+    }
+
+    # Count BioMaterials -> Hybs
+    $self->_print_edf_workflow( $bag_of );
+
+    if ( $exptinfo->{$EDF_EXPTDESCRIPTION} ) {
+        format_description(
+	    $exptinfo->{$EDF_EXPTDESCRIPTION},
+            $self->log_fh('report'),
+	);
+    }
+
+    # Check for standard journal name
+    if ( $exptinfo->{$EDF_PUBJOURNAL} ) {
+        my $entrez_approved = parse_entrez_names;
+        unless ( $entrez_approved->{ $exptinfo->{$EDF_PUBJOURNAL} } ) {
+            $self->logprint(
+                'error',
+                "Warning: Non-standard publication name: ",
+                $exptinfo->{$EDF_PUBJOURNAL}, "\n"
+            );
+            $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+        }
+    }
+
+    # Check for numeric-only pubmed ID.
+    if ( $exptinfo->{$EDF_PUBMEDID} ) {
+	if ( $exptinfo->{$EDF_PUBMEDID} =~ m/\D/ ) {
+	    $self->logprint(
+		'error',
+		"Warning: PubMed ID contains non-numeric characters: ",
+		$exptinfo->{$EDF_PUBMEDID}, "\n",
+	    );
+	    $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+	}
+    }
+
+    # Check that appropriate dates have been set, and are the correct format.
+    $self->check_date_format($exptinfo->{$EDF_EXPTRELEASEDATE},    'release' );
+    $self->check_date_format($exptinfo->{$EDF_EXPTSUBMISSIONDATE}, 'submission' );
+    if ( $exptinfo->{$EDF_EXPTGEORELEASEDATE} ) {
+	$self->check_date_format($exptinfo->{$EDF_EXPTGEORELEASEDATE}, 'GEO release' );
+    }
+
+    # Check the submitter details.
+    if ( !$exptinfo->{$EDF_EXPTSUBMITTER} ) {
+        $self->logprint( 'error',
+            "Warning: Experiment submitter has no name.\n");
+        $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+    }
+    if ( !$exptinfo->{$EDF_EXPTSUBMITTER_EMAIL} ) {
+        $self->logprint( 'error',
+            "Warning: Submitter email address is missing.\n" );
+        $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+    }
+
+    # Collect ExperimentDesignType terms for DW checking. We use the
+    # MAGE.pm subroutine for parsing the appropriate $exptinfo tag.
+    my $experimentdesign = $tab2mage->new_experimentdesign();
+    $self->add_expt_designs(
+	map { $_->getValue() }
+	    @{ $experimentdesign->getTypes() || [] }
+    );
+
+    # Also collect Organism OEs.
+    foreach my $source ( @{ $bag_of->{biosource}->() } ) {
+	foreach my $char ( @{ $source->getCharacteristics() || [] } ) {
+	    if ( $char->getCategory() eq 'Organism' ) {
+		$self->add_expt_organisms( $char->getValue() );
+	    }
+	}
+    }
+
+    # Check that all variable BMCs are also declared as FVs.
+    $self->check_bmchars_against_fvs( $bag_of );
+
+    # Checks on protocols and parameters
+    $self->_check_protocols( $tab2mage );
+    $self->_check_parsed_protocols( $bag_of );
+    $self->_check_parameters( $tab2mage );
+
+    # all OEs (warn) FIXME;
+
+    # Missing columns or situations where defaults will be
+    # used. (e.g. MaterialType) FIXME this is actually non-trivial
+    # without re-writing the parser.
+
+    # Number of FVs / Arrays / BMCs per hyb/biosource
+    $self->_check_hybridizations( $bag_of );
+    $self->check_mage_bmchars( $bag_of->{'biosource'}->() );
+
+    # Check Labeled Extract - Label association is present.
+    $self->check_mage_labeledextracts( $bag_of->{'labeledextract'}->() );
+
+    # Insert files into @$filelist here. Note $file->get_is_exp() flag is
+    # populated.  Also checks File[*] headings that will be ignored.
+    my $filelist = $self->_datafile_to_filelist( $tab2mage );
+
+    $self->logprint_line( 'error', 'Spreadsheet parsing END' );
+
+    # Table of files per hyb
+    $self->logprint( 'report',
+        "\nChecking for presence of raw and/or normalized data files:\n" );
+    $self->logprint( 'report', datafile_consistency_table($filelist) );
+
+    # List of PBA ids for FGEM checking
+    my $hyb_ids = {};
+    foreach my $pba ( @{ $bag_of->{pba}->() } ) {
+        $hyb_ids->{ $pba->getName }++;
+    }
+
+    # List of DBA ids for FGEM checking
+    my $norm_ids = {};
+    foreach my $dba ( @{ $bag_of->{dba}->() } ) {
+        $norm_ids->{ $dba->getName }++;
+    }
+
+    $self->set_mage_generator( $tab2mage );
+
+    return ( $filelist, $hyb_ids, $norm_ids );
+
+}
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../index.html">
+                <img src="../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: Validate.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Curator::Validate - a module used by expt_check.pl
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Curator::Validate;
+ 
+ my $checker = ArrayExpress::Curator::Validate->new({
+     spreadsheet_filename => $tab2mage_file,
+ });
+ 
+ $checker->check();
+
+=head1 DESCRIPTION
+
+This module provides a set of subroutines used by the experiment
+checker script to confirm the MIAME metadata supplied in a Tab2MAGE
+spreadsheet.
+
+=head1 OPTIONS
+
+The following options must be used in addition to those provided by the
+parent class (see L<ArrayExpress::Curator::ExperimentChecker>):
+
+=over 2
+
+=item C<spreadsheet_filename>
+
+The Tab2MAGE spreadsheet to check.
+
+=back
+
+=head1 TESTS
+
+The following tests are performed by this module, with output
+printed to the error and/or report filehandles:
+
+=over 4
+
+=item B<Hybridizations>
+
+Confirms that each hybridization is associated with an array 
+design (i.e., ArrayExpress accession number), and at least 
+one experimental factor value.
+
+=item B<BioMaterialCharacteristics>
+
+Checks that each BioSource has at least some annotation. 
+This is not, however, a full test of MIAME compliance.
+
+=item B<Undeclared Experimental Factors>
+
+Checks all material characteristics against the factors described by
+the document, alerting the user if any such characteristics vary
+during the experiment without having been declared as an experimental
+factor.
+
+=item B<Protocols>
+
+Checks that all protocols referenced in the Hybridization spreadsheet 
+section are declared in the Protocol section (and vice versa).
+
+=item B<Protocol accessions>
+
+Confirms that all declared protocols in the Protocol section have accession numbers.
+
+=item B<Protocol text length>
+
+Warns if any of the protocol texts seem too brief.
+
+=item B<Protocol presence and usage>
+
+Checks that a protocol has been attached to each step of the 
+experiment (biomaterial treatments, hybridization, scanning and normalization).
+
+=item B<Parameters>
+
+Checks that all parameters referenced in the Hybridization spreadsheet 
+section are declared in the Protocol section (and vice versa).
+
+=item B<BioMaterials>
+
+Creates the links between sample, extract, labeled extract and
+hybridization, and writes a biomaterials log file listing the 
+numbers of each. If the Graphviz software is installed 
+(http://www.graphviz.org) then the script will use the 'dot' 
+program to produce a PNG format graph showing how the
+various components relate to each other.
+
+=item B<Publication journal>
+
+Warns the user if the specified publication journal name is not 
+in the included list of standard Entrez journal abbreviations.
+
+=item B<PubMed ID>
+
+Warns the user if a non-numeric PubMed ID has been entered.
+
+=item B<Submission and release dates>
+
+Warns the user if submission and/or release dates have not been
+specified.
+
+=item B<Submitter>
+
+Checks that both submitter name and email have been provided.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+1;
diff --git a/lib/ArrayExpress/Curator/Visualize.pm b/lib/ArrayExpress/Curator/Visualize.pm
new file mode 100644
index 0000000..aef1585
--- /dev/null
+++ b/lib/ArrayExpress/Curator/Visualize.pm
@@ -0,0 +1,356 @@
+#!/usr/bin/env perl
+#
+# Module to provide visualization subroutines for tab2mage spreadsheets.
+#
+# Tim Rayner 2005, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Visualize.pm 1941 2008-02-11 12:27:15Z tfrayner $
+#
+
+package ArrayExpress::Curator::Visualize;
+
+use strict;
+use warnings;
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../index.html">
+                <img src="../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: Visualize.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Curator::Visualize - a module used by expt_check.pl
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Curator::Visualize qw(dot_and_png);
+ 
+ dot_and_png( $mage_bags, 'my_expt', 'luxisr', 1 );
+
+=head1 DESCRIPTION
+
+This module provides a set of subroutines used by the experiment
+checker script to confirm the MIAME metadata supplied in a Tab2MAGE
+spreadsheet.
+
+=head1 FUNCTIONS
+
+=over 2
+
+=item C<dot_and_png( $mage_bags, $output_base, $font, $clobber, $classes )>
+
+Takes: a hashref of MAGE containers, as generated by Tab2MAGE, MAGE
+and MAGE-TAB classes; a string for the output filenames (minus
+extension, so e.g. "out" becomes "out.dot" and "out.png"); an optional
+font name; an optional flag indicating whether files should be
+silently overwritten; an optional hashref of classes to draw. This
+last argument will usually be omitted. Generates the dot file and
+passes it to the Graphviz dot for drawing. Returns a new $clobber
+argument.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2008.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+use Carp;
+use IO::File;
+use Readonly;
+
+use ArrayExpress::Curator::MAGE::Definitions qw(
+    $AE_LABELCOMPOUND_PREFIX
+);
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+use base 'Exporter';
+our @EXPORT_OK = qw(
+    dot_and_png
+);
+
+Readonly my $LIGHT_YELLOW => '#f8ff98';
+Readonly my $RED          => '#ff5454';
+Readonly my $GREEN        => '#81ff6d';
+Readonly my $YELLOW       => '#fff835';
+Readonly my $LIGHT_BLUE   => '#add9e6';
+Readonly my $GREY         => '#c5c5c5';
+Readonly my $MAUVE        => '#9b7bff';
+
+sub _check_file {
+
+    my ( $file, $clobberref ) = @_;
+
+    if ( -e $file && !$$clobberref ) {
+        print STDERR ( "\n\nFile \'$file\' aleady exists. "
+                . "Overwrite? [Y(es)\/N(o)\/A(ll)]\n" );
+        chomp( my $answer = lc <STDIN> );
+
+        SWITCH:
+        {
+            $answer eq 'y' && do { last SWITCH; };
+            $answer eq 'a' && do { $$clobberref = 1; last SWITCH; };
+            return (0);
+        }
+    }
+
+    return (1);
+
+}
+
+sub _mysyscall {
+
+    # Wrapper sub for the system() call.
+
+    my $systemcall = shift();
+
+    my $rc = 0xffff & system($systemcall);
+    my $ok = ( $rc == 0 );
+    return $ok;
+}
+
+sub dot_and_png {
+
+    my ( $bag_of, $output_base, $font, $clobber, $classes ) = @_;
+
+    $font ||= $CONFIG->get_VISUALIZE_FONT();
+
+    my $dotfile = "$output_base.dot";
+
+    unless ( _check_file( $dotfile, \$clobber ) ) {
+        print STDERR ("Aborting creation of dot and png files.\n");
+        return ($clobber);
+    }
+
+    print STDOUT ("Creating 'dot' graph file...\n");
+
+    my $dot_fh = IO::File->new( $dotfile, '>' )
+        or croak("Unable to open $dotfile for writing.\n");
+
+    print $dot_fh (<<'END_HEADER');
+digraph "Experiment"{
+rankdir=LR; 
+rankspace=200
+END_HEADER
+
+    # keys are from Tab2MAGE->initialize_bags()
+    $classes ||= {
+        biosource         => 'BioSource',
+        biosample         => 'Sample',
+        extract           => 'Extract',
+        immunoprecipitate => 'IP',
+        labeledextract    => 'Labeled Extract',
+        pba               => 'Hybridization',
+        mba               => 'Scan',
+        datamatrix_mba    => 'Scan',
+        dba               => 'Normalization',
+        datamatrix_dba    => 'Transformation',
+    };
+
+    my %colors = (
+        Cy3    => $GREEN,
+        Cy5    => $RED,
+        biotin => $MAUVE,
+    );
+
+    CLASS:
+    foreach my $class ( keys %$classes ) {
+
+	unless ( ref($bag_of->{$class}) eq 'CODE' ) {
+	    croak("ERROR: no CODE ref available for $class container during visualization.");
+	}
+
+        OBJECT:
+        foreach my $object ( @{ $bag_of->{$class}->() } ) {
+
+            my $identifier = $object->getIdentifier();
+
+            # Default color for nodes
+            my $color = $LIGHT_BLUE;
+            my $name  = $object->getName();
+            my $type  = $classes->{$class};
+
+            # Exceptions to the light_blue rule:
+            # 1. Labels
+            if ( $object->can('getLabels') ) {
+
+                my @labels = @{ $object->getLabels() || [] };
+
+                # More than one dye
+                if ( @labels > 1 ) { $color = $YELLOW; }
+
+                # No dye
+                elsif ( @labels < 1 ) { $color = $GREY; }
+
+                # Just right
+                else {
+
+                    my $dye = $labels[0]->getIdentifier();
+                    $dye =~ s/^$AE_LABELCOMPOUND_PREFIX//;
+
+                    if ( $colors{$dye} ) { $color = $colors{$dye}; }
+
+                    # if dye not recognized, color the same as default
+                }
+            }
+
+            # 2. Data files
+            elsif ( $object->can('getBioDataValues') ) {
+                $color = $LIGHT_YELLOW;
+            }
+
+            # 3. MeasuredBioAssays
+            elsif ( $object->can('getMeasuredBioAssayData') ) {
+                $color = $LIGHT_YELLOW;
+            }
+
+            # 4. DerivedBioAssays
+            elsif ( $object->can('getDerivedBioAssayData') ) {
+                $color = $LIGHT_YELLOW;
+            }
+
+            print $dot_fh ( qq{"$identifier" [label="$name\\n$type",}
+                    . qq{ color=black, shape=box, style=filled,}
+                    . qq{ color="$color", fontname=$font];\n} );
+
+            # Sort out our edges between nodes
+            my %sources;
+
+            # BioMaterials
+            if ( $object->can('getTreatments') ) {
+
+                my @treatments = @{ $object->getTreatments() || [] };
+
+                foreach my $treat (@treatments) {
+
+                    if ( my $bmms
+                        = $treat->getSourceBioMaterialMeasurements() ) {
+                        foreach my $bmm ( @{$bmms} ) {
+                            my $id = $bmm->getBioMaterial()->getIdentifier();
+                            $sources{$id}++;
+                        }
+                    }
+                }
+            }
+
+            # Hybridization PBAs
+            if ( $object->can('getBioAssayCreation') ) {
+
+		if ( my $bac = $object->getBioAssayCreation() ) {
+
+		    my $bmms = $bac->getSourceBioMaterialMeasurements();
+
+		    foreach my $bmm ( @{ $bmms || [] } ) {
+			$sources{ $bmm->getBioMaterial()->getIdentifier() }++;
+		    }
+		}
+            }
+
+            # MBAs
+            if ( $object->can('getFeatureExtraction') ) {
+
+		if ( my $fext = $object->getFeatureExtraction() ) {
+
+		    my $source = $fext->getPhysicalBioAssaySource();
+
+		    BIOASSAY:
+		    foreach my $hyb_pba ( @{ $bag_of->{'pba'}->() } ) {
+
+			# Single channel is easy.
+			if ( $source->getIdentifier() eq $hyb_pba->getIdentifier() ) {
+			    $sources{ $hyb_pba->getIdentifier() }++;
+			    next BIOASSAY;
+			}
+
+			# Multi-channel is a bit harder. We assume a
+			# chain of three PBAs here: hyb, channel and
+			# merged. This is the standard two-colour
+			# coding supported by the Tab2MAGE and
+			# MAGE-TAB parsers.
+			foreach my $bat ( @{ $hyb_pba->getBioAssayTreatments() || [] } ) {
+			    my $ch_pba = $bat->getTarget() or next BIOASSAY;
+			    foreach my $ch_bat ( @{ $ch_pba->getBioAssayTreatments() || [] } ) {
+				my $target = $ch_bat->getTarget() or next BIOASSAY;
+				if ( $source->getIdentifier() eq $target->getIdentifier() ) {
+				    $sources{ $hyb_pba->getIdentifier() }++;
+				}
+			    }
+			}
+		    }
+                }
+            }
+
+            # DBAs
+            if ( $object->can('getDerivedBioAssayMap') ) {
+
+                my @source_ba_lists = map { $_->getSourceBioAssays() }
+                    @{ $object->getDerivedBioAssayMap() || [] };
+
+                foreach my $list (@source_ba_lists) {
+                    foreach my $ba (@$list) {
+                        $sources{ $ba->getIdentifier() }++;
+                    }
+                }
+            }
+
+            # Write all the links to the dot file.
+            foreach my $source ( keys %sources ) {
+                print $dot_fh ( qq{"$source"->"$identifier"[color=black];\n});
+            }
+        }
+    }
+
+    print $dot_fh "}\n";
+
+    close($dot_fh) or croak("Error closing output filehandle: $!\n");
+
+    my $pngfile = "$output_base.png";
+
+    unless ( _check_file( $pngfile, \$clobber ) ) {
+        print STDERR ("Aborting creation of PNG file.\n");
+        return ($clobber);
+    }
+
+    print STDOUT ("Creating PNG graph file...\n");
+
+    _mysyscall(qq{dot -Tpng -o "$pngfile" "$dotfile"})
+        or print STDERR (
+        "Error: Graph drawing requires that the Graphviz 'dot' program is installed and on your executable path.\n"
+        );
+
+    return $clobber;
+
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile.pm b/lib/ArrayExpress/Datafile.pm
new file mode 100644
index 0000000..caae04b
--- /dev/null
+++ b/lib/ArrayExpress/Datafile.pm
@@ -0,0 +1,3685 @@
+#!/usr/bin/env perl
+#
+# Datafile.pm - an OO module derived from and used in the experiment
+# checker script. Contains routines which might be useful elsewhere.
+#
+# Tim Rayner 2005 ArrayExpress team, EBI
+#
+# $Id: Datafile.pm 2079 2008-06-12 08:43:31Z tfrayner $
+#
+
+package ArrayExpress::Datafile;
+
+use strict;
+use warnings;
+
+use charnames qw( :full );
+
+use Class::Std;
+use English qw( -no_match_vars );
+use Carp;
+use File::Spec;
+use Storable qw(dclone);
+use IO::File;
+use Scalar::Util qw(looks_like_number openhandle);
+use List::Util qw(sum first);
+use List::MoreUtils qw(any none);
+use Digest::MD5 qw(md5_hex);
+
+use ArrayExpress::Curator::Common qw(
+    strip_discards
+    get_indexcol
+    check_linebreaks
+    $RE_EMPTY_STRING
+    $RE_LINE_BREAK
+    $RE_SURROUNDED_BY_WHITESPACE
+    $RE_WITHIN_BRACKETS
+);
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../index.html">
+                <img src="../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: Datafile.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile.pm - an OO module providing methods
+for parsing data files.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile;
+ my $file = ArrayExpress::Datafile->new({
+     name            => 'data.txt',
+     data_type       => 'raw',
+     array_design_id => 'A-MEXP-123',
+ });
+
+=head1 DESCRIPTION
+
+This is a module providing methods for data file parsing. See also
+L<ArrayExpress::Datafile::Parser> for Datafile handler objects.
+
+=cut
+
+# Incrementable integer accessors
+my %row_count            : ATTR( :name<row_count>,        :default<0> );
+my %parse_errors         : ATTR( :name<parse_errors>,     :default<0> );
+my %not_null             : ATTR( :name<not_null>,         :default<0> );
+
+# Hash for further mutator construction (below).
+my %incr_integer_attr = (
+    row_count    => \%row_count,
+    parse_errors => \%parse_errors,
+    not_null     => \%not_null,
+);
+
+# Plain boolean flag accessors
+my %is_miamexpress       : ATTR( :name<is_miamexpress>,   :default<0> );
+my %is_exp               : ATTR( :name<is_exp>,           :default<0> );
+my %is_binary            : ATTR( :default<0> );   # Autodetected
+my %is_illumina_fgem     : ATTR( :default<undef> );
+
+# String accessors
+my %hyb_identifier       : ATTR( :name<hyb_identifier>,   :default<q{}> );
+my %hyb_sysuid           : ATTR( :name<hyb_sysuid>,       :default<q{}> );
+my %array_design_id      : ATTR( :set<array_design_id>, :init_arg<array_design_id>, :default<q{}> );
+my %datamatrix_chip_type : ATTR( :set<dm_chip_type>,      :default<q{}> );
+my %ded_identifier       : ATTR( :name<ded_identifier>,   :default<q{}> );
+my %test_data_line       : ATTR( :name<test_data_line>,   :default<q{}> );
+my %md5_digest           : ATTR( :set<md5_digest>,        :default<q{}> );
+my %qt_type              : ATTR( :name<qt_type>,          :default<q{}> );
+my %path                 : ATTR( :name<path>,             :default<undef> );
+my %target_filename      : ATTR( :name<target_filename>,  :default<undef> );
+my %sdrf_id_column       : ATTR( :name<sdrf_id_column>,   :default<undef> );
+
+# Hashref accessors
+my %data_metrics         : ATTR( :name<data_metrics>,     :default<{}> );
+# my $intensity_vector     : ATTR( :name<intensity_vector>, :default<{}> );
+
+# Arrayref accessors
+my %index_columns        : ATTR( :name<index_columns>,    :default<[]> );
+my %column_headings      : ATTR( :name<column_headings>,  :default<[]> );
+my %heading_qts          : ATTR( :name<heading_qts>,      :default<[]> );
+my %heading_hybs         : ATTR( :name<heading_hybs>,     :default<[]> );
+
+# Accumulative hashref accessor hashes (mutator and accessor methods below)
+my %fail_columns         : ATTR( :default<{}> );
+my %fail_hybs            : ATTR( :default<{}> );
+
+# Hash for further mutator construction (below).
+my %accum_hashref_attr = (
+    fail_columns => \%fail_columns,
+    fail_hybs    => \%fail_hybs,
+);
+
+# More specialized cases; mutators below
+my %factor_value         : ATTR( :get<factor_value>,      :default<{}> );
+my %name                 : ATTR( :get<name>,              :default<undef>, :init_arg<name> );
+my %exp_data             : ATTR( :get<exp_data>,          :default<{}> );
+my %mage_qtd             : ATTR( :get<mage_qtd>,          :default<undef> );
+my %mage_badata          : ATTR( :name<mage_badata>,      :default<undef> );
+my %filehandle           : ATTR( :set<filehandle>,        :default<undef> );
+
+# Enumerated types with validating mutators (below)
+my %ded_type             : ATTR( :get<ded_type>,          :default<q{}> );
+my %format_type          : ATTR( :get<format_type>,       :default<q{}> );
+my %data_type            : ATTR( :get<data_type>,         :default<q{}>,   :init_arg<data_type> );
+my %linebreak_type       : ATTR( :set<linebreak_type>     :default<undef> );
+
+# Occasionally useful as a dummy object, in which case turn off
+# constructor argument validation. Particularly useful in unit testing.
+my %is_dummy             : ATTR( :get<is_dummy>, :default<undef>, :init_arg<is_dummy> );
+
+# Error filehandle, allowing STDERR messages to be captured.
+my %error_fh             : ATTR( :name<error_fh>, :default<\*STDOUT> );
+
+# ArrayDesign objects linked via this attribute.
+my %array_design         : ATTR( :name<array_design>,     :default<undef> );
+
+sub START {
+
+    my ($self, $id, $args) = @_;
+
+    # This sets the path as well, if not already initialized (this
+    # must be done after default attribute initialization).
+    $self->set_name($args->{name}) if $args->{name};
+
+    # A little validation of attributes.
+    if ( ! $self->get_is_dummy() ) {
+
+	if ( ! $self->get_name() ) {
+	    croak("Error: File name attribute not set.");
+	}
+	if ( ! $self->get_data_type() ) {
+	    croak(
+		sprintf(
+		    "Error: File data_type attribute not set for file %s",
+		    $self->get_name(),
+		)
+	    );
+	}
+	if ( ! $self->get_array_design_id() ) {
+	    croak(
+		sprintf(
+		    "Error: File array_design_id attribute "
+		  . "(e.g. accession number) not set for file %s",
+		    $self->get_name(),
+		)
+	    );
+	}
+    }
+}
+
+#######################
+# Main object methods #
+#######################
+
+sub parse_header {
+
+    my ( $self ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $input_fh = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    # Parse Data Matrix headers.
+    if (   $self->get_data_type() eq $CONFIG->get_RAW_DM_FILE_TYPE()
+	|| $self->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE() ) {
+
+	if ( $self->get_mage_badata() ) {
+
+	    # MAGE-TAB style data matrices.
+	    $self->parse_data_matrix_header();
+	}
+	else {
+
+	    # Old-style FGEMs.
+	    $self->parse_fgem_header();
+	}
+    }
+    else {
+
+	# Check for raw/norm data file formats.
+	$self->parse_header_with_indices( $CONFIG->get_T2M_INDICES() );
+    }
+
+    return $self->get_format_type();
+}
+
+sub get_linebreak_type {
+
+    my ( $self ) = @_;
+
+    unless ( defined $linebreak_type{ ident $self } ) {
+
+	# Check the linebreaks and set the input record separator
+	# accordingly
+	my ( $count, $linebreak ) = check_linebreaks( $self->get_path() );
+
+	unless ( defined $linebreak ) {
+
+	    # If we couldn't decide on a line ending format, we arrive
+	    # here. Set some defaults here:
+	    $self->set_row_count(0);
+	    $self->set_format_type('Unknown');
+
+	    # Set the linebreak type so that we don't try getting it
+	    # again should the following croak() be trapped in an eval().
+	    $linebreak_type{ ident $self } = 'Unknown';
+
+	    # Crash hard. Typically calls to this method are wrapped
+	    # in an eval to capture this error.
+	    my $filename = ( File::Spec->splitpath( $self->get_path() ) )[2];
+	    croak(
+		sprintf(
+		    "ERROR: Cannot parse linebreaks for file %s (%s Unix, %s DOS, %s Mac)\n",
+		    $filename,
+		    $count->{'unix'},
+		    $count->{'dos'},
+		    $count->{'mac'},
+		),
+	    );
+	}
+
+	$linebreak_type{ ident $self } = $linebreak;
+    }
+
+    return $linebreak_type{ ident $self };
+}
+
+sub get_line_format {
+
+    my ( $self ) = @_;
+
+    my $linebreak = $self->get_linebreak_type();
+
+    my %separator = (
+	"\N{LINE FEED}"                    => 'Unix',
+	"\N{CARRIAGE RETURN}\N{LINE FEED}" => 'DOS',
+	"\N{CARRIAGE RETURN}"              => 'Mac',
+	'Unknown'                          => 'Unknown',
+    );
+
+    if ( defined $separator{ $linebreak } ) {
+	return $separator{ $linebreak };
+    }
+    else {
+
+	# Failure here reflects an inconsistency in the linebreaks
+	# supported here and in Common::check_linebreaks, so it
+	# warrants a full confess().
+	confess("Error: Unrecognized linebreaks.");
+    }
+}
+
+sub parse_datafile {
+
+    my ( $self, $QTs, $hyb_ids, $norm_ids ) = @_;
+
+    ref $QTs      eq 'HASH' or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    ref $hyb_ids  eq 'HASH' or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    ref $norm_ids eq 'HASH' or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    # Rewind the file before running any checks.
+    seek($self->get_filehandle, 0, 0)
+	or croak("Error rewinding filehandle: $!");
+
+    my $linebreak;
+    eval {
+	$linebreak = $self->get_linebreak_type();
+    };
+    if ( $EVAL_ERROR ) {
+
+	# Skip this file if linebreak parsing failed.
+	return ( [], $EVAL_ERROR );
+    }
+    local $INPUT_RECORD_SEPARATOR = $linebreak;
+
+    # We now treat all datafiles as though they will be parsed by our
+    # Datafile::Parser system (rather than the MIAMExpress datafile
+    # parsers).
+    $self->parse_header();
+    my $rc = q{};
+    unless ( scalar @{ $self->get_index_columns() } ) {
+	$rc .= "Unable to detect supported data file column headings.\n";
+    }
+
+    if (   $self->get_format_type() =~ /^Illumina/
+        && $self->is_illumina_fgem() ) {
+
+	# Fix Illumina data matrix-style files.
+	$self->rewrite_illumina_as_fgem();
+    }
+    else {
+
+	# Rewrite non-Generic data formats into a temporary Generic
+	# filehandle. This is now all handled within the
+	# fix_known_text_format method.
+	$self->fix_known_text_format();
+    }
+
+    # Allow Normalization IDs as column labels for data matrices.
+    if (   $self->get_data_type() eq $CONFIG->get_RAW_DM_FILE_TYPE()
+        || $self->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE() ) {
+
+	$hyb_ids = { %{ $hyb_ids }, %{ $norm_ids } };
+    }
+
+    # Check the column headings here. This populates %fail_columns and
+    # %fail_hybs
+    my $column_rc;
+    $column_rc = $self->check_column_headings( $QTs, $hyb_ids )
+        unless $rc;
+
+    # feature_coords is an array of "1.1.1.1"-style coord strings.
+    my ( $feature_coords, $parse_rc );
+    ( $feature_coords, $parse_rc ) = $self->_parse_datarows()
+        unless $rc;
+
+    $rc .= $column_rc if $column_rc;
+    $rc .= $parse_rc  if $parse_rc;
+
+    return ( $feature_coords, $rc );
+
+}
+
+sub is_ignored_qt {
+
+    # Method returns true if the argument matches against the
+    # Config.pm list of ignored QTs.
+    my ( $self, $qt ) = @_;
+
+    return ( first { $qt =~ m/\A\s*$_\s*\z/ms }
+		 @{ $CONFIG->get_IGNORED_QTS() || [] } );
+}
+
+sub check_column_headings {
+
+    # need lists of approved QTs, most likely on a per-datafile-format
+    # basis.  also check QT types (MAGE spec; float, boolean, enum? If
+    # standard, what?).  Make sure QTs are free of spaces and other
+    # typos also need the hyb names here for combined data matrix
+    # files.
+
+    my ( $self, $QTs, $hyb_ids ) = @_;
+
+    ref $QTs eq 'HASH' or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    $hyb_ids ||= {};
+
+    # Figure out which software type best represents the column
+    # headings; value will be returned as $self->get_qt_type.
+    my $rc = $self->_derive_consensus_software($QTs);
+
+    my $software_QTs = $self->get_data_metrics();
+
+    HEADING:
+    foreach my $heading ( @{ $self->get_heading_qts() } ) {
+
+        # Skip common column headings (various index columns etc.)
+        next HEADING if ( $self->is_ignored_qt($heading) );
+
+        # Record a failing column if it's not represented in the consensus
+        # software type (qt_type).
+        unless ( $software_QTs && $software_QTs->{$heading} ) {
+            $self->add_fail_columns($heading);
+        }
+
+    }
+
+    # Deal with combined data files
+    if (   $self->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE()
+        || $self->get_data_type() eq $CONFIG->get_RAW_DM_FILE_TYPE() ) {
+
+        HEADING:
+        foreach my $column ( @{ $self->get_heading_hybs() } ) {
+            foreach my $id (@$column) {
+                $self->add_fail_hybs($id) unless $hyb_ids->{$id};
+            }
+        }
+
+        # Internal consistency check
+        croak(    "Error: Hyb and QT numbers differ for file "
+                . $self->get_name()
+                . "\n" )
+            unless ( $#{ $self->get_heading_qts() } == $#{ $self->get_heading_hybs() } );
+
+    }
+
+    return $rc;
+}
+
+sub parse_exp_file {
+
+    # Parse EXP file and capture the relevant metadata. Returns undef
+    # on failure.
+    my $self    = shift;
+    my $section = 'header';
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $expfile = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+
+    my $exp_data = {};
+
+    EXP_LINE:
+    while ( my $line = <$expfile> ) {
+        chomp $line;
+
+        # Just to be doubly sure about DOS-style line endings (FIXME
+        # is this really necessary? chomp() should have done the
+        # job...)
+        $line =~ s/$RE_LINE_BREAK//xms;
+
+        if ( $line =~ m/\A $RE_WITHIN_BRACKETS/xms ) {
+
+            $section = $1;
+
+        }
+        else {
+
+            my @linearray = split /\t/, $line;
+
+            next EXP_LINE unless $linearray[0];    # lose the undefined rows
+
+            $exp_data->{$section}{ $linearray[0] } = $linearray[1]
+                if ( $#linearray == 1 );
+
+        }
+    }
+
+    close $expfile or croak("Unable to close EXP filehandle: $!\n");
+
+    $self->set_exp_data($exp_data);
+
+    return $exp_data;
+}
+
+sub fix_known_text_format {    # This subroutine maps values in file
+                               # format to the appropriate reformatting
+                               # sub. To add a new format, edit the
+                               # Constants.pm file to indicate the
+                               # coordinate columns and set the format CV
+                               # string, write the conversion sub and link
+                               # the two here.
+
+    my ( $self ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $input_fh;
+
+    my $format_type = $self->get_format_type();
+
+    my %dispatch = (
+	'GenePix'           => '_fix_genepix',
+	'ArrayVision'       => '_fix_arrayvision',
+	'ArrayVision_lg2'   => '_fix_arrayvision_lg2',
+	'Agilent'           => '_fix_agilent',
+	'Scanalyze'         => '_fix_scanalyze',
+	'ScanArray'         => '_fix_scanarray',
+	'QuantArray'        => '_fix_scanarray',
+	'Spotfinder'        => '_fix_spotfinder',
+	'MEV'               => '_fix_mev',
+	'BlueFuse'          => '_fix_bluefuse',
+	'UCSFSpot'          => '_fix_ucsfspot',
+	'CodeLink'          => '_fix_codelink',
+	'NimbleScanFeature' => '_fix_nimblescanfeat',
+	'NimbleScanNorm'    => '_fix_nimblescanfeat',
+	'NimblegenNASA'     => '_fix_nimblegennasa',
+	'AppliedBiosystems' => '_fix_appliedbiosystems',
+	'Illumina'          => '_fix_illumina_perhyb',
+	'ImaGene'           => '_fix_imagene',
+	'ImaGene3'          => '_fix_imagene3',
+	'ImaGene7'          => '_fix_genepix',
+	'CSIRO_Spot'        => '_fix_csiro_spot',
+    );
+
+    if ( my $method = $dispatch{$format_type} ) {
+	$input_fh = $self->$method;
+    }
+
+    # Exchange the old filehandle for the new one.
+    $self->set_filehandle($input_fh) if $input_fh;
+
+    # return value is effectively a true = success return code (not always used).
+    return $input_fh;
+
+}
+
+sub strip_and_sort {
+
+    # Sub to rewrite a datafile, stripping out unwanted columns and
+    # substituting null entries.
+
+    my ( $self, $output_file, $new_headings ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $input_fh = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    ref $output_file and confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    ref $new_headings eq 'ARRAY'
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    # Create a temporary file in MC/MR/C/R format; sorted on the fly.
+    # We want to sort our output; here we do it on the fly using unix
+    # sort (this is much faster than using perl).
+    #
+    # Windows support - omit the key flags. N.B. we also have to
+    # explicitly close these filehandles for Windows, otherwise we
+    # cannot delete the temporary files.  Win2000 ActiveState Perl 5.8.6
+    # registers as "MSWin32"
+    #
+    my $sortkeys = q{};
+    unless ( $^O =~ m/\A mswin/ixms ) {
+        $sortkeys = ' -n ';
+        my $columnno = 0;
+
+        # We reorder the file below so that the first four columns are
+        # index columns, regardless of where they are in the original
+        # file.
+        foreach my $index ( 0 .. $#{ $self->get_index_columns() } ) {
+
+            # $index is 0..n; we need 1..(n+1)
+            $sortkeys .= q{ -k } . ( $index + 1 ) . q{ };
+        }
+    }
+
+    my @indexcols = @{ $self->get_index_columns() };
+
+    my @original_headings = @{ $self->get_column_headings };
+
+    my %is_heading_to_keep = map { $_ => 1 } @$new_headings;
+
+    # Organize our new file information before we start
+    # processing. This effectively reorders the columns so that the
+    # indexcols come first.
+    my @fixed_headings = @original_headings[@indexcols];
+    my @fixed_qts;
+
+    # This will be used as an array slice below to reorder
+    # the column values on the fly.
+    my @fixed_indices = @indexcols;
+
+    # Here we sort the rest of the columns. This enables us to
+    # generate a common QT Dimension for files such as GenePix which
+    # are not always ordered identically.
+    my %heading_to_index
+        = map { $original_headings[$_] => $_ } ( 0 .. $#original_headings );
+
+    # Check that the column headings are all unique (not true e.g. for FGEM).
+    if ( scalar(@original_headings) == scalar( keys %heading_to_index ) ) {
+
+        # If they are, sort using the lookup hash %heading_to_index
+        foreach my $heading_name ( sort keys %heading_to_index ) {
+            my $index = $heading_to_index{$heading_name};
+            if ( $is_heading_to_keep{$heading_name}
+                && ( none { $index == $_ } @indexcols ) ) {
+
+                push( @fixed_headings, $heading_name );
+                push( @fixed_qts,      $heading_name );
+                push( @fixed_indices,  $index );
+            }
+        }
+    }
+    else {
+
+        # Files with non-unique column headings can't be sorted.
+        foreach my $index ( 0 .. $#original_headings ) {
+            my $heading_name = $original_headings[$index];
+            if ( $is_heading_to_keep{$heading_name}
+                && ( none { $index == $_ } @indexcols ) ) {
+
+                push( @fixed_headings, $heading_name );
+                push( @fixed_qts,      $heading_name );
+                push( @fixed_indices,  $index );
+            }
+        }
+    }
+
+    # Actually open the file as a pipe via the external "sort" utility.
+    open( my $output_fh, q{|-}, qq{sort $sortkeys > "$output_file"} )
+        or croak("Error opening output file $output_file: $!\n");
+
+    # Read through the file, using @fixed_indices to decide what to
+    # keep.
+    DATA_LINE:
+    while ( my $line = <$input_fh> ) {
+
+        # Empty lines, especially at the end of files, can be a
+        # problem. We just dump them here and carry on.
+        if ( $line =~ $RE_EMPTY_STRING ) {
+            print {$self->get_error_fh()} ("Warning: skipping empty line in data file.\n");
+            next DATA_LINE;
+        }
+
+        # Strip all kinds of line ending
+        $line =~ s/$RE_LINE_BREAK//xms;
+
+        # We used to use Text::CSV_XS here, but this has to handle
+        # non-validated data files which occasionally crashed it.
+        my @line_array = split /\t/, $line, -1;
+
+        # Strip whitespace on the fly here, create the new line from
+        # the @fixed_indices array slice of the old one.
+        my @new_line = map {
+            my ( $value ) = defined $_ ? ( $_ =~ $RE_SURROUNDED_BY_WHITESPACE ) : q{};
+            defined $value && ( $value ne q{} )
+                ? $value
+                : 'null'
+        } @line_array[@fixed_indices];
+
+        print $output_fh ( join( "\t", @new_line ), $INPUT_RECORD_SEPARATOR );
+
+    }
+
+    close($output_fh)
+        or croak("Error: unable to close temporary filehandle: $!\n");
+
+    # Set the new information for this object
+    $self->set_index_columns( [ 0 .. $#indexcols ] );
+    $self->set_column_headings( \@fixed_headings );
+    $self->set_heading_qts( \@fixed_qts );
+    $self->set_path($output_file);
+
+    # Relink the filehandle.
+    my $self_fh = IO::File->new( $output_file, '<' )
+        or
+        croak("Error: Unable to reopen temporary file after sorting: $!\n");
+    $self->set_filehandle($self_fh);
+
+    return;
+}
+
+sub percent_null {
+
+    my ($self) = @_;
+
+    my $data_metrics = $self->get_data_metrics();
+    my $data_rowno   = $self->get_row_count();
+
+    # We want to count the intersection between the QTs (heading_qts)
+    # and the known QTs (data_metrics).
+    my $data_colno
+        = ( grep { exists $data_metrics->{$_} } @{ $self->get_heading_qts() } );
+
+    my $total_notnull = $self->get_not_null;
+
+    my $percent_null;
+    if ( $data_colno && $data_rowno && $total_notnull ) {
+        $percent_null
+            = 100 * ( ( $data_colno * $data_rowno ) - $total_notnull )
+            / ( $data_colno * $data_rowno );
+
+	# Round to 5dp.
+	return sprintf("%.5f", $percent_null);
+    }
+
+    else {
+        return ("N/A");
+    }
+}
+
+sub parse_header_with_indices {
+
+    my ( $self, $indices ) = @_;
+
+    ref $indices eq 'HASH' or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $input_fh = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my $type = $self->get_data_type();
+
+    my $linecount = 0;                     # Track the number of lines seen
+
+    my $column_headings = [];
+    my $indexcols       = [];
+    my $format;
+
+    HEADERLINE:
+    while ( my $line = <$input_fh> ) {
+
+        # Give up after we've seen a good chunk of the file (this is a
+        # performance measure).
+        last HEADERLINE if ( $linecount > 1000 );
+        $linecount++;
+
+        # Just to be doubly sure about DOS-style line endings
+        $line =~ s/$RE_LINE_BREAK//xms;
+
+        @$column_headings = split /\t/, $line, -1;
+
+        # Strip off surrounding quotes; I assume our loader can cope with
+        # QTs in quotes. Easy to change if not (but see parse_datafile for
+        # another instance of this!!!).
+        foreach my $heading (@$column_headings) {
+            $heading =~ s/$RE_SURROUNDED_BY_WHITESPACE/$1/xms;
+        }
+
+        # Here we do a check on column heading for our supported file
+        # formats.
+
+        # MX FGEM first. FIXME consider removing this special-case;
+        # its only real effect is to limit FGEM files to single
+        # indexcol formats; maybe we can remove this assumption
+        # elsewhere in the code and simplify this.
+        if ( $type && $type eq $CONFIG->get_FGEM_FILE_TYPE() ) {
+
+            # Check for Reporter Identifier column, CS id or Affy Probe ID
+            foreach my $col_format ( qw( FGEM FGEM_CS AffyNorm GEO Illumina ) ) {
+
+                # FGEM only ever has one identifier column.
+		my $heading = $CONFIG->get_T2M_INDICES()->{$col_format}[0];
+                my $column = get_indexcol(
+		    $column_headings,
+                    qr/$heading/,
+		);
+                unless ( $column == -1 ) {
+
+		    # Column found; we stop looking.
+                    $indexcols  = [ $column ];
+		    $format     = $col_format;
+		    last HEADERLINE;
+                }
+            }
+
+            # If no suitable identifier columns found, check the next line.
+            next HEADERLINE;
+
+        }
+
+        # Non-FGEM files (everything else - raw, normalized)
+        else {
+
+	    # N.B. for ordered searches here use a Tie::IxHash to
+	    # store the indices in Config.pm.
+
+            COLUMNFORMAT:
+            foreach my $col_format ( keys %$indices ) {
+
+                # Some index sets don't have 'Generic' indices; we skip them
+                # here to avoid autovivification bugs below
+                next COLUMNFORMAT unless ( $indices->{$col_format} );
+
+                foreach my $header ( @{ $indices->{$col_format} } ) {
+
+		    # Empty string is special-cased.
+                    my $column = get_indexcol(
+			$column_headings,
+			( $header eq q{} ? $header : qr/$header/ ),
+		    );
+
+                    if ( $column == -1 ) {
+
+			# Column not found; reset $indexcols and skip
+			# to the next format type.
+                        $indexcols = [];
+                        next COLUMNFORMAT;
+                    }
+		    else {
+
+			# Column found; add to $indexcols.
+			push( @{ $indexcols }, $column );
+		    }
+                }
+
+		# All columns found, we stop looking.
+		$format = $col_format;
+		last HEADERLINE;
+            }
+        }
+    }    # End of HEADERLINE
+
+    # Put the results where we can find them later.
+    if ( $format ) {
+	$self->set_column_headings( $column_headings );
+	$self->set_index_columns( $indexcols );
+	$self->set_heading_qts( strip_discards( $indexcols, $column_headings ) );
+    }
+    $self->set_format_type( $format || 'Unknown' );
+
+    return;
+}
+
+sub is_illumina_fgem {
+
+    # Takes a parsed Datafile, checks column_headings to see whether
+    # it's an FGEM as opposed to per-hyb data.
+
+    my $self = shift;
+
+    unless ( defined $is_illumina_fgem{ident $self} ) {
+	unless ( $self->get_format_type =~ /Illumina/i ) {
+	    croak("Can't call is_illumina_fgem() on non-Illumina file.\n");
+	}
+
+	# Start by assuming the negative.
+	$is_illumina_fgem{ident $self} = 0;
+
+	# Check the headings for repeated values.  Make sure we make a
+	# copy of the array here, as we're going to modify it.
+	my @headings = @{ $self->get_column_headings() };
+	my %seen;
+
+	HEADING:
+	foreach my $heading (@headings) {
+
+	    # Strip off everything before the last period, or return
+	    # the whole thing. Assumes that Illumina QTs never contain
+	    # periods.
+	    if ( $heading =~ m/\A .* \. ([^\.]+) \z/gxms ) {
+		$heading = $1;
+	    }
+	    if ( $heading && $seen{$heading}++ ){
+		$is_illumina_fgem{ident $self} = 1;
+		last HEADING;
+	    }
+	}
+    }
+
+    return $is_illumina_fgem{ident $self};
+}
+
+sub rewrite_illumina_as_fgem {
+
+    my $self = shift;
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    unless ( $self->get_format_type =~ /Illumina/i && $self->is_illumina_fgem ) {
+	croak("File is not an Illumina multi-hyb data matrix.\n");
+    }
+
+    # Rewrite the file here (reuse _fix_illumina() code)
+    my $input_fh = $self->_fix_illumina();
+
+    # Exchange the old filehandle for the new one.
+    $self->set_filehandle($input_fh);
+
+    # Rewrite the headings as standard FGEM-style. Assumes that
+    # Illumina QTs never contain periods.
+    my ( @new_headings, @file_qts, @hyb_ids );
+    foreach my $heading ( @{ $self->get_column_headings() } ) {
+	my ($hyb, $qt) = ($heading =~ /\A (.*) \. ([^\.]+) \z/xms);
+	if ( $qt && $hyb ) {
+	    push @new_headings, $qt;
+	    push @file_qts,     $qt;
+	    push @hyb_ids,    [ $hyb ];
+	}
+	else {
+
+	    # Index column.
+	    push @new_headings, $heading;
+	}
+    }
+    $self->set_format_type('FGEM');
+    $self->set_column_headings(\@new_headings);
+    $self->set_heading_qts(\@file_qts);
+    $self->set_heading_hybs(\@hyb_ids);
+
+    # If it's not already transformed data, set it as a measured data matrix.
+    if ( $self->get_data_type() eq 'raw' ) {
+	$self->set_data_type( $CONFIG->get_RAW_DM_FILE_TYPE() );
+	$self->set_sdrf_id_column('Hybridization Name');
+    }
+    else {
+	$self->set_data_type( $CONFIG->get_FGEM_FILE_TYPE() );
+	$self->set_sdrf_id_column('Normalization Name');
+    }
+
+    return;
+}
+
+sub get_dm_chip_type {
+
+    # Return the chip_type associated with a data matrix file via its
+    # MAGE BioAssayData association.
+    my ( $self ) = @_;
+
+    unless ( $datamatrix_chip_type{ ident $self } ) {
+	if ( my $badata = $self->get_mage_badata() ) {
+
+	    NVT:
+	    foreach my $nvt ( @{ $badata->getPropertySets() || [] } ) {
+		if ( $nvt->getName() =~ /\A CDF \z/ixms ) {
+		    my $chip_type = $nvt->getValue();
+		    $chip_type =~ s/\.CDF \z//ixms;
+		    if ( $chip_type ) {
+			$datamatrix_chip_type{ ident $self } = $chip_type;
+			last NVT;
+		    }
+		}
+	    }
+	}
+    }
+
+    return $datamatrix_chip_type{ ident $self };
+}
+
+sub get_array_design_id {
+
+    my ( $self ) = @_;
+
+    return $self->get_dm_chip_type() || $array_design_id{ ident $self };
+}
+
+sub parse_fgem_header : PRIVATE {
+
+    my ( $self ) = @_;
+
+    # Initial basic parse to get our header info.
+    $self->parse_header_with_indices( $CONFIG->get_T2M_INDICES() );
+
+    # Sort out column_headings, heading_qts and heading_hybs here.
+
+    # Allow checks to omit already-recognized index columns, such
+    # as the rather polymorphic ProbeSet ID for AffyNorm.
+    my %is_index_heading = map { $_ => 1 }
+	map { $self->get_column_headings()->[$_] }
+           @{ $self->get_index_columns() };
+
+    my ( @new_headings, @file_qts, @hyb_ids );
+
+    COLUMN_HEADING:
+    foreach my $heading ( @{ $self->get_column_headings() } ) {
+
+	# Skip index column headings.
+	if ( $is_index_heading{ $heading } ) {
+	    push @new_headings, $heading;
+	}
+	else {
+
+            # FIXME this regexp may need some work:
+            if ( my ( $qt_heading, $hyb_string )
+		     = ( $heading =~ m{\A \s* (.*?) \s* \( (.*) \) \s* \z}xms ) ) {
+
+                push( @new_headings, $qt_heading );
+                push( @file_qts,     $qt_heading );
+
+		# Hyb ids split into paren-encapsulated list,
+		# e.g. QT(Hyb1)(Hyb2)
+                my @ids = split /\)\s*\(/, $hyb_string;
+                push( @hyb_ids, \@ids );
+            }
+
+            else {
+
+		# Failure to parse FGEM heading. Make plenty of noise
+		# about this by dumping the full heading
+		# everywhere. This will be picked up elsewhere ard
+		# complained about.
+		push( @new_headings, $heading );
+		push( @file_qts,     $heading );
+		push( @hyb_ids,    [ $heading ] );
+            }
+        }
+    }
+
+    $self->set_column_headings( \@new_headings );
+    $self->set_heading_hybs( \@hyb_ids );
+    $self->set_heading_qts( \@file_qts );
+
+    return;
+}
+
+sub parse_data_matrix_header : PRIVATE {
+
+    my ( $self ) = @_;
+
+    unless ( ( $self->get_data_type() eq $CONFIG->get_RAW_DM_FILE_TYPE()
+            || $self->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE() )
+		&& $self->get_mage_badata() ) {
+	croak("Error: Unsuitable Datafile object passed to rewrite method.");
+    }
+
+    # Make sure we know what our line format is.
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $input_fh = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    # Parse the two-line header.  FIXME this assumes the rather rigid
+    # two-line header is the first thing in the file. We should at
+    # least allow comment lines (#...) here.
+    my $header1 = <$input_fh>;
+    chomp $header1;
+    $header1 =~ s/\"//g;
+    my $header2 = <$input_fh>;
+    chomp $header2;
+    $header2 =~ s/\"//g;
+    my @harry1 = split /\t/, $header1;
+    my @harry2 = split /\t/, $header2;
+
+    # $harry1[0] should be an SDRF column reference, while $harry2[0]
+    # has to be Reporter Identifier or CompositeSequence Identifier
+    # (CompositeElement) at the moment.
+
+    # Note the SDRF columns to use for later bioassay mapping.
+    my $sdrf_colref = $harry1[0];
+    $self->set_sdrf_id_column( $sdrf_colref );
+    my @hyb_ids;
+    for ( my $i = 1; $i <= $#harry1; $i++ ) {
+	my @hybs = split /;/, $harry1[$i];
+	push @hyb_ids, \@hybs;
+    }
+    $self->set_heading_hybs( \@hyb_ids );
+
+    # Coerce the DesignElement identifier column into something we can
+    # use for MAGEv1.
+    my $de_type = $harry2[0];
+    my $format;
+    if ( $de_type =~ /Composite *Element(?: *REF)?/i ) {
+	$de_type = 'CompositeSequence Identifier';
+	$format  = 'FGEM_CS';
+    }
+    elsif ( $de_type =~ /Reporter(?: *REF)?/i ) {
+	$de_type = 'Reporter Identifier';
+	$format  = 'FGEM';
+    }
+
+    if ( $format ) {
+	$self->set_index_columns( [ 0 ] );
+	$self->set_column_headings( [ $de_type, @harry2[1..$#harry2] ] );
+	$self->set_heading_qts( [ @harry2[1..$#harry2] ] );
+    }
+    $self->set_format_type( $format || 'Unknown' );
+
+    return;
+}
+
+####################
+# Accessor methods #
+####################
+
+#####################
+# Generic accessors #
+#####################
+
+{
+    ## no critic ProhibitNoStrict
+    no strict qw(refs);
+    ## use critic ProhibitNoStrict
+
+    # Create the integer incrementors here
+    while (my ($method, $hash) = each %incr_integer_attr) {
+	*{"increment_$method"} = sub {
+            my ( $self, $value ) = @_;
+            $value = 1 unless defined($value);
+            looks_like_number($value)
+                or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+            $hash->{ident $self} += $value;
+            return $hash->{ident $self};
+	};
+    }
+
+    # Create the accumulative arrayref accessors here
+    while (my ($method, $hash) = each %accum_hashref_attr) {
+	*{"get_$method"} = sub {    # Returns arrayref
+            my $self = shift;
+            return [ sort keys %{ $hash->{ident $self} } ];
+	};
+        *{"add_$method"} = sub {    # Push on assignment (i.e. add to
+                                    # old).
+            my $self = shift;
+
+	    confess( $CONFIG->get_ERROR_MESSAGE_ARGS() ) unless scalar(@_);
+
+	    foreach (@_) {
+		$hash->{ident $self}{$_}++;
+            }
+            return [ sort keys %{ $hash->{ident $self} } ];
+        };
+    }
+}
+
+################################
+# Specialized accessor methods #
+################################
+
+sub get_filehandle {
+
+    my $self = shift;
+
+    unless ( $filehandle{ident $self} ) {
+	croak("Error: file path not set") unless $self->get_path();
+	$filehandle{ident $self} = IO::File->new($self->get_path(), '<');
+    }
+
+    return $filehandle{ident $self};
+}
+
+sub get_is_binary {
+
+    my $self = shift;
+
+    if (! defined($self->get_path())) {
+	croak("Error: Datafile path not set.\n");
+    }
+    elsif (! -r $self->get_path() ) {
+	croak("Error: cannot read file " . $self->get_path . "\n");
+    }
+    $is_binary{ident $self} = -B $self->get_path() ? 1 : 0;
+
+    return $is_binary{ident $self};
+}
+
+sub get_md5_digest {
+
+    my $self = shift;
+
+    unless ( $md5_digest{ident $self} ) {
+	$self->set_md5_digest( $self->calculate_md5_digest() );
+    }
+
+    return $md5_digest{ident $self};
+}
+
+sub calculate_md5_digest : PRIVATE {
+
+    my ( $self ) = @_;
+
+    my $fh = $self->get_filehandle();
+
+    my $pos = tell( $fh );
+
+    # NOTE that we only ever use seek/read on binary files (don't mix
+    # these with sysseek/sysread) because other operations use seek
+    # elsewhere. Note that the Affymetrix file parsers use sys*
+    # functions exclusively though.
+    seek( $fh, 0, 0 )
+	or croak("Error: Unable to rewind binary file for hashing.");
+
+    my $md5 = Digest::MD5->new();
+    my $chunk;
+    my $chunksize = 65536;    # 64k for reasonable efficiency (untested though).
+    while ( my $bytes = read( $fh, $chunk, $chunksize ) ) {
+	$md5->add( $chunk );
+    }
+
+    seek( $fh, $pos, 0 )
+	or croak("Error: Unable to reset binary filehandle after hashing.");
+
+    return $md5->hexdigest();
+}
+
+sub add_factor_value {
+
+    # Takes (category, value); returns hash ref; category => [value1, value2]
+    my ( $self, $category, $value ) = @_;
+
+    # Check the already-assigned FVs for this category, add if not
+    # present.
+    $factor_value{ident $self}{$category} ||= [];
+    my $found = first { $value eq $_ }
+	@{ $factor_value{ident $self}{$category} };
+    push( @{ $factor_value{ident $self}{$category} }, $value )
+	unless $found;
+
+    return $factor_value{ident $self};
+}
+
+sub set_ded_type {    # string
+
+    my ( $self, $type ) = @_;
+
+    confess( $CONFIG->get_ERROR_MESSAGE_ARGS() ) unless defined($type);
+
+    unless ( any { $type eq $_ }
+		 qw(Feature Reporter CompositeSequence) ) {
+	confess(qq{Unrecognized DesignElement class "$type".});
+    }
+    $ded_type{ident $self} = $type;
+
+    return $ded_type{ident $self};
+}
+
+sub set_format_type {    # string (enum: various as set in Config.pm,
+                         # "Affymetrix" and "Unknown")
+
+    my ( $self, $type ) = @_;
+
+    confess( $CONFIG->get_ERROR_MESSAGE_ARGS() ) unless defined($type);
+
+    my @allowed_types = qw(Unknown Affymetrix);
+    foreach my $constant (
+	$CONFIG->get_T2M_INDICES()
+    ) {
+	push( @allowed_types, keys %$constant );
+    }
+    unless ( any { $type eq $_ } @allowed_types ) {
+	confess("Unrecognized format type $type");
+    }
+    $format_type{ident $self} = $type;
+
+    return $format_type{ident $self};
+}
+
+sub set_data_type {    # string (enum)
+
+    my ( $self, $type ) = @_;
+
+    confess( $CONFIG->get_ERROR_MESSAGE_ARGS() ) unless defined($type);
+
+    my @allowed_types = (
+	@{ $CONFIG->get_T2M_FILE_TYPES() },
+	$CONFIG->get_FGEM_FILE_TYPE(),
+	$CONFIG->get_RAW_DM_FILE_TYPE(),
+	qw(EXP),
+    );
+    unless ( any { $type eq $_ } @allowed_types ) {
+	confess("Unrecognized data type $type");
+    }
+    $data_type{ident $self} = $type;
+
+    return $data_type{ident $self};
+}
+
+sub set_name {    # string; filename only. Sets path if not otherwise set
+
+    my ( $self, $name ) = @_;
+
+    confess( $CONFIG->get_ERROR_MESSAGE_ARGS() ) unless defined($name);
+
+    ref $name and confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    $name{ident $self} = $name;
+    $self->set_path( File::Spec->rel2abs( $name ) )
+	unless $self->get_path();
+
+    return $name{ident $self};
+}
+
+sub set_exp_data {    # complex hash ref
+    my ( $self, $data ) = @_;
+
+    confess( $CONFIG->get_ERROR_MESSAGE_ARGS() ) unless defined($data);
+
+    ref $data eq 'HASH'
+	or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    $exp_data{ident $self} = $data;
+
+    $self->set_is_exp(1);
+    $self->set_format_type('Affymetrix');
+    $self->set_data_type('EXP');
+
+    return $exp_data{ident $self};
+}
+
+sub set_mage_qtd {
+
+    my ( $self, $qtd ) = @_;
+
+    confess( $CONFIG->get_ERROR_MESSAGE_ARGS() ) unless defined($qtd);
+
+    $qtd->isa('Bio::MAGE::BioAssayData::QuantitationTypeDimension')
+	or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    $mage_qtd{ident $self} = $qtd;
+
+    return $mage_qtd{ident $self};
+}
+
+###################
+# Private methods #
+###################
+
+sub _get_blocks : PRIVATE {
+
+    my ( $self ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $input_fh = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my $blocks = [];
+
+    my $saved_position = tell($input_fh);
+
+    # Text::CSV_XS not appropriate here - bad data can crash it too easily.
+    while ( my $line = <$input_fh> ) {
+
+        $line =~ s/$RE_LINE_BREAK//xms;
+        my @line_array = split /\t/, $line, -1;
+
+        my $row_coords = $self->_get_row_coords( \@line_array );
+        $blocks
+            = _update_blocks( $blocks, \@line_array, $self->get_index_columns() );
+
+    }
+
+    seek( $input_fh, $saved_position, 0 );
+
+    return $blocks;
+
+}
+
+sub _update_blocks : PRIVATE {    # Updated for Scanalyze
+
+    my ( $blocks, $line_array, $indexcols ) = @_;
+
+    ref $blocks eq 'ARRAY'
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    ref $line_array eq 'ARRAY'
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    ref $indexcols eq 'ARRAY'
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my $block_num = $line_array->[ $indexcols->[0] ];
+    my $x         = $line_array->[ $indexcols->[3] ];
+    my $y         = $line_array->[ $indexcols->[4] ];
+    $blocks->[$block_num]{max_x} ||= $x;
+    $blocks->[$block_num]{max_x} = $x
+        if ( $blocks->[$block_num]{max_x} < $x );
+
+    $blocks->[$block_num]{min_x} ||= $x;
+    $blocks->[$block_num]{min_x} = $x
+        if ( $blocks->[$block_num]{min_x} > $x );
+
+    $blocks->[$block_num]{max_y} ||= $y;
+    $blocks->[$block_num]{max_y} = $y
+        if ( $blocks->[$block_num]{max_y} < $y );
+
+    $blocks->[$block_num]{min_y} ||= $y;
+    $blocks->[$block_num]{min_y} = $y
+        if ( $blocks->[$block_num]{min_y} > $y );
+
+    return $blocks;
+
+}
+
+sub _get_row_coords : PRIVATE {
+
+    my ( $self, $line_array ) = @_;
+
+    ref $line_array eq 'ARRAY'
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my $row_coords = [];
+    my $format_type = $self->get_format_type();
+
+    # Affy has yet to be sorted out FIXME - will probably use
+    # composite/reporter IDs, not feature coordinates
+    if ( $format_type eq 'Affymetrix' ) {
+	return $row_coords;
+    }
+
+    # Strip out whitespace, initiate undef indices
+    foreach my $index ( @{ $self->get_index_columns() } ) {
+
+        # Prevent warnings about uninitiated indices
+        $line_array->[$index] = q{} unless defined( $line_array->[$index] );
+        $line_array->[$index] =~ s/$RE_SURROUNDED_BY_WHITESPACE/$1/xms
+	    unless $self->get_is_miamexpress();
+    }
+
+    # Simple for Generic MetaColumn/MetaRow/Column/Row format files
+    if ( first { $format_type eq $_ } qw(Generic
+					 FGEM
+					 FGEM_CS
+					 GEO
+					 AffyNorm) ) {
+
+        foreach my $index ( @{ $self->get_index_columns() } ) {
+            push @$row_coords, $line_array->[$index];
+        }
+
+        return $row_coords;    # MC/MR/C/R or FGEM
+    }
+
+    # Make preparations for converting GenePix/Scanalyze coordinates to
+    # Generic ones.  @blocks is an array of hashes, $blocks[$block_num]
+    # = {max_x => "highest x coordinate in the block"} This is processed
+    # later in the subroutine
+    if ( first { $format_type eq $_ } qw(GenePix Scanalyze ImaGene7) ) {
+
+        foreach my $index ( @{ $self->get_index_columns() }[ 0 .. 2 ] ) {
+            push @$row_coords, $line_array->[$index];
+        }
+
+        return $row_coords;
+    }    # B/C/R; will convert below
+
+    # This should never happen:
+    croak(
+	sprintf(
+	    "Error: Unknown file type %s Internal script error in sub _get_row_coords.\n",
+	    $format_type,
+	)
+    );
+}
+
+sub _parse_datarows : PRIVATE {    # Generic, Affymetrix and FGEM/FGEM_CS only
+
+    my ( $self ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $input_fh = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my $rc = q{};
+
+    # We do the data metrics thing on a hashref rather than using
+    # expensive method calls.
+    my $metrics     = $self->get_data_metrics();
+    my $format_type = $self->get_format_type();
+    my $headings    = $self->get_column_headings;
+
+    # Initialise some values in our data summary hashref, $metrics
+    foreach my $heading ( keys %{$metrics} ) {
+        $metrics->{$heading}{max}     = -4294967295;    # 2^32 - 1
+        $metrics->{$heading}{min}     = 4294967295;
+        $metrics->{$heading}{benford} = 0;
+    }
+
+    # Now we process the remainder of the data lines. This while loop is
+    # where the script spends the vast majority of its time so we want
+    # to be quite careful here.
+
+    # We may want to skip columns, e.g. if there's no heading at all:
+    my %bad_column;
+    my @feature_coords;
+
+    # The line numbers to use for file-vs-file comparisons.
+    my %selected_line_no = map {$_ => 1} (3, 4, 6, 7, 9, 31, 33, 35, 37, 39);
+
+    # For Pearson and the like
+    my %intensity_vector;
+    my $notnull = 0;
+
+    DATA_LINE:
+    while ( my $line = <$input_fh> ) {
+
+        # just to be doubly sure about DOS-style line endings
+        $line =~ s/$RE_LINE_BREAK//xms;
+
+        # Prevent run-on into the mask/outlier sections of Affy CEL files
+        last DATA_LINE
+            if ( $format_type eq 'Affymetrix'
+            && ( $line =~ $RE_EMPTY_STRING ) );
+
+        # Increment the counter, take a line where selected
+	my $current_row = $self->increment_row_count();
+        if ( $selected_line_no{$current_row} ) {
+	    my $test_vector = $self->get_test_data_line()
+		            . md5_hex($line);
+	    $self->set_test_data_line($test_vector)
+        }
+
+        # Check for empty lines (typically the last one). These are
+        # typically not major problems, so we just print to error_fh.
+        if ( $line =~ $RE_EMPTY_STRING ) {
+            printf {$self->get_error_fh()} (
+		"Warning: Blank line at data row %s in file %s\n",
+		$self->get_row_count(),
+                $self->get_path(),
+	    );
+            next DATA_LINE;
+        }
+
+        # We skip column-wise checks (percent null, Benford's law,
+        # Pearson's) checks for Affy CELv3. This gives an approx. 4x
+        # increase in speed.
+        next DATA_LINE if ( $format_type eq 'Affymetrix' );
+
+        # Negative LIMIT preserves trailing empty values
+        my @line_array = split /\t/, $line, -1;
+
+        # Get the row coordinates. We will fix these below; we can't do it
+        # on the fly because e.g. for GenePix we need some global data
+        # before we can tell where the blocks fit into the
+        # MetaColumn-MetaRow format.
+        my $row_coords = $self->_get_row_coords( \@line_array );
+
+        # Transform arrayref into .-delimited string (this is slightly
+        # better memory-wise than an AoA).
+        if (@$row_coords) {
+            my $coord_string = join( '.', @$row_coords );
+            push( @feature_coords, $coord_string );
+        }
+
+        # Now we take a look at the data
+
+        my %measured_data;    # channel => intensity value
+        my $colno = -1;       # kludgey but better than the alternative
+
+        COLUMN_VALUE:
+        foreach my $value (@line_array) {
+
+            # Check we haven't already marked this column as bad.
+            $colno++;    # first column is zero in the @column_headings array
+            next COLUMN_VALUE if ( $bad_column{$colno} );
+
+            my $heading = $headings->[$colno];
+
+            # Quick check that we're not on the brink of disaster (data in
+            # columns with no headings)
+            if ( !defined($heading) && defined($value) ) {
+                $rc .= "ERROR: data in column "
+                    . ( $colno + 1 )
+                    . " has no column heading!\n";
+                $bad_column{$colno}++;
+                next COLUMN_VALUE;
+            }
+
+            # Skip if the column has no attributes.
+            next COLUMN_VALUE unless $metrics->{$heading};
+
+            # We also skip if the column contains non-numeric values (and
+            # flag up an error!)  This tests for the general form of a
+            # number.
+
+            # It's okay if this is meant to be a string, set error flag if not
+            if ( $metrics->{$heading}{datatype}
+                && ( $metrics->{$heading}{datatype} eq 'string_datatype' ) ) {
+                $notnull++;
+                next COLUMN_VALUE;    # no further analysis on string values.
+            }
+
+            # Screen out unexpected null and string values here.
+            unless ( looks_like_number($value) ) {
+                my $error_message;
+                if ( $value eq q{} ) {
+                    $error_message = 'Null in numeric data field';
+                }
+                else {
+                    $error_message = 'Text in numeric data field';
+                }
+                $metrics->{$heading}{errors}{$error_message}++;
+                next COLUMN_VALUE;
+            }
+
+            # Update maximum and minimum values on a per-column basis
+            if ( $value < $metrics->{$heading}{min} ) {
+                $metrics->{$heading}{min} = $value;
+            }
+            elsif ( $value > $metrics->{$heading}{max} ) {
+                $metrics->{$heading}{max} = $value;
+            }
+
+            # Check that boolean values are 0 or 1
+            if (   $metrics->{$heading}{datatype}
+                && ( $metrics->{$heading}{datatype} eq 'boolean' )
+                && ( $value != 1 )
+                && ( $value != 0 ) ) {
+
+                my $error_message = 'Boolean value not 0 or 1';
+                $metrics->{$heading}{errors}{$error_message}++;
+
+            }
+
+            # These next few things need an actual value to work on
+            # (i.e. not zero at this point)
+            unless ( $value == 0 ) {
+
+                $notnull++;    # We count zeroes as nulls, at least for now
+
+                # Check for floats vs. integers.
+                if (
+
+                    # not integer or boolean; presumably a float
+                    ( int($value) != $value )
+
+                    # in a column where it's not supposed to be
+                    && ( $metrics->{$heading}{datatype} ne 'float' )
+                    ) {
+                    my $error_message = 'Floats in non-float data field';
+                    $metrics->{$heading}{errors}{$error_message}++;
+                }
+
+                # Do the Benford's Law thing.
+                # N.B. we only do this for floats that are declared as such
+                # New and Improved - uses vec() for greater efficiency.
+
+                # We just do this on MeasuredSignal and DerivedSignal.
+                my $subclass = $metrics->{$heading}{subclass};
+                if ( $subclass && $subclass =~ m/Signal/oxms ) {
+
+                    # Strip out leading whitespace, zeros, minuses and
+                    # decimal points.
+                    $value =~ s/\A \s* [0.-]*//oxms;
+                    my $first_char = substr( $value, 0, 1 );
+                    vec( $metrics->{$heading}{benford}, $first_char, 32 )++;
+
+                    # get intensities by channel
+                    # MeasuredSignal (raw data) only, for now
+                    if ( $subclass eq 'MeasuredSignal' ) {
+                        $measured_data{ $metrics->{$heading}{channel} } +=
+                            $metrics->{$heading}{is_background}
+                            ? -$value
+                            : $value;
+                    }
+                }
+            }
+        }
+
+      # FIXME intensity vector capture currently inactivated - we're not
+      # using it at all yet, and it adds to the memory overhead.
+      #
+      #    # Vector for Pearson correlation coefficient calculations
+      #    # Ignore this if no suitable data found
+      #    if (my $num_channels = scalar (grep defined, keys %measured_data)){
+
+        #      my $measured_signal;
+        #      if ($num_channels == 2){ # two-channel is a special case
+        #	my ($numerator, $denominator) = sort keys %measured_data;
+
+#	# Avoid illegal division by zero; the Pearson routines later will ignore non-numbers
+#	$measured_signal = $measured_data{$denominator} ?
+#	  ($measured_data{$numerator} / $measured_data{$denominator}) : 'NaN';
+#      }
+#      else { # one channel or >2 channel data is simply averaged
+#	$measured_signal = sum(values %measured_data) / $num_channels;
+#      }
+#      my $row_id = join('.',@$row_coords);
+#      $intensity_vector{$row_id} = $measured_signal;
+#    }
+    }
+
+    # FIXME - see above for intensity vec comments
+    #  $self->set_intensity_vector(\%intensity_vector);
+
+    $self->increment_not_null($notnull);
+
+    $self->set_data_metrics($metrics);
+
+    return ( \@feature_coords, $rc );
+
+}
+
+sub _derive_consensus_software : PRIVATE {
+
+    my ( $self, $QTs ) = @_;
+
+    ref $QTs eq 'HASH' or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my ( @file_qts );
+    my $rc = q{};
+
+    # Allow checks to omit already-recognized index columns, such as
+    # the rather polymorphic ProbeSet ID for AffyNorm.
+    my %is_index_heading = map { $_ => 1 }
+	map { $self->get_column_headings()->[$_] }
+           @{ $self->get_index_columns() };
+
+    COLUMN_HEADING:
+    foreach my $heading ( @{ $self->get_column_headings() } ) {
+
+	# Skip index column headings.
+	push( @file_qts, $heading ) unless ( $is_index_heading{ $heading } );
+    }
+
+    # Initialize our counters
+    my $consensus = 'Unknown';
+    my %count;
+    foreach my $software ( keys %{$QTs}, $consensus ) {
+        $count{$software} = 0;
+    }
+
+    # Do the count
+    SOFTWARE_TYPE:
+    foreach my $software ( keys %{$QTs} ) {
+
+        foreach my $qt (@file_qts) {
+            $count{$software}++ if ( $QTs->{$software}{$qt} );
+            $consensus = $software
+                if ( $count{$software} > $count{$consensus} );
+        }
+    }
+
+    # Check for ambiguity
+    SOFTWARE_TYPE:
+    foreach my $software ( keys %{$QTs} ) {
+
+        # same-scoring softwares will be summarized as 'Ambiguous'; this
+        # is thought to be better than randomly picking one of the
+        # alternatives.
+        if (   ( $count{$software} == $count{$consensus} )
+            && ( $software ne $consensus )
+            && $count{$consensus} ) {
+            $consensus = 'Ambiguous';
+            print STDERR ( "WARNING: Ambiguous software type for file "
+                    . $self->get_name()
+                    . "\n" );
+            $rc .= "Ambiguous QuantitationTypes;"
+                . " unable to determine software type.\n";
+            last SOFTWARE_TYPE;
+        }
+    }
+
+    # Note down the kind of QTs we have
+    $self->set_qt_type($consensus);
+
+    # We want all the known QTs in data_metrics, not just the headings,
+    # because some manufacturers have ConfidenceIndicators mapped to QTs
+    # which don't appear in the data file.
+    # Make sure we're making a deep copy of the relevant QT_hash
+    # portion; otherwise this comes back to bite us later!
+    my $known_qts =
+        ( $QTs->{$consensus} && ( ref( $QTs->{$consensus} ) eq 'HASH' ) )
+        ? dclone $QTs->{$consensus}
+        : {};
+    $self->set_data_metrics($known_qts);
+
+    return $rc;
+}
+
+sub _average_block_dimension : PRIVATE {
+
+    my ($blocks, $maxdim, $mindim) = @_;
+
+    ref $blocks eq 'ARRAY'
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    defined ($maxdim) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    defined ($mindim) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my $count = 0;
+    my $total = 0;
+    for ( my $block_num = 1; $block_num <= $#{$blocks}; $block_num++ ) {
+        my $delta_y = $blocks->[$block_num]{$maxdim}
+            - $blocks->[$block_num]{$mindim};
+
+        if ( $delta_y > 10 ) {    # 10 here is arbitrary; sometimes blocks on
+                                  # the same row have slightly different
+                                  # Y-coords, and here we attempt not to count
+                                  # them. The real results should hopefully
+                                  # drown out the errors.
+            $total += $delta_y;
+            $count++;
+        }
+    }
+
+    return ( $count ? ( $total / $count ) : 0 );    # avoid divide-by-zero
+
+}
+
+sub _average_block_height : PRIVATE {
+
+    my ($blocks) = @_;
+
+    ref $blocks eq 'ARRAY'
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    return _average_block_dimension($blocks, 'max_y', 'min_y');
+
+}
+
+sub _average_block_width : PRIVATE {
+
+    my ($blocks) = @_;
+
+    ref $blocks eq 'ARRAY'
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    return _average_block_dimension($blocks, 'max_x', 'min_x');
+
+}
+
+sub _slice_minus_discards : PRIVATE {
+
+    my ($self) = @_;
+
+    my %discards = map { $_ => 1 } @{ $self->get_index_columns() };
+    my @slice;
+    my $lastheading = $#{ $self->get_column_headings() };
+    foreach ( 0 .. $lastheading ) {
+        push( @slice, $_ ) unless $discards{$_};
+    }
+
+    return ( \@slice, $lastheading );
+
+}
+
+sub _fix_scanalyze : PRIVATE {
+
+    my ( $self ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $input_fh = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    # Store the start of the data, for use in cases where the SPOT
+    # line is not found.
+    my $init_pos = tell($input_fh);
+
+    seek($input_fh, 0, 0) or croak("Error seeking in filehandle: $!");
+
+    my $fixed_fh = IO::File->new_tmpfile;
+
+    # Scan through the REMARKs and empty lines, and parse out the
+    # mapping from channel to fluor, if we can. The first SPOT line is
+    # the start of the data; however if it's not present we want to be
+    # able to parse these data anyway.
+    my $pos  = tell($input_fh);
+    my $line = <$input_fh>;
+
+    my $info = {};
+
+    LINE:
+    until ( $line =~ m/\A SPOT \b/xms ) {
+        $pos  = tell($input_fh);    # Store the last-but-one line position
+        $line = <$input_fh>;
+
+        # End of file (prevent infinite loops in files without SPOT).
+        unless ( defined $line ) {
+	    $pos = $init_pos;
+	    last LINE;
+	}
+
+        # Only Cy3/Cy5 and Alexa555/647 supported for now FIXME.
+	#
+	# We default to Cy3 or Alexa555 in CH1; this is to match the usage seen in
+	# Stanford submissions.
+	my ($channel, $fluor);
+        if ( ( $channel, $fluor )
+            = ( $line =~ m{ \b(\w+)\b \s+ IMAGE \s+ .* Cy.*([35]) }ixms ) ) {
+
+            $info->{ lc($channel) } = ( $fluor == 3 ) ? "CH1" : "CH2";
+        }
+        elsif ( ( $channel, $fluor )
+            = ( $line =~ m{ \b(\w+)\b \s+ IMAGE \s+ .* Alexa(555|647) }ixms ) ) {
+
+            $info->{ lc($channel) } = ( $fluor == 555 ) ? "CH1" : "CH2";
+        }
+    }
+
+    $self->_map_channel_to_fluor($info);
+
+    seek( $input_fh, $pos, 0 )
+        or croak("Error seeking in data file: $!\n");    # Back up one line
+
+    my $blocks = $self->_get_blocks();
+
+    # Block height is used below as a margin of error to cope with the
+    # real-world fact that spotters don't run in perfect grids.
+    # Likewise this approach is also imperfect.
+    my $block_height = _average_block_height($blocks);
+
+    # Construct a block -> MC/MR lookup table (this takes into account
+    # the Y coord as well as X). Numbering is from the top left corner
+    # of the grid.
+    my $last_max_x             = 0;
+    my $last_max_y             = 0;
+    my $metacolumn             = 0;
+    my $metarow                = 1;
+    my $populated_x_grid       = 0;
+    my $populated_y_grid       = 0;
+    my $populated_x_coordinate = 0;
+
+    foreach my $block ( @{$blocks}[ 1 .. $#$blocks ] ) {   # blocks start at 1
+
+        if ( $block->{max_x} < $last_max_x ) {
+            if ( $block->{max_y} < ( $last_max_y - ( $block_height / 2 ) ) ) {
+                print {$self->get_error_fh()}
+		    ("Error: Overlapping blocks in ScanAlyze array.\n");
+            }
+
+            else {    # X is less than in previous block, but Y is not.
+
+                # The following checks whether the current block x coord is
+                # less than the previous metablock max x coord.
+                if ( $block->{max_x}
+                    < ( $populated_x_coordinate - ( $block_height / 2 ) ) ) {
+
+                    # If so, reset back to zero (full carriage return).
+                    $populated_x_grid       = 0;
+                    $populated_x_coordinate = 0;
+                    $populated_y_grid       = $metarow;
+                }
+
+                # Set the metacolumn back to the end of the last completed
+                # metablock, move down a row.
+                $metacolumn = $populated_x_grid + 1;
+                $metarow++;
+
+            }
+        }
+        else {
+
+            # Move across to the next metacolumn as normal; skip if we're
+            # moving across to the next block.
+            $metacolumn++
+                unless (
+                $block->{max_y} < ( $last_max_y - ( $block_height / 2 ) ) );
+        }
+
+        if ( $block->{max_y} < ( $last_max_y - ( $block_height / 2 ) ) ) {
+            if ( $block->{max_x} < $last_max_x ) {
+                print {$self->get_error_fh()}
+		    ("Error: Overlapping blocks in ScanAlyze array.\n");
+            }
+
+            else {    # Y is less than in previous block, but X is not.
+
+                # Update the completed metablock column info
+                $populated_x_grid       = $metacolumn;
+                $populated_x_coordinate = $block->{max_x};
+
+                # Back up the metarow to the last completed metablock row,
+                # move over to the appropriate metacolumn
+                $metarow = $populated_y_grid + 1;
+                $metacolumn++;
+            }
+        }
+        else {
+
+            # metarow stays the same (simple move of one block to the
+            # right). This space left blank intentionally.
+        }
+
+        $block->{metarow}    = $metarow;
+        $block->{metacolumn} = $metacolumn;
+
+        $last_max_x = $block->{max_x};
+        $last_max_y = $block->{max_y};
+
+    }
+
+    # Convert the GRID/COL/ROW coords into MC/MR/R/C
+
+    # Set up an array slice base to use for the stripped array (faster
+    # than strip_discards()).
+    my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+    while ( my $line = <$input_fh> ) {
+
+        $line =~ s/$RE_LINE_BREAK//xms;
+        my @line_array = split /\t/, $line;
+
+        my $block_num = $line_array[ $self->get_index_columns()->[0] ];
+        my $column    = $line_array[ $self->get_index_columns()->[1] ];
+        my $row       = $line_array[ $self->get_index_columns()->[2] ];
+
+        my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+        my $new_line = [ @line_array[@rowslice] ];
+
+        my $metacolumn = $blocks->[$block_num]{metacolumn};
+        my $metarow    = $blocks->[$block_num]{metarow};
+
+        unshift( @$new_line, $metacolumn, $metarow, $column, $row );
+
+        {
+            no warnings qw(uninitialized);
+            print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+        }
+
+    }
+
+    seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+    $self->_genericize_fileinfo();
+
+    return $fixed_fh;
+
+}
+
+sub _fix_genepix : PRIVATE {
+
+    my ( $self ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $input_fh = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my $fixed_fh = IO::File->new_tmpfile;
+
+    my $blocks = $self->_get_blocks();
+
+    # Block width is used below as a margin of error to cope with
+    # array designs with only one MetaColumn, and the real-world fact
+    # that spotters don't run in perfect grids.  Likewise this
+    # approach is also imperfect.
+    my $half_block_width = _average_block_width($blocks) / 2;
+
+    # Construct a block -> MC/MR lookup table
+    my $last_max_x = 0;
+    my $metacolumn = 0;
+    my $metarow    = 1;
+    foreach my $block ( @{$blocks}[ 1 .. $#$blocks ] ) {   # Blocks start at 1
+
+        if ( $block->{max_x} < ($last_max_x + $half_block_width) ) {
+
+	    # New MetaRow, "carriage return" for MetaColumn.
+	    $metarow++;
+	    $metacolumn = 0;
+	}
+        $metacolumn++;
+
+        $block->{metarow}    = $metarow;
+        $block->{metacolumn} = $metacolumn;
+
+        $last_max_x = $block->{max_x}
+
+    }
+
+    # Convert the BCR coords into MC/MR/R/C
+
+    # Set up an array slice base to use for the stripped array (faster
+    # than strip_discards()).
+    my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+    while ( my $line = <$input_fh> ) {
+
+        $line =~ s/$RE_LINE_BREAK//xms;
+        my @line_array = split /\t/, $line;
+
+        my $block_num = $line_array[ $self->get_index_columns()->[0] ];
+        my $column    = $line_array[ $self->get_index_columns()->[1] ];
+        my $row       = $line_array[ $self->get_index_columns()->[2] ];
+
+        my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+        my $new_line = [ @line_array[@rowslice] ];
+
+        my $metacolumn = $blocks->[$block_num]{metacolumn};
+        my $metarow    = $blocks->[$block_num]{metarow};
+
+        unshift( @$new_line, $metacolumn, $metarow, $column, $row );
+
+        {
+            no warnings qw(uninitialized);
+            print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+        }
+    }
+
+    seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+    $self->_genericize_fileinfo();
+
+    return $fixed_fh;
+
+}
+
+sub _fix_arrayvision : PRIVATE {
+
+    my ( $self ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $input_fh = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my $fixed_fh = IO::File->new_tmpfile;
+
+    # Set up an array slice base to use for the stripped array (faster
+    # than strip_discards()).
+    my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+    while ( my $line = <$input_fh> ) {
+
+        $line =~ s/$RE_LINE_BREAK//xms;
+        my @line_array = split /\t/, $line;
+
+        my $primary   = $line_array[ $self->get_index_columns()->[0] ];
+        my $secondary = $line_array[ $self->get_index_columns()->[1] ];
+
+        # This format supplied by Elizabeth Herbolsheimer (WMIT)
+        my ( $metarow, $metacolumn ) = ( $primary   =~ m/(\d+) - (\d+)/ );
+        my ( $row,     $column )     = ( $secondary =~ m/(\d+) - (\d+)/ );
+
+        my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+        my $new_line = [ @line_array[@rowslice] ];
+
+        unshift( @$new_line, $metacolumn, $metarow, $column, $row );
+
+        {
+            no warnings qw(uninitialized);
+            print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+        }
+
+    }
+
+    seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+    $self->_genericize_fileinfo();
+
+    return $fixed_fh;
+}
+
+sub _fix_arrayvision_lg2 : PRIVATE {
+
+    my ( $self ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $input_fh = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    my $fixed_fh = IO::File->new_tmpfile;
+
+    # Set up an array slice base to use for the stripped array (faster
+    # than strip_discards()).
+    my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+    # Flag to indicate whether we've found features or not.
+    my $is_feature_level;
+
+    # Process the file.
+    while ( my $line = <$input_fh> ) {
+
+        $line =~ s/$RE_LINE_BREAK//xms;
+        my @line_array = split /\t/, $line;
+
+        my $spotlabels = $line_array[ $self->get_index_columns()->[0] ];
+
+	my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+	my $new_line = [ @line_array[@rowslice] ];
+
+	# If we have feature-level data, handle it here.
+	if ( my @coords
+	    = ($spotlabels =~ m/(\w+) ?- ?(\w+) ?: ?(\w+)(?: ?- ?(\w+))?/) ) {
+
+	    # Strip out "R" and "C" from block-level coords.
+	    my ($metarow)    = ($coords[0] =~ m/(\d)+/);
+	    my ($metacolumn) = ($coords[1] =~ m/(\d)+/);
+
+	    # Convert text coords into numbers (whose idiotic idea was this, anyway?).
+	    my $row        = ord(uc($coords[2])) - 64;
+	    my $column     = ord(uc($coords[3])) - 64;
+
+	    unshift( @$new_line, $metacolumn, $metarow, $column, $row );
+
+	    $is_feature_level = 1;
+	}
+	# Otherwise, we assume reporter-level data.
+	else {
+	    unshift( @$new_line, $spotlabels );
+	    $is_feature_level = 0;
+	}
+
+	# Print out the reconstructed line; quell undef warnings.
+        {
+            no warnings qw(uninitialized);
+            print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+        }
+
+    }
+
+    seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+    if ( $is_feature_level ) {
+	$self->_genericize_fileinfo();
+    }
+    else {
+	$self->_reporter_genericize_fileinfo();
+    }
+
+    return $fixed_fh;
+}
+
+sub _fix_agilent : PRIVATE {
+
+    my ( $self ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $input_fh = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    my $fixed_fh = IO::File->new_tmpfile;
+
+    # Set up an array slice base to use for the stripped array (faster
+    # than strip_discards()).
+    my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+    while ( my $line = <$input_fh> ) {
+
+        $line =~ s/$RE_LINE_BREAK//xms;
+        my @line_array = split /\t/, $line;
+
+        my $row    = $line_array[ $self->get_index_columns()->[0] ];
+        my $column = $line_array[ $self->get_index_columns()->[1] ];
+
+        # We assume here that Agilent only prints their arrays in single
+        # blocks
+        my $metarow    = 1;
+        my $metacolumn = 1;
+
+        my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+        my $new_line = [ @line_array[@rowslice] ];
+
+        unshift( @$new_line, $metacolumn, $metarow, $column, $row );
+
+        {
+            no warnings qw(uninitialized);
+            print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+        }
+
+    }
+
+    seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+    $self->_genericize_fileinfo();
+
+    return $fixed_fh;
+}
+
+sub _fix_nimblescanfeat : PRIVATE {
+
+    my ( $self ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $input_fh = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    my $fixed_fh = IO::File->new_tmpfile;
+
+    # Set up an array slice base to use for the stripped array (faster
+    # than strip_discards()).
+    my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+    while ( my $line = <$input_fh> ) {
+
+        $line =~ s/$RE_LINE_BREAK//xms;
+        my @line_array = split /\t/, $line;
+
+        my $column = $line_array[ $self->get_index_columns()->[0] ];
+        my $row    = $line_array[ $self->get_index_columns()->[1] ];
+
+        # Nimblegen, like Agilent, only prints their arrays in single
+        # blocks
+        my $metarow    = 1;
+        my $metacolumn = 1;
+
+        my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+        my $new_line = [ @line_array[@rowslice] ];
+
+        unshift( @$new_line, $metacolumn, $metarow, $column, $row );
+
+        {
+            no warnings qw(uninitialized);
+            print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+        }
+
+    }
+
+    seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+    $self->_genericize_fileinfo();
+
+    return $fixed_fh;
+}
+
+sub _fix_nimblegennasa : PRIVATE {
+
+    my ( $self ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $input_fh = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    my $fixed_fh = IO::File->new_tmpfile;
+
+    # Set up an array slice base to use for the stripped array (faster
+    # than strip_discards()).
+    my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+    while ( my $line = <$input_fh> ) {
+
+        $line =~ s/$RE_LINE_BREAK//xms;
+        my @line_array = split /\t/, $line;
+
+        my $column = $line_array[ $self->get_index_columns()->[0] ];
+        my $row    = $line_array[ $self->get_index_columns()->[1] ];
+
+        # Nimblegen, like Agilent, only prints their arrays in single
+        # blocks
+        my $metarow    = 1;
+        my $metacolumn = 1;
+
+        my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+        my $new_line = [ @line_array[@rowslice] ];
+
+        unshift( @$new_line, $metacolumn, $metarow, $column, $row );
+
+        {
+            no warnings qw(uninitialized);
+            print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+        }
+
+    }
+
+    seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+    $self->_genericize_fileinfo();
+
+    return $fixed_fh;
+}
+
+sub _fix_appliedbiosystems : PRIVATE {
+
+    my ( $self ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $input_fh = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    my $fixed_fh = IO::File->new_tmpfile;
+
+    # Set up an array slice base to use for the stripped array (faster
+    # than strip_discards()).
+    my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+    while ( my $line = <$input_fh> ) {
+
+        $line =~ s/$RE_LINE_BREAK//xms;
+        my @line_array = split /\t/, $line;
+
+        my $probe_id = $line_array[ $self->get_index_columns()->[0] ];
+
+        my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+        my $new_line = [ @line_array[@rowslice] ];
+
+        unshift( @$new_line, $probe_id );
+
+        {
+            no warnings qw(uninitialized);
+            print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+        }
+
+    }
+
+    seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+    # FIXME this may actually be compseq, not reporter - depends on
+    # array design TBA.
+    $self->_reporter_genericize_fileinfo();
+
+    $self->_strip_common_heading_suffixes();
+
+    return $fixed_fh;
+}
+
+sub _fix_illumina_perhyb : PRIVATE {
+
+    my ( $self ) = @_;
+
+    # Generic Illumina fixes.
+    my $fixed_fh = $self->_fix_illumina();
+
+    # Per-hyb fixes. Don't strip suffixes if there's only one data column!
+    my $num_datacols = scalar( @{ $self->get_column_headings() } )
+	             - scalar( @{ $self->get_index_columns()   } );
+    if ( $num_datacols > 1 ) {
+	$self->_strip_common_heading_prefixes();
+    }
+
+    return $fixed_fh;
+}
+
+sub _fix_illumina : PRIVATE {
+
+    my ( $self ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    # Fix either per-hyb or FGEM Illumina data.
+    my $input_fh = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    my $fixed_fh = IO::File->new_tmpfile;
+
+    # Set up an array slice base to use for the stripped array (faster
+    # than strip_discards()).
+    my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+    while ( my $line = <$input_fh> ) {
+
+        $line =~ s/$RE_LINE_BREAK//xms;
+        my @line_array = split /\t/, $line;
+
+        my $probe_id = $line_array[ $self->get_index_columns()->[0] ];
+
+        my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+        my $new_line = [ @line_array[@rowslice] ];
+
+        unshift( @$new_line, $probe_id );
+
+        {
+            no warnings qw(uninitialized);
+            print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+        }
+
+    }
+
+    seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+    $self->_reporter_genericize_fileinfo();
+
+    return $fixed_fh;
+}
+
+sub _strip_common_heading_prefixes : PRIVATE {
+
+    my $self = shift;
+
+    # Common suffixes contain metainfo; we don't want them in our
+    # internal QT processing. Here we strip them out.
+    my @headings = @{ $self->get_column_headings() };
+    my $identifier_col = shift @headings;    # Drop the identifier column.
+    my $prefix         = q{};
+
+    # Adapted from similar code by Albannach on perlmonks.org node 274134
+    CHAR:
+    for my $length ( 1 .. length( $headings[0] ) ) {
+        $prefix = substr( $headings[0], 0, $length - 1 );
+        last CHAR unless ( scalar grep {/^\Q$prefix\E/} @headings ) == @headings;
+    }
+
+    # We've gone one CHAR too far - strip off the last character:
+    chop $prefix;
+
+    # Remove the suffix from all the headings.
+    foreach my $heading (@headings) {
+        $heading =~ s/^\Q$prefix\E//;
+    }
+    $self->set_column_headings( [ $identifier_col, @headings ] );
+
+    return;
+}
+
+sub _strip_common_heading_suffixes : PRIVATE {
+
+    my $self = shift;
+
+    # Common suffixes contain metainfo; we don't want them in our
+    # internal QT processing. Here we strip them out.
+    my @headings = @{ $self->get_column_headings() };
+    my $identifier_col = shift @headings;    # Drop the identifier column.
+    my $suffix         = q{};
+
+    # Adapted from similar code by Albannach on perlmonks.org node 274134
+    CHAR:
+    for my $length ( 1 .. length( $headings[0] ) ) {
+        $suffix = substr( $headings[0], -($length), $length );
+        last CHAR unless ( scalar grep {/\Q$suffix\E$/} @headings ) == @headings;
+    }
+
+    # We've gone one CHAR too far - strip off the first character:
+    substr( $suffix, 0, 1, q{} );
+
+    # Remove the suffix from all the headings.
+    foreach my $heading (@headings) {
+        $heading =~ s/\Q$suffix\E$//;
+    }
+    $self->set_column_headings( [ $identifier_col, @headings ] );
+
+    return;
+}
+
+sub _scanarray_parse_image : PRIVATE {
+
+    my ( $self ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $input_fh = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    my $line = <$input_fh>;
+    my @info_headings = split /\t/, $line;
+    my %info;
+
+    IMAGE_LINE:
+    while ( $line = lc(<$input_fh>) ) {
+        last IMAGE_LINE if ( $line =~ m/\A end [ ]/ixms );
+
+        my @info_data = split /\t/, $line, -1;
+
+        my ( $channel, $dye, $fallback );
+        for ( my $i = 0; $i <= $#info_headings; $i++ ) {
+
+	    # Skip blank values.
+	    next unless $info_data[$i];
+
+            $channel = $info_data[$i]
+                if ( $info_headings[$i] =~ m/channel/ixms );
+            $dye = $info_data[$i]
+                if ( $info_headings[$i] =~ m/fluorophore?/ixms );
+
+	    # Fall back to image file name (dubious FIXME).
+            $fallback ||= $info_data[$i]
+                if ( $info_headings[$i] =~ m/image/ixms );
+        }
+
+	# Attempt some smart matching on the results.
+	my $cy3_regexp = qr/ cy (?:anine?)? [-_ ]* 3/ixms;
+	my $cy5_regexp = qr/ cy (?:anine?)? [-_ ]* 5/ixms;
+        if ($dye) {
+            $dye = 'Cy3' if ( $dye =~ $cy3_regexp );
+            $dye = 'Cy5' if ( $dye =~ $cy5_regexp );
+
+            $info{$channel} = $dye;
+        }
+	elsif ( $fallback ) {
+
+	    # This is a fairly desperate measure. We try to guess the
+	    # label used from the image filename.
+	    my $label;
+            $label = 'Cy3' if ( $fallback =~ $cy3_regexp );
+            $label = 'Cy5' if ( $fallback =~ $cy5_regexp );
+
+            $info{$channel} = $label;
+	}
+    }
+    return \%info;
+}
+
+sub _scanarray_parse_measurements : PRIVATE {
+
+    my ( $self ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $input_fh = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    # Read in any measurements, if present
+    my %measurements;
+    my @measurement_headings = @{ $self->get_column_headings() };
+
+    MEASUREMENT_LINE:
+    while ( my $line = <$input_fh> ) {
+
+        last MEASUREMENT_LINE if ( $line =~ m/\A end [ ]/ixms );
+
+        $line =~ s/$RE_LINE_BREAK//xms;
+        my @line_array = split /\t/, $line;
+
+        my $metacolumn = $line_array[ $self->get_index_columns()->[0] ];
+        my $metarow    = $line_array[ $self->get_index_columns()->[1] ];
+        my $column     = $line_array[ $self->get_index_columns()->[2] ];
+        my $row        = $line_array[ $self->get_index_columns()->[3] ];
+
+        for ( my $i = 0; $i <= $#{ $self->get_column_headings() }; $i++ ) {
+            $measurements{"$metacolumn.$metarow.$column.$row"}
+                { $self->get_column_headings()->[$i] } = $line_array[$i];
+        }
+    }
+    return ( \%measurements, \@measurement_headings );
+}
+
+sub _parse_scanarray_header : PRIVATE {
+
+    # Input filehandle should be set to the start of the DATA section
+    # (immediately after the header line) upon exit from this sub.
+
+    my ( $self, $content_start ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $input_fh = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my $info                 = {};
+    my $measurements         = {};
+    my $measurement_headings = [];
+    seek( $input_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+    HEADER_LINE:
+    while ( my $line = <$input_fh> ) {
+
+        # Check we've not run past the first set of headers
+        # (measurement or data). Sometimes DATA starts without a BEGIN
+        # DATA tag.
+        if ( tell($input_fh) > $content_start ) {
+            seek( $input_fh, $content_start, 0 )
+                or croak("Error seeking in data file: $!\n");
+            last HEADER_LINE;
+        }
+
+        # Parse the various block types.
+        if ( my ($data_block_type) = ( $line =~ m/\A begin [ ] (\w*)/ixms ) )
+        {
+
+            # Image info present in header, parse ready for
+            # re-assigning QTs.
+            ( $data_block_type =~ m/image/ixms ) && do {
+                $info = $self->_scanarray_parse_image();
+            };
+
+            # Header contains measurements block as first parsable
+            # header row; parse it into the measurement variables.
+            ( $data_block_type =~ m/measurements/ixms ) && do {
+                seek( $input_fh, $content_start, 0 )
+                    or croak("Error seeking in data file: $!\n");
+                ( $measurements, $measurement_headings )
+                    = $self->_scanarray_parse_measurements();
+
+                # The $file object currently thinks the measurement
+                # section is the start of the data. Re-parse to find
+                # the DATA section.
+                $self->parse_header();
+                last HEADER_LINE;
+            };
+
+            # Data block is the first parsable header row; seek to the
+            # top of the content (DATA) section, drop out of the
+            # header loop.
+            ( $data_block_type =~ m/data/ixms ) && do {
+                seek( $input_fh, $content_start, 0 )
+                    or croak("Error seeking in data file: $!\n");
+                last HEADER_LINE;
+            };
+        }
+    }
+    return ( $info, $measurements, $measurement_headings );
+}
+
+sub _fix_scanarray : PRIVATE {
+
+    my ( $self ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $input_fh = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    # Figure out which channel is which from the file header, detect
+    # measurements if present
+    my $content_start = tell($input_fh);
+
+    # Check the header for image/channel/fluorophore info. Set the
+    # input_fh to the top of the DATA section.
+    my ( $info, $measurements, $measurement_headings )
+        = $self->_parse_scanarray_header( $content_start );
+
+    # Reformat the coordinates in the data section
+    my $fixed_fh = IO::File->new_tmpfile;
+
+    my %data_headings = map { $_ => 1 } @{ $self->get_column_headings() };
+
+    # Set up an array slice base to use for the stripped array (faster
+    # than strip_discards()).
+    my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+    DATA_LINE:
+    while ( my $line = <$input_fh> ) {
+
+        last DATA_LINE if ( $line =~ m/\A end [ ]/ixms );
+
+        $line =~ s/$RE_LINE_BREAK//xms;
+        my @line_array = split /\t/, $line;
+
+        my $metacolumn = $line_array[ $self->get_index_columns()->[0] ];
+        my $metarow    = $line_array[ $self->get_index_columns()->[1] ];
+        my $column     = $line_array[ $self->get_index_columns()->[2] ];
+        my $row        = $line_array[ $self->get_index_columns()->[3] ];
+
+        my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+        my $new_line = [ @line_array[@rowslice] ];
+
+        # Add measurement columns where they're missing from the data section
+        if ( my $header_row
+            = $measurements->{"$metacolumn.$metarow.$column.$row"} ) {
+            foreach my $measurement ( sort keys %$header_row ) {
+                push( @$new_line, $header_row->{$measurement} )
+                    unless ( $data_headings{$measurement} );
+            }
+        }
+
+        unshift( @$new_line, $metacolumn, $metarow, $column, $row );
+
+        {
+            no warnings qw(uninitialized);
+            print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+        }
+
+    }
+
+    # Fix the measurement headings, if we have them
+    foreach my $measurement ( sort @$measurement_headings ) {
+        push( @{ $self->get_column_headings() }, $measurement )
+            unless ( $data_headings{$measurement} );
+    }
+
+    $self->_map_channel_to_fluor($info);
+
+    $self->_genericize_fileinfo();
+
+    # Check that there's no data we're missing
+    FOOTER_LINE:
+    while ( my $line = <$input_fh> ) {
+        if ( $line =~ m/\S/xms && $line !~ m/filter/ixms )
+        {    # Any non-whitespace
+            print {$self->get_error_fh()} (
+                "WARNING: Possible data after END DATA marker is being discarded.\n"
+            );
+            last FOOTER_LINE;
+        }
+    }
+
+    seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+    return $fixed_fh;
+
+}
+
+sub _map_channel_to_fluor : PRIVATE {
+
+    my ( $self, $info ) = @_;
+
+    ref $info eq 'HASH' or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my $warning_given;
+
+    # Fix the column headings
+    HEADING:
+    foreach my $heading ( @{ $self->get_column_headings() } ) {
+
+        my ($wanted) = ( $heading =~ m/\A (ch\d+) [ a-z]/ixms )
+            or next HEADING;
+        $wanted = lc($wanted);
+
+        unless ( $info->{$wanted} ) {
+            unless ( $warning_given->{$wanted} ) {
+                print {$self->get_error_fh()} (
+                    "Warning: unable to parse channel $wanted to fluorophore mapping from file header.\n"
+                );
+                $warning_given->{$wanted}++;
+            }
+            next HEADING;
+        }
+
+        $heading =~ s/\A $wanted/$info->{$wanted}/iexms;
+
+    }
+
+    return;
+}
+
+sub _fix_simple_mcmr : PRIVATE {
+
+    # A generic method to fix files in which the index columns 0..3
+    # are MetaColumn, MetaRow, Column, Row in that order (this is
+    # controled by the T2M_INDICES option in Config.pm).
+
+    my ( $self ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $input_fh = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    my $fixed_fh = IO::File->new_tmpfile;
+
+    # Set up an array slice base to use for the stripped array (faster
+    # than strip_discards()).
+    my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+    while ( my $line = <$input_fh> ) {
+
+        $line =~ s/$RE_LINE_BREAK//xms;
+        my @line_array = split /\t/, $line;
+
+        my $metacolumn = $line_array[ $self->get_index_columns()->[0] ];
+        my $metarow    = $line_array[ $self->get_index_columns()->[1] ];
+        my $column     = $line_array[ $self->get_index_columns()->[2] ];
+        my $row        = $line_array[ $self->get_index_columns()->[3] ];
+
+        my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+        my $new_line = [ @line_array[@rowslice] ];
+
+        unshift( @$new_line, $metacolumn, $metarow, $column, $row );
+
+        {
+            no warnings qw(uninitialized);
+            print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+        }
+
+    }
+
+    seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+    $self->_genericize_fileinfo();
+
+    return $fixed_fh;
+}
+
+sub _fix_spotfinder : PRIVATE {
+
+    my ( $self ) = @_;
+
+    my $fixed_fh = $self->_fix_simple_mcmr();
+}
+
+sub _fix_mev : PRIVATE {
+
+    my ( $self ) = @_;
+
+    my $fixed_fh = $self->_fix_simple_mcmr();
+}
+
+sub _fix_bluefuse : PRIVATE {
+
+    # Note that ideally we would also fix the CH1/CH2 QTs to point to
+    # fluors FIXME (the problem is that there's no standard tag for this
+    # in the files).
+
+    my ( $self ) = @_;
+
+    my $fixed_fh = $self->_fix_simple_mcmr();
+
+    return $fixed_fh;
+}
+
+sub _fix_ucsfspot : PRIVATE {
+
+    my ( $self ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $input_fh = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    my $fixed_fh = IO::File->new_tmpfile;
+
+    # Set up an array slice base to use for the stripped array (faster
+    # than strip_discards()).
+    my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+    while ( my $line = <$input_fh> ) {
+
+        $line =~ s/$RE_LINE_BREAK//xms;
+        my @line_array = split /\t/, $line;
+
+        my $metacolumn = $line_array[ $self->get_index_columns()->[0] ];
+        my $metarow    = $line_array[ $self->get_index_columns()->[1] ];
+        my $column     = $line_array[ $self->get_index_columns()->[2] ];
+        my $row        = $line_array[ $self->get_index_columns()->[3] ];
+
+        # Strip zero-padding
+        $metacolumn =~ s/^0+// if defined($metacolumn);
+        $metarow    =~ s/^0+// if defined($metarow);
+        $column     =~ s/^0+// if defined($column);
+        $row        =~ s/^0+// if defined($row);
+
+        my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+        my $new_line = [ @line_array[@rowslice] ];
+
+        unshift( @$new_line, $metacolumn, $metarow, $column, $row );
+
+        {
+            no warnings qw(uninitialized);
+            print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+        }
+
+    }
+
+    seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+    $self->_genericize_fileinfo();
+
+    return $fixed_fh;
+}
+
+sub _fix_csiro_spot : PRIVATE {
+
+    my ( $self ) = @_;
+
+    my $fixed_fh = $self->_fix_simple_mcmr();
+
+    return $fixed_fh;
+}
+
+sub _fix_imagene : PRIVATE {
+
+    my ( $self ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $input_fh = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    # Try and determine which label was used from the file header.
+    my $label;
+    my $content_start = tell($input_fh);
+    seek($input_fh, 0, 0) or croak("Error rewinding file for input: $!");
+    HEADERLINE:
+    while (my $line = <$input_fh>) {
+
+	# Skip if at the end of the header.
+	if (tell($input_fh) > $content_start) {
+	    seek($input_fh, $content_start, 0)
+		or croak("Error resetting filehandle to data section: $!");
+	    last HEADERLINE;
+	}
+
+	# Dye info only in filename (if then!)
+	if ( $line =~ m/\bImage File\b/ ) {
+	    ($label) = ($line =~ m/(?:\b|_) (cy[35]|alexa\d{3}|green|red) (?:\b|_)/ixms);
+
+	    # Fix casing issues.
+	    if ($label) {
+		$label =~ s/cy/Cy/i;
+		$label =~ s/alexa/Alexa/i;
+		$label =~ s/red/Cy5/i;
+		$label =~ s/green/Cy3/i;
+	    }
+	}
+    }
+    if ($label) {
+	my $columns = $self->get_column_headings();
+	for (my $i = 0; $i <= $#{ $columns }; $i++) {
+
+	    # Don't alter the index columns.
+	    unless ( first { $i == $_ } @{ $self->get_index_columns() } ) {
+		$columns->[$i] .= "_$label";
+	    }
+	}
+	$self->set_column_headings($columns);
+    }
+    else {
+	printf {$self->get_error_fh()} (
+	    "Warning: Unable to determine channel assignment for file %s\n",
+	    $self->get_name(),
+	);
+    }
+
+    my $fixed_fh = $self->_fix_simple_mcmr();
+
+    return $fixed_fh;
+}
+
+sub _imagene3_parse_measurements : PRIVATE {
+
+    my ( $self ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $input_fh = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    # Read in any measurements, if present
+    my %measurements;
+    my @measurement_headings = @{ $self->get_column_headings() };
+    my $data_section_line;
+
+    # No explicit "End" line, we have to spot the next "Begin" line
+    # and backtrack.
+    my $previous_line_loc = tell($input_fh);
+
+    MEASUREMENT_LINE:
+    while ( my $line = <$input_fh> ) {
+
+         if ( $line =~ m/\A begin [ ]/ixms ) {
+	     $data_section_line = $line;
+	     last MEASUREMENT_LINE;
+	 }
+	$previous_line_loc = tell($input_fh);
+
+        $line =~ s/$RE_LINE_BREAK//xms;
+        my @line_array = split /\t/, $line;
+
+        my $metacolumn = $line_array[ $self->get_index_columns()->[0] ];
+        my $metarow    = $line_array[ $self->get_index_columns()->[1] ];
+        my $column     = $line_array[ $self->get_index_columns()->[2] ];
+        my $row        = $line_array[ $self->get_index_columns()->[3] ];
+
+        for ( my $i = 0; $i <= $#{ $self->get_column_headings() }; $i++ ) {
+            $measurements{"$metacolumn.$metarow.$column.$row"}
+                { $self->get_column_headings()->[$i] } = $line_array[$i];
+        }
+    }
+
+    # Rewind to the previous line.
+    seek($input_fh, $previous_line_loc, 0);
+
+    return ( \%measurements, \@measurement_headings, $data_section_line );
+}
+
+sub _parse_imagene3_header : PRIVATE {
+
+    # Input filehandle should be set to the start of the DATA section
+    # (immediately after the header line) upon exit from this sub.
+
+    my ( $self, $content_start ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $input_fh = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my $measurements         = {};
+    my $measurement_headings = [];
+    my $data_section_line;
+    seek( $input_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+    HEADER_LINE:
+    while ( my $line = <$input_fh> ) {
+
+        # Check we've not run past the first set of headers
+        # (measurement or data). Sometimes DATA starts without a BEGIN
+        # DATA tag.
+        if ( tell($input_fh) > $content_start ) {
+            seek( $input_fh, $content_start, 0 )
+                or croak("Error seeking in data file: $!\n");
+            last HEADER_LINE;
+        }
+
+        # Parse the various block types.
+        if ( my ($data_block_type) = ( $line =~ m/\A begin [ ] (.*)/ixms ) )
+        {
+
+            # Header contains measurements block as first parsable
+            # header row; parse it into the measurement variables.
+            if ( $data_block_type =~ m/log [ ]+ ratio [ ]+ data/ixms ) {
+                seek( $input_fh, $content_start, 0 )
+                    or croak("Error seeking in data file: $!\n");
+                ( $measurements, $measurement_headings, $data_section_line )
+                    = $self->_imagene3_parse_measurements();
+
+                # The $file object currently thinks the measurement
+                # section is the start of the data. Re-parse to find
+                # the DATA section.
+                $self->parse_header();
+                last HEADER_LINE;
+            }
+
+            # Data block is the first parsable header row; seek to the
+            # top of the content (DATA) section, drop out of the
+            # header loop.
+            elsif ( $data_block_type =~ m/extracted [ ] data/ixms ) {
+		$data_section_line = $line;
+                seek( $input_fh, $content_start, 0 )
+                    or croak("Error seeking in data file: $!\n");
+                last HEADER_LINE;
+            }
+        }
+    }
+    return ( $measurements, $measurement_headings, $data_section_line );
+}
+
+sub _fix_imagene3 : PRIVATE {
+
+    my ( $self ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $input_fh = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my $content_start = tell($input_fh);
+
+    # Check the header for image/channel/fluorophore info. Set the
+    # input_fh to the top of the DATA section.  Detect measurements if
+    # present. If we ever get these files with meaningful channel
+    # information we would incorporate it in this method call.
+    my ( $measurements, $measurement_headings, $data_section_line )
+        = $self->_parse_imagene3_header( $content_start );
+
+    unless ( defined($data_section_line) && $data_section_line =~ /\A begin /ixms ) {
+	die("Error: Unable to parse extracted data section headings.");
+    }
+
+    # Sort out the Control vs. Experimental columns here, modify the QT names.
+    my (undef, @data_sections) = grep { $_ } split /\t/, $data_section_line;
+    my %is_index = map { $_ => 1 } @{ $self->get_index_columns() };
+    my %qt_processed;
+    my $headings = $self->get_column_headings();
+    DATA_HEADING:
+    for ( my $i = 0; $i < @{ $headings }; $i++ ) {
+	next DATA_HEADING if ($is_index{$i} || ! $headings->[$i]);
+	my $section_num = $qt_processed{ $headings->[$i] } || 0;
+	if ( my $section = $data_sections[$section_num] ) {
+	    $qt_processed{ $headings->[$i] }++;
+	    $headings->[$i] = "$section $headings->[$i]";
+	}
+	else {
+	    die("Error: More duplicate column headings than there are data sections.");
+	}
+    }
+
+    my $fixed_fh = IO::File->new_tmpfile;
+
+    my %data_headings = map { $_ => 1 } @{ $self->get_column_headings() };
+
+    # Set up an array slice base to use for the stripped array (faster
+    # than strip_discards()).
+    my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+    # Drop columns without headings.
+    my @stripped_slice = grep { $headings->[$_] } @$slice;
+    $slice = \@stripped_slice;
+
+    # N.B. this won't work if the index columns are ever allowed to
+    # contain q{}.
+    my @stripped_headings = grep { $_ } @{ $headings };
+    $self->set_column_headings( \@stripped_headings );
+
+    DATA_LINE:
+    while ( my $line = <$input_fh> ) {
+
+        $line =~ s/$RE_LINE_BREAK//xms;
+
+	# Skip over empty lines, end on any "end" or "begin" markers.
+        next DATA_LINE if ( $line =~ m/\A \s* \z/ixms );
+        last DATA_LINE if ( $line =~ m/\A (begin|end) [ ]/ixms );
+
+        my @line_array = split /\t/, $line;
+
+        my $metacolumn = $line_array[ $self->get_index_columns()->[0] ];
+        my $metarow    = $line_array[ $self->get_index_columns()->[1] ];
+        my $column     = $line_array[ $self->get_index_columns()->[2] ];
+        my $row        = $line_array[ $self->get_index_columns()->[3] ];
+
+        my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+        my $new_line = [ @line_array[@rowslice] ];
+
+        # Add measurement columns where they're missing from the data section
+	# FIXME we want to use the $measurements value preferentially for ImaGene3.
+        if ( my $header_row
+            = $measurements->{"$metacolumn.$metarow.$column.$row"} ) {
+            foreach my $measurement ( sort keys %$header_row ) {
+                push( @$new_line, $header_row->{$measurement} )
+                    unless ( $data_headings{$measurement} );
+            }
+        }
+
+        unshift( @$new_line, $metacolumn, $metarow, $column, $row );
+
+        {
+            no warnings qw(uninitialized);
+            print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+        }
+
+    }
+
+    seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+    # Add the measurement headings, if we have them. FIXME sort out
+    # the logic here to preferentially use $measurements headings.
+    foreach my $measurement ( sort @$measurement_headings ) {
+        push( @{ $self->get_column_headings() }, $measurement )
+            unless ( $data_headings{$measurement} );
+    }
+
+    # Check that there's no data we're missing
+    FOOTER_LINE:
+    while ( my $line = <$input_fh> ) {
+        if ( $line =~ m/\S/xms )
+        {    # Any non-whitespace
+            print {$self->get_error_fh()} (
+                "WARNING: Possible data after END DATA marker is being discarded.\n"
+            );
+            last FOOTER_LINE;
+        }
+    }
+
+    $self->_genericize_fileinfo();
+
+    return $fixed_fh;
+}
+
+sub _fix_codelink : PRIVATE {
+
+    my ( $self ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+    my $input_fh = $self->get_filehandle()
+	or croak("Error: Unable to retrieve filehandle.");
+
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    my $fixed_fh = IO::File->new_tmpfile;
+
+    # Set up an array slice base to use for the stripped array (faster
+    # than strip_discards()).
+    my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+    while ( my $line = <$input_fh> ) {
+
+        $line =~ s/$RE_LINE_BREAK//xms;
+
+	# Apparently CodeLink can have empty lines at the end.
+	next if $line =~ m/$RE_EMPTY_STRING/xms;
+
+        my @line_array = split /\t/, $line;
+
+        my $row    = $line_array[ $self->get_index_columns()->[0] ];
+        my $column = $line_array[ $self->get_index_columns()->[1] ];
+
+        # We assume here that CodeLink only prints their arrays in single
+        # blocks; I suspect this may have to change FIXME.
+        my $metarow    = 1;
+        my $metacolumn = 1;
+
+        my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+        my $new_line = [ @line_array[@rowslice] ];
+
+        unshift( @$new_line, $metacolumn, $metarow, $column, $row );
+
+        {
+            no warnings qw(uninitialized);
+            print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+        }
+
+    }
+
+    seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+    $self->_genericize_fileinfo();
+
+    return $fixed_fh;
+}
+
+sub _genericize_fileinfo : PRIVATE {
+
+    my $self = shift;
+
+    my $new_headings
+        = strip_discards( $self->get_index_columns(), $self->get_column_headings() );
+
+    unshift( @$new_headings, 'MetaColumn', 'MetaRow', 'Column', 'Row' );
+
+    $self->set_index_columns( [ 0, 1, 2, 3 ] );
+    $self->set_format_type('Generic');
+    $self->set_column_headings( \@$new_headings );
+
+    return;
+}
+
+sub _reporter_genericize_fileinfo : PRIVATE {
+
+    my $self = shift;
+
+    my $new_headings
+        = strip_discards( $self->get_index_columns(), $self->get_column_headings() );
+
+    unshift( @$new_headings, 'Reporter Identifier' );
+
+    $self->set_index_columns( [0] );
+    $self->set_format_type('FGEM');
+    $self->set_column_headings( \@$new_headings );
+
+    return;
+}
+
+sub _compseq_genericize_fileinfo : PRIVATE {
+
+    my $self = shift;
+
+    my $new_headings
+        = strip_discards( $self->get_index_columns(), $self->get_column_headings() );
+
+    unshift( @$new_headings, 'CompositeSequence Identifier' );
+
+    $self->set_index_columns( [0] );
+
+    # Not FGEM_CS as otherwise this crashes in DataFile::Parser.
+    $self->set_format_type('FGEM');
+    $self->set_column_headings( \@$new_headings );
+
+    return;
+}
+
+=head2 Accessor methods
+
+=over 2
+
+=item set_row_count
+
+Setter method for the total row count.
+
+=item get_row_count
+
+Getter method for the total row count.
+
+=item increment_row_count
+
+Adds one (or the passed value) to the overall row count. Returns the
+new row count.
+
+=item get_parse_errors
+
+Getter method for the total number of parse errors.
+
+=item increment_parse_errors
+
+Adds one (or the passed value) to the overall parsing error
+count. Returns the new error count.
+
+=item get_not_null
+
+The number of data file cells which are not null relating to known
+QTs.
+
+=item increment_not_null
+
+This is an incremental counter for the number of data file cells which
+are not null relating to known QTs. Returns the new "not null" count.
+
+=item set_hyb_identifier
+
+Setter method for the Tab2MAGE or MIAMExpress hybridization identifier
+associated with a raw or normalized data file. Does not apply to FGEM
+files.
+
+=item get_hyb_identifier
+
+Getter method for the Tab2MAGE or MIAMExpress hybridization identifier
+associated with a raw or normalized data file. Does not apply to FGEM
+files.
+
+=item set_hyb_sysuid
+
+Setter method for the internal MIAMExpress SYSUID value associated
+with a raw or normalized data file. Does not apply to FGEM files.
+
+=item get_hyb_sysuid
+
+Getter method for the internal MIAMExpress SYSUID value associated
+with a raw or normalized data file. Does not apply to FGEM files.
+
+=item set_array_design_id
+
+Setter method for an identifier linking the data file to an array
+design. For Tab2MAGE this identifier is the ArrayExpress array
+accession number. For MIAMExpress this identifier is the internal
+ArrayExpress Oracle database identifier for the array design.
+
+=item get_array_design_id
+
+Getter method for an identifier linking the data file to an array
+design. For Tab2MAGE this identifier is the ArrayExpress array
+accession number. For MIAMExpress this identifier is the internal
+ArrayExpress Oracle database identifier for the array design.
+
+=item set_ded_identifier
+
+Setter method for the MAGE identifier string representing the
+DesignElementDimension which has been associated with the file.
+
+=item get_ded_identifier
+
+Getter method for the MAGE identifier string representing the
+DesignElementDimension which has been associated with the file.
+
+=item set_array_design
+
+Setter method for the ArrayDesign object associated with this data
+file. See L<ArrayExpress::Datafile::ArrayDesign> for information on
+this class.
+
+=item get_array_design
+
+Getter method for the Array Design object associated with this data
+file.
+
+=item set_data_metrics
+
+The set_data_metrics method is a setter method for a hashref
+which relates actual column heading to datatype, scale and
+subclass. The keys are QT names as defined in
+L<ArrayExpress::Datafile::QT_list>. Note that the returned hashref
+should only have daughter hashrefs as values, and so if no
+datatype,scale or subclass info is available for a column heading
+(e.g. MetaRow, MetaColumn) then that coumn should not be
+represented. In practice, this method should only return information
+on the QTs for a single software type (see
+$self->check_column_headings).
+
+=item get_data_metrics
+
+Getter method for data metrics hashref (see set_data_metrics).
+
+=item set_intensity_vector
+
+Setter method for a hashref linking a datafile row identifier
+(e.g. "1.1.4.1") to a measured data value. Used in Pearson correlation
+coefficient calculation.
+
+=item get_intensity_vector
+
+Getter method for a hashref linking a datafile row identifier
+(e.g. "1.1.4.1") to a measured data value. Used in Pearson correlation
+coefficient calculation.
+
+=item set_index_columns
+
+Setter method for an arrayref describing the array indices of the
+coordinate columns (MetaColumn, MetaRow etc.) in
+$self->get_column_headings.
+
+=item get_index_columns
+
+Getter method for an arrayref describing the array indices of the
+coordinate columns (MetaColumn, MetaRow etc.) in
+$self->get_column_headings.
+
+=item set_column_headings
+
+Setter method for the actual column headings found in the data file
+(arrayref).
+
+=item get_column_headings
+
+Getter method for the actual column headings found in the data file
+(arrayref).
+
+=item set_heading_qts
+
+Setter method for an arrayref containing a list of the actual
+recognized QTs in the file. This is not a uniqued list - a repeated QT
+will appear multiple times (as for example, in a FGEM data file).
+
+=item get_heading_qts
+
+Getter method for an arrayref containing a list of the actual
+recognized QTs in the file. This is not a uniqued list - a repeated QT
+will appear multiple times (as for example, in a FGEM data file).
+
+=item set_heading_hybs
+
+Setter method for an arrayref containing a list of (potential) hyb ids
+derived from the column headings of a FGEM file. These are checked
+elsewhere.
+
+=item get_heading_hybs
+
+Getter method for an arrayref containing a list of (potential) hyb ids
+derived from the column headings of a FGEM file. These are checked
+elsewhere.
+
+=item add_fail_columns
+
+Method which adds the passed argument to a list of column headings
+which are unrecognized.
+
+=item get_fail_columns
+
+Returns an arrayref listing the unrecognized column headings (uniqued
+and sorted).
+
+=item add_fail_hybs
+
+Method which adds the passed argument to a list of unrecognized
+hybridization identifiers parsed from FGEM column headings which are
+unrecognized. Hybridization identifiers are either the Tab2MAGE or
+MIAMExpress user-supplied names for the hybridizations.
+
+=item get_fail_hybs
+
+Returns an arrayref listing the unrecognised hyb identifiers (uniqued
+and sorted).
+
+=item set_is_exp
+
+Setter method for boolean flag indicating whether the file is an EXP file or not.
+
+=item get_is_exp
+
+Getter method for EXP file flag.
+
+=item get_is_binary
+
+Getter method for binary file flag.
+
+=item set_is_miamexpress
+
+Setter method for boolean flag indicating whether the file is part of
+a MIAMExpress submission, for which slightly different validation
+rules are used. More typically the is_miamexpress argument to new() is used.
+
+=item get_is_miamexpress
+
+Getter method for MIAMExpress file flag.
+
+=item add_factor_value
+
+Mutator method for the experimental factor values associated with the
+file. Takes (category, value) as an argument, adds them to the list.
+
+=item get_factor_value
+
+Getter method for factor values associated with the file; returns a
+hashref in the form:
+
+ {category => [value1, value2, ...], ...}
+
+=item set_ded_type
+
+Setter method for the type of DesignElementDimension associated with
+the file (Feature, Reporter or CompositeSequence).
+
+=item get_ded_type
+
+Getter method for the type of DesignElementDimension associated with
+the file.
+
+=item set_format_type
+
+Setter method for the format type (e.g., Affymetrix, GenePix, BlueFuse
+etc.). See L<ArrayExpress::Curator::Config> for the enumerated types.
+
+=item get_format_type
+
+Getter method for the format type.
+
+=item set_data_type
+
+Setter method for the data type (e.g., raw, normalized,
+transformed). See L<ArrayExpress::Curator::Config> for the enumerated
+types. Also allowed is the 'EXP' file type (Affymetrix).
+
+=item get_data_type
+
+Getter method for the data type.
+
+=item set_qt_type
+
+Setter method for the QT type associated with the file. This is
+derived from the software names defined in
+L<ArrayExpress::Datafile::QT_list>.
+
+=item get_qt_type
+
+Getter method for the QT type associated with the file.
+
+=item set_path
+
+Setter method for the full filesystem path of the data file.
+
+=item get_path
+
+Getter method for the full filesystem path of the data file.
+
+=item set_name
+
+Setter method for the name of the file. Also sets $self->set_path if
+it has not been otherwise set. Note that this is not the same
+behaviour as the "name" argument to the object constructor, which does
+not change the path at all.
+
+=item get_name
+
+Getter method for the name of the file.
+
+=item set_target_filename
+
+Setter method for the name of the output stripped data file.
+
+=item get_target_filename
+
+Getter method for the name of the output stripped data file.
+
+=item get_linebreak_type
+
+Getter method for the line-ending character(s). Can be C<\n>, C<\r\n>,
+C<\r> or C<Unknown>.
+
+=item get_line_format
+
+Getter method for the line-ending format (Mac, Unix, DOS or
+Unknown). Used in reports.
+
+=item set_exp_data
+
+Setter method for storing the output of the $self->parse_exp_file
+method.
+
+=item get_exp_data
+
+Getter method for retrieving the output of the
+$self->parse_exp_file method.
+
+=item set_mage_qtd
+
+Setter method for the
+Bio::MAGE::BioAssayData::QuantitationTypeDimension object associated
+with the file.
+
+=item get_mage_qtd
+
+Getter method for the
+Bio::MAGE::BioAssayData::QuantitationTypeDimension object associated
+with the file.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2004.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments. 
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+1;
+
diff --git a/lib/ArrayExpress/Datafile/Affymetrix.pm b/lib/ArrayExpress/Datafile/Affymetrix.pm
new file mode 100644
index 0000000..4ccc367
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix.pm
@@ -0,0 +1,166 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix data files.
+#
+# Tim Rayner 2005, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Affymetrix.pm 1857 2007-12-16 19:53:50Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../index.html">
+                <img src="../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: Affymetrix.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix - a factory class for generating
+Affymetrix data file parsing objects.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix;
+
+ my $fac = ArrayExpress::Datafile::Affymetrix->new();
+
+ my $cel = $fac->make_parser( 'Data1.CEL' );
+
+ $cel->parse();
+
+ $cel->export($output_filehandle);
+
+=head1 DESCRIPTION
+
+This module is a factory class used in to create data file parsers for
+Affymetrix file formats. CEL, CHP and EXP formats are supported, with
+limited CDF parsing. Both old (GDAC) and new (GCOS/XDA) file formats
+can be parsed.
+
+=head1 METHODS
+
+=over 2
+
+=item new()
+
+The class constructor.
+
+=item make_parser($file)
+
+This method takes a filename argument and returns a parser object that
+can then be used to process the data file and return interesting
+values.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix;
+
+use strict;
+use warnings;
+
+use Class::Std;
+use Carp;
+use Scalar::Util qw(openhandle);
+
+use ArrayExpress::Datafile::Binary qw(
+    get_integer
+);
+
+sub make_parser {
+
+    my ( $self, $file ) = @_;
+
+    unless ( -r $file ) {
+	croak(qq{Error: Cannot read input file "$file".});
+    }
+
+    my %dispatch = (
+	qr/\.CEL \z/ixms => 'ArrayExpress::Datafile::Affymetrix::CELFactory',
+	qr/\.CDF \z/ixms => 'ArrayExpress::Datafile::Affymetrix::CDFFactory',
+	qr/\.CHP \z/ixms => 'ArrayExpress::Datafile::Affymetrix::CHPFactory',
+	qr/\.EXP \z/ixms => 'ArrayExpress::Datafile::Affymetrix::EXPFactory',
+    );
+
+    my $class;
+    DISPATCH:
+    while ( my ( $ext, $target ) = each %dispatch ) {
+	if ( $file =~ $ext ) {
+	    $class = $target;
+	    last DISPATCH;
+	}
+    }
+    unless ( $class ) {
+	croak("Error: Unrecognized filename extension: $file");
+    }
+
+    # Hand off to the appropriate subclass. Allow Perl::Critic to ignore this line:
+    ## no critic ProhibitStringyEval
+    eval "require $class";
+    ## use critic ProhibitStringyEval
+    if ( $@ ) {
+	confess("Error loading subclass $class: $@");
+    }
+    my $fac = $class->new();
+
+    return $fac->make_parser($file);
+}
+
+sub read_magic : RESTRICTED {
+
+    my ( $self, $input ) = @_;
+
+    my $fh;
+    if ( openhandle($input) ) {
+        $fh = $input;
+    }
+    else {
+        $fh = IO::File->new( $input, '<' )
+            or croak("Unable to open CDF file $input : $!\n");
+    }
+
+    binmode($fh);
+    sysseek( $fh, 0, 0 )
+        or croak(
+        qq{Error rewinding filehandle for "magic" integer check : $!\n});
+
+    my $magic = get_integer($fh);
+
+    return wantarray ? ( $magic, $fh ) : $magic;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CDF.pm b/lib/ArrayExpress/Datafile/Affymetrix/CDF.pm
new file mode 100644
index 0000000..5703ed6
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CDF.pm
@@ -0,0 +1,136 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: CDF.pm 2023 2008-04-13 11:25:46Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../../index.html">
+                <img src="../../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: CDF.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CDF.pm - CDF data file
+parsing.
+
+=head1 SYNOPSIS
+
+ use base qw( ArrayExpress::Datafile::Affymetrix::CDF );
+
+=head1 DESCRIPTION
+
+This module is an abstract superclass used in parsing and export of
+data from Affymetrix CDF files.
+
+Please see L<ArrayExpress::Datafile::Affymetrix::Parser> for
+methods common to all the Affymetrix parser classes.
+
+=head1 METHODS
+
+=over 2
+
+=item get_num_qc_cells()
+
+The number of cells on the chip dedicated to QC measurements.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CDF;
+use base 'ArrayExpress::Datafile::Affymetrix::Parser';
+
+use strict;
+use warnings;
+
+use Carp;
+use Scalar::Util qw(openhandle);
+use IO::File;
+use Class::Std;
+
+use ArrayExpress::Datafile::Binary qw(
+    get_integer
+);
+
+my %num_qc_cells : ATTR( :name<num_qc_cells>, :default<undef> );
+
+sub parse {
+
+    my ( $self ) = @_;
+
+    my $input = $self->get_input();
+
+    my $fh;
+    if ( openhandle($input) ) {
+        $fh = $input;
+    }
+    else {
+        $fh = IO::File->new( $input, '<' )
+            or croak("Unable to open CDF file $input : $!\n");
+    }
+
+    binmode($fh);
+    sysseek( $fh, 0, 0 )
+        or croak("Error rewinding filehandle for input: $!\n");
+
+    my $magic = get_integer($fh);
+
+    unless ($magic == $self->get_required_magic()) {
+	croak("Error: Incorrect parser class used for CDF type ($magic)");
+    }
+
+    $self->parse_cdf($fh);
+
+    return;
+}
+
+sub get_probeset_ids {
+
+    # Returns a list of probeset identifiers, in order.
+
+    my ( $self ) = @_;
+
+    my $data = $self->get_data();
+
+    return [ map { $_->{name} } @{ $data } ];
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CDF/GDAC_CDF.pm b/lib/ArrayExpress/Datafile/Affymetrix/CDF/GDAC_CDF.pm
new file mode 100644
index 0000000..acdad49
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CDF/GDAC_CDF.pm
@@ -0,0 +1,201 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: GDAC_CDF.pm 2081 2008-06-14 22:00:45Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../../index.html">
+                <img src="../../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: GDAC_CDF.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::GDAC_CDF.pm - GDAC CDF data file
+parsing.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::CDF::GDAC_CDF;
+
+ my $cdf = ArrayExpress::Datafile::Affymetrix::CDF::GDAC_CDF->new({
+     input => 'HG-U133A.cdf',
+ });
+ $cdf->parse();
+
+=head1 DESCRIPTION
+
+This module implements parsing and export of data from Affymetrix GDAC
+CDF files.
+
+Please see L<ArrayExpress::Datafile::Affymetrix::Parser> for
+methods common to all the Affymetrix parser classes.
+
+=head1 METHODS
+
+Most accessors and methods are implemented in the superclass; see
+L<ArrayExpress::Datafile::Affymetrix::CDF>.
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CDF::GDAC_CDF;
+use base 'ArrayExpress::Datafile::Affymetrix::CDF';
+
+use strict;
+use warnings;
+
+use Carp;
+use Scalar::Util qw(openhandle);
+use Class::Std;
+
+use ArrayExpress::Curator::Common qw(
+    round
+);
+
+use ArrayExpress::Datafile::Binary qw(
+    get_integer
+    get_DWORD
+    get_unsigned_short
+    get_unsigned_char
+    get_signed_char
+    get_float
+    get_ascii
+);
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    $self->set_required_magic(1178878811);
+
+    return;
+}
+
+#############################
+# GDAC CDF specific methods #
+#############################
+
+sub parse_cdf : RESTRICTED {
+
+    my ( $self, $fh ) = @_;
+
+    openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+    binmode( $fh, ":crlf" );
+    seek( $fh, 0, 0 ) or croak("Error rewinding filehandle for input: $!\n");
+
+    my $label = <$fh>;
+    $label =~ s/[\r\n]* \z//xms;
+    unless ( $label eq '[CDF]' ) {
+        croak("Error: Unrecognized CDF file format: $label\n");
+    }
+
+    my $version = <$fh>;
+
+    # We just want the version string.
+    $version =~ s{\A Version= ([^\r\n]*) [\r\n]*}{$1}xms;
+    unless ( $version eq 'GC3.0' ) {
+        croak("Error: Unrecognized CDF file version: $version\n");
+    }
+    $self->set_version($version);
+
+    my $line = q{};
+    until ( $line eq '[Chip]' ) { $line = $self->_get_line($fh); }
+
+    $self->set_chip_type(   ( split /=/, $self->_get_line($fh) )[1] );
+    $self->set_num_columns( ( split /=/, $self->_get_line($fh) )[1] );
+    $self->set_num_rows(    ( split /=/, $self->_get_line($fh) )[1] );
+    $self->set_num_cells(   ( split /=/, $self->_get_line($fh) )[1] );
+    my $max_cell = ( ( split /=/, $self->_get_line($fh) )[1] );
+    $self->set_num_qc_cells( ( split /=/, $self->_get_line($fh) )[1] );
+
+    # Here we skip all the QC info for now
+
+    my $data;
+    $data->[ $self->get_num_cells() - 1 ] = {};
+
+    # Generate a mapping of unit names and numbers
+    foreach my $cell (@$data) {
+
+        my ( $unitno, $unitname );
+        until ( $line =~ m{\[ Unit \d+ \]}xms ) {
+            unless ( defined( $line = <$fh> ) ) {
+                croak("Premature end of CDF file.");
+            }
+        }
+
+        until ( ( $unitname ) = ( $line =~ m{\A Name= ([^\r\n]+) [\r\n]* \z}xms ) ) {
+            unless ( defined( $line = <$fh> ) ) {
+                croak("Premature end of CDF file.");
+            }
+        }
+        until ( ( $unitno ) = ( $line =~ m{\A UnitNumber= (\d+) [\r\n]* \z}xms ) ) {
+            unless ( defined( $line = <$fh> ) ) {
+                croak("Premature end of CDF file.");
+            }
+        }
+
+        # Expression CDFs don't keep the name in the unit, but in the blocks.
+        if ( $unitname eq 'NONE' ) {
+            $unitname = undef;
+            until ( $line =~ m!\[Unit($unitno)_Block\d+\]! ) {
+                unless ( defined( $line = <$fh> ) ) {
+                    croak("Premature end of CDF file.");
+                }
+            }
+            until ( ($unitname) = ( $line =~ m/^Name=([^\r\n]+)[\r\n]*$/ ) ) {
+                unless ( defined( $line = <$fh> ) ) {
+                    croak("Premature end of CDF file.");
+                }
+            }
+        }
+
+        unless ($unitname) {
+            croak("Error: No name for unit $unitno.\n");
+        }
+        $cell->{cell_no} = $unitno;
+        $cell->{name}    = $unitname;
+    }
+    $self->set_data($data);
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CDF/XDA_CDF.pm b/lib/ArrayExpress/Datafile/Affymetrix/CDF/XDA_CDF.pm
new file mode 100644
index 0000000..0eb10d0
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CDF/XDA_CDF.pm
@@ -0,0 +1,226 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: XDA_CDF.pm 2023 2008-04-13 11:25:46Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../../index.html">
+                <img src="../../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: XDA_CDF.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CDF::XDA_CDF.pm - XDA CDF data
+file parsing.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::CDF::XDA_CDF;
+
+ my $cdf = ArrayExpress::Datafile::Affymetrix::CDF::XDA_CDF->new({
+     input => 'HG-U133A.cdf',
+ });
+ $cdf->parse();
+
+=head1 DESCRIPTION
+
+This module implements parsing and export of data from Affymetrix XDA
+CDF files.
+
+Please see L<ArrayExpress::Datafile::Affymetrix::Parser> for
+methods common to all the Affymetrix parser classes.
+
+=head1 METHODS
+
+Most parsing methods and accessors are implemented in the
+superclasses. See L<ArrayExpress::Datafile::Affymetrix::CDF::GDAC_CDF> for
+information.
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CDF::XDA_CDF;
+use base 'ArrayExpress::Datafile::Affymetrix::CDF';
+
+use strict;
+use warnings;
+
+use Carp;
+use Scalar::Util qw(openhandle);
+use IO::File;
+use Class::Std;
+
+use ArrayExpress::Curator::Common qw(
+    round
+);
+
+use ArrayExpress::Datafile::Binary qw(
+    get_integer
+    get_DWORD
+    get_unsigned_short
+    get_unsigned_char
+    get_signed_char
+    get_float
+    get_ascii
+);
+
+sub START {
+    my ( $self, $id, $args ) = @_;
+
+    $self->set_required_magic(67);
+
+    return;
+}
+
+############################
+# XDA CDF specific methods #
+############################
+
+sub parse_cdf : RESTRICTED {
+
+    # NB quite a lot of stuff is being discarded here. This relieves
+    # memory overhead, but we may want to add some things back at some
+    # stage.
+
+    my ( $self, $fh ) = @_;
+
+    openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+    binmode($fh);
+    sysseek( $fh, 0, 0 )
+        or croak("Error rewinding filehandle for input: $!\n");
+
+    my $magic = get_integer($fh);
+    unless ( $magic == 67 ) {
+        croak("Error: Unrecognized CDF type: $magic\n");
+    }
+
+    # Header parsing
+    $self->set_version( get_integer($fh) );
+    $self->set_num_columns( get_unsigned_short($fh) );
+    $self->set_num_rows( get_unsigned_short($fh) );
+    $self->set_num_cells( get_integer($fh) );    # cells == probesets
+    $self->set_num_qc_cells( get_integer($fh) );
+
+    # header reseq_ref_seq
+    my $reseq_ref_seq = get_ascii( $fh, get_integer($fh) );
+
+    # Initialize $data. Points to an array of hashes
+    my $data;
+    $data->[ $self->get_num_cells() - 1 ] = {};
+
+    # Initialize $qc_data. Points to an array of hashes. FIXME discarded
+    # at the moment.
+    my $qc_data;
+    $qc_data->[ $self->get_num_qc_cells() - 1 ] = {};
+
+    # Get probeset names. This is what we're mainly interested in here.
+    foreach my $cell (@$data) {
+        $cell->{name} = get_ascii( $fh, 64 );
+
+        # Remove trailing zeroes (non-ASCII data).
+        $cell->{name} =~ s/\x00* \z//xms;
+    }
+
+    foreach my $qc_cell (@$qc_data) {
+        get_integer($fh);
+    }    # qc_cell filepos
+    foreach my $cell (@$data) { get_integer($fh) }    # cell filepos
+
+    foreach my $qc_cell (@$qc_data) {
+        get_unsigned_short($fh);                      # qc_cell type
+        my $num_probes = get_integer($fh);            # qc_cell num_probes
+
+        for ( 0 .. ( $num_probes - 1 ) ) { # We just discard all this for now.
+
+            get_unsigned_short($fh);       # probe x
+            get_unsigned_short($fh);       # probe y
+            get_unsigned_char($fh);        # probe length
+
+            # NB docs say this is unsigned but they appear to be wrong:
+            get_signed_char($fh);          # probe match_flag
+            get_unsigned_char($fh);        # probe bkd_flag
+
+        }
+    }
+
+    foreach my $cell (@$data) {
+
+        get_unsigned_short($fh);           # cell type
+        get_unsigned_char($fh);            # cell direction
+        get_integer($fh);                  # cell num_atoms
+        my $num_blocks = get_integer($fh);
+        get_integer($fh);                  # cell num_cells
+        $cell->{cell_no} = get_integer($fh); # corresponds to probe set number
+        get_unsigned_char($fh);              # cell cells_per_atom
+
+        foreach my $block ( @{ $cell->{block} }[ 0 .. ( $num_blocks - 1 ) ] )
+        {
+
+            get_integer($fh);                # block num_atoms
+            my $num_cells = get_integer($fh);
+            get_unsigned_char($fh);          # block cells_per_atom
+            get_unsigned_char($fh);          # block direction
+            get_integer($fh);                # block atom1_pos
+            get_integer($fh);                # block atom2_pos
+            $block->{name} = get_ascii( $fh, 64 );
+
+            my @cells;
+            foreach my $cell ( @cells[ 0 .. ( $num_cells - 1 ) ] ) {
+
+                get_integer($fh);            # cell atom_no
+                get_unsigned_short($fh);     # cell x
+                get_unsigned_short($fh);     # cell y
+
+                # Relative to seq for reseq cells:
+                get_integer($fh);            # cell index_pos
+                get_ascii( $fh, 1 );         # cell probe_base
+                get_ascii( $fh, 1 );         # cell target_base
+
+            }
+        }
+    }
+
+    $self->set_data($data);
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CDFFactory.pm b/lib/ArrayExpress/Datafile/Affymetrix/CDFFactory.pm
new file mode 100644
index 0000000..45ee500
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CDFFactory.pm
@@ -0,0 +1,130 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: CDFFactory.pm 1857 2007-12-16 19:53:50Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../index.html">
+                <img src="../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: CDFFactory.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CDFFactory - a factory class for generating
+Affymetrix CDF file parsing objects.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::CDFFactory;
+
+ my $fac = ArrayExpress::Datafile::Affymetrix::CDFFactory->new();
+
+ my $cdf = $fac->make_parser( 'Data1.CDF' );
+
+ $cdf->parse();
+
+ $cdf->export($output_filehandle);
+
+=head1 DESCRIPTION
+
+This module is a factory class used in to create data file parsers for
+Affymetrix CDF files. Both old (GDAC) and new (GCOS/XDA) file formats
+can be parsed.
+
+=head1 METHODS
+
+=over 2
+
+=item new()
+
+The class constructor.
+
+=item make_parser($file)
+
+This method takes a filename argument and returns a parser object that
+can then be used to process the data file and return interesting
+values.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CDFFactory;
+use base 'ArrayExpress::Datafile::Affymetrix';
+
+use strict;
+use warnings;
+
+use Carp;
+use IO::File;
+use Class::Std;
+
+sub make_parser {
+
+    my ( $self, $input ) = @_;
+
+    my ( $magic, $fh ) = $self->read_magic( $input );
+
+    # FIXME it would be good to derive this automatically from what's
+    # available.
+    my %dispatch = (
+	1178878811 => 'ArrayExpress::Datafile::Affymetrix::CDF::GDAC_CDF',
+	67         => 'ArrayExpress::Datafile::Affymetrix::CDF::XDA_CDF',
+    );
+
+    my $class;
+    unless ( $class = $dispatch{ $magic } ) {
+	croak("Error: Unrecognized CDF file type: $magic");
+    }
+
+    # Return the appropriate parser object. Allow Perl::Critic to ignore this line:
+    ## no critic ProhibitStringyEval
+    eval "require $class";
+    ## use critic ProhibitStringyEval
+    if ( $@ ) {
+	confess("Error loading subclass $class: $@");
+    }
+    return $class->new({
+	input => $fh,
+    });
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CEL.pm b/lib/ArrayExpress/Datafile/Affymetrix/CEL.pm
new file mode 100644
index 0000000..d530dea
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CEL.pm
@@ -0,0 +1,294 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: CEL.pm 2023 2008-04-13 11:25:46Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../../index.html">
+                <img src="../../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: CEL.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CEL.pm - CEL data file parsing
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::CEL;
+
+ my $cel = ArrayExpress::Datafile::Affymetrix::CEL->new({
+     input => 'mydatafile.CEL',
+ });
+ $cel->parse();
+ $cel->export($output_fh);
+
+=head1 DESCRIPTION
+
+This module implements an abstract superclass used in parsing and
+export of data from Affymetrix CEL files.
+
+Please see L<ArrayExpress::Datafile::Affymetrix::Parser> for
+methods common to all the Affymetrix parser classes.
+
+=head1 METHODS
+
+=over 2
+
+=item parse_header()
+
+This method will take the C<input> attribute and parse only the header
+metadata. Note that for older CEL file formats, the numbers of masked,
+outlier or modified cells are not set by this method, since these
+values are embedded in the main body of the data.
+
+=item export($fh)
+
+This method takes a filehandle and prints the parsed expression data
+out to it. The QuantitationTypeDimension and DesignElementDimension of
+the resulting matrix are given by the C<get_qtd> and C<get_ded> methods,
+respectively.
+
+=item get_ded($chip_type)
+
+This method takes an optional string argument representing the chip type (e.g.,
+"HG-U133A") and uses it to generate a reference to an array of Feature
+identifiers arranged in the order that the B<export> method outputs
+them (i.e., a DesignElementDimension). The chip type can be derived in
+a number of ways; if EXP files are available, for instance, the
+$exp->get_chip_type() method should return an appropriate string. If this 
+argument is not supplied then the $cel->get_chip_type() method
+is used to derive a value; note however that that method relies
+on parsing an undocumented tag and as such it may fail.
+
+=item get_num_masked()
+
+The number of masked cells.
+
+=item get_num_outliers()
+
+The number of outlier cells.
+
+=item get_num_modified()
+
+The number of modified cells (this should always be zero).
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CEL;
+use base 'ArrayExpress::Datafile::Affymetrix::Parser';
+
+use strict;
+use warnings;
+
+use Readonly;
+use Carp;
+use Scalar::Util qw(openhandle);
+use IO::File;
+use Class::Std;
+
+use ArrayExpress::Datafile::Binary qw(
+    get_integer
+);
+
+my %dedimension    : ATTR( :set<ded>,             :default<[]>    );
+my %num_masked     : ATTR( :name<num_masked>,     :default<undef> );
+my %num_outliers   : ATTR( :name<num_outliers>,   :default<undef> );
+my %num_modified   : ATTR( :name<num_modified>,   :default<undef> );
+
+# Standard CEL QTD
+Readonly my $AFFY_QTD_CEL => [
+    qw(
+        CELX
+        CELY
+        CELIntensity
+        CELIntensityStdev
+        CELPixels
+        CELOutlier
+        CELMask
+        )
+];
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    $self->set_data_storage('HASH');
+
+    # Set the stardard qtd for CEL data.
+    $self->set_headings($AFFY_QTD_CEL);
+
+    return;
+}
+
+sub parse {
+
+    my ( $self ) = @_;
+
+    # Parse_header returns the filehandle set to the top of the main data.
+    my $fh = $self->parse_header();
+
+    $self->parse_cel_body($fh);
+
+    return;
+}
+
+sub parse_header {
+
+    my ( $self ) = @_;
+
+    my $fh = $self->get_filehandle();
+
+    binmode($fh);
+    sysseek( $fh, 0, 0 )
+        or croak(
+        qq{Error rewinding filehandle for "magic" integer check : $!\n});
+
+    $self->set_magic( get_integer($fh) );
+
+    unless ($self->get_magic() == $self->get_required_magic()) {
+	croak("Error: Incorrect parser class used for CEL type ("
+		  . $self->get_magic() . ")");
+    }
+
+    $self->parse_cel_header($fh);
+
+    return $fh;    # Filehandle now set for reading main data body.
+}
+
+sub export {    # Now in full tab-delimited glory...
+    my ( $self, $fh ) = @_;
+
+    openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+    my $data = $self->get_data();
+
+    foreach my $coord ( sort keys %$data ) {
+        print $fh ( join( q{}, $coord, "\t", $data->{$coord}, "\n" ) );
+    }
+
+    return;
+}
+
+sub get_ded {
+
+    my ( $self, $chip_type ) = @_;
+
+    ref $chip_type and confess( 'Bad parameters passed to method' );
+
+    unless ( scalar @{ $dedimension{ident $self} } ) {
+
+        # If no chip type passed, try and figure it out on our own.
+        $chip_type ||= $self->get_chip_type();
+
+        # Basic sanity check here.
+        croak("No chip type information available to CEL->get_ded()\n")
+            unless $chip_type;
+
+        my $data = $self->get_data();
+
+        my @features;
+
+        # Initialize the array; prevents memory fragmentation (maybe).
+        $#features = scalar( grep { defined $_ } values %$data ) - 1;
+
+        my $counter = 0;
+        foreach my $coord ( sort keys %$data ) {
+
+            my ( $colno, $rowno ) = split /\t/, $coord;
+            $features[$counter] = "Affymetrix:Feature:"
+                . $chip_type
+                . ":Probe($colno,$rowno)";
+
+            $counter++;
+        }
+
+        $dedimension{ident $self} = \@features;
+
+    }
+
+    return $dedimension{ident $self};
+}
+
+sub parse_v3_header_tags : RESTRICTED {
+
+    # NB this method is also useful to the CELv4 parser.
+
+    my ( $self, $tagstr ) = @_;
+
+    defined( $tagstr ) or confess( 'Bad parameters passed to method' );
+
+    my @lines = split /[\r\n]+/, $tagstr;
+
+    my %tag;
+    foreach my $line ( @lines ) {
+	my ( $key, $value ) = split /=/, $line, 2;
+	$tag{ $key } = $value;
+    }
+
+    # CEL v4 parsing also uses this, but we don't want to override the
+    # 'official' values.
+    $self->set_num_columns( $tag{ 'Cols' } ) unless $self->get_num_columns();
+    $self->set_num_rows(    $tag{ 'Rows' } ) unless $self->get_num_rows();
+    $self->set_algorithm( $tag{ 'Algorithm' } || 'Unknown' );
+
+    # The following is an undocumented tag, but seems to be consistently
+    # used and is extremely useful. It's likely to be used by GDAC
+    # Exporter (Affy) as it's the only way of identifying the chip type
+    # if only the CEL file is available.
+    my ($chip_type) = ( $tag{ 'DatHeader' } =~ m{[ ] ([^ .]*).1sq [ ]}ixms );
+    $self->set_chip_type($chip_type);
+
+    my @params = split /;/, $tag{ 'AlgorithmParameters' };
+
+    my $parameter_hash = {};
+    foreach my $param (@params) {
+        my ( $key, $value ) = split /:/, $param;
+        $parameter_hash->{$key} = $value;
+    }
+
+    $self->add_parameters($parameter_hash);
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CEL/CELv3.pm b/lib/ArrayExpress/Datafile/Affymetrix/CEL/CELv3.pm
new file mode 100644
index 0000000..a5685dd
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CEL/CELv3.pm
@@ -0,0 +1,281 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: CELv3.pm 2021 2008-04-09 09:55:25Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../../index.html">
+                <img src="../../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: CELv3.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CEL::CELv3.pm - CELv3 data file parsing
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::CEL::CELv3;
+
+ my $cel = ArrayExpress::Datafile::Affymetrix::CEL::CELv3->new({
+     input => 'mydatafile.CEL',
+ });
+ $cel->parse();
+ $cel->export($output_fh);
+
+=head1 DESCRIPTION
+
+This module implements parsing and export of data from Affymetrix GDAC
+CEL (v3) files.
+
+Please see L<ArrayExpress::Datafile::Affymetrix::Parser> for
+methods common to all the Affymetrix parser classes.
+
+=head1 METHODS
+
+Most methods are implemented in the superclass. See
+L<ArrayExpress::Datafile::Affymetrix::CEL> for details.
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CEL::CELv3;
+use base 'ArrayExpress::Datafile::Affymetrix::CEL';
+
+use strict;
+use warnings;
+
+use Readonly;
+use Carp;
+use Scalar::Util qw(openhandle);
+use IO::File;
+use Class::Std;
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    $self->set_required_magic(1279607643);
+
+    return;
+}
+
+##########################
+# CELv3-specific methods #
+##########################
+
+sub parse_cel_header : RESTRICTED {
+
+    my ( $self, $fh ) = @_;
+
+    openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+    # Set fh to plain text parsing, and rewind the file
+    binmode( $fh, ":crlf" );
+    seek( $fh, 0, 0 ) or croak("Error rewinding filehandle for input: $!\n");
+
+    # Read the data in as a series of arrays/hashes.
+
+    ##########
+    # HEADER #
+    ##########
+    my $label = $self->_get_line($fh);
+    unless ( $label eq '[CEL]' ) {
+        croak("Error: unknown CEL file format: $label\n");
+    }
+
+    $self->set_version( ( split /\=/, $self->_get_line($fh) )[1] );
+
+    my $line = q{};
+    until ( $line =~ m/\A \[HEADER\]/xms || ! defined( $line ) ) {
+	$line = $self->_get_line($fh);
+    }
+
+    my $header_tags = q{};
+
+    LINE:
+    while ( $line = <$fh> ) {
+        last LINE if ( $line =~ m/\A \s* \z/xms );
+        $header_tags .= $line;
+    }
+
+    $self->parse_v3_header_tags($header_tags);
+
+    #############
+    # INTENSITY #
+    #############
+    until ( $line =~ m/\A \[INTENSITY\]/xms || ! defined( $line ) ) {
+	$line = $self->_get_line($fh);
+    }
+
+    $self->set_num_cells( ( split /\=/, $self->_get_line($fh) )[1] );
+
+    if ( $self->get_num_cells()
+	     != ( $self->get_num_rows() * $self->get_num_columns() ) ) {
+	carp(
+	    "Format error: number of cells does not agree with row and column numbers"
+	);
+    }
+
+    $self->add_stats(
+        {   'Number of Cells' => $self->get_num_cells(),
+            'Rows'            => $self->get_num_rows(),
+            'Columns'         => $self->get_num_columns(),
+        }
+    );
+
+    return;
+}
+
+sub parse_cel_body : RESTRICTED {
+
+    my ( $self, $fh ) = @_;
+
+    openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+    # Our filehandle should be set to the main data column headings line.
+    if ( $self->_get_line($fh) ne "CellHeader=X\tY\tMEAN\tSTDV\tNPIXELS" ) {
+        croak("Error: unrecognized CEL [INTENSITY] column headings.\n");
+    }
+
+    my $data;
+    my $line = q{};
+
+    LINE:
+    while ( $line = $self->_get_line($fh) ) {
+
+        last LINE if ( $line =~ m/\A \s* \z/xms );
+
+        # Strip unnecessary whitespace.
+        $line =~ s/\A [ ]*//xms;
+        $line =~ s/[ ]* \z//xms;
+        $line =~ s/[ ]* \t [ ]*/\t/gxms;
+
+        my @line_array = split /\t/, $line;
+
+        my $colno = $line_array[0];
+        my $rowno = $line_array[1];
+
+        $data->{"$colno\t$rowno"} = sprintf( "%.2f\t%.2f\t%d\tfalse\tfalse",
+            @line_array[ 2 .. 4 ] );
+
+    }
+
+    #########
+    # MASKS #
+    #########
+    until ( $line =~ m/\A \[MASKS\]/xms || ! defined( $line ) ) {
+	$line = $self->_get_line($fh);
+    }
+
+    $self->set_num_masked( ( split /\=/, $self->_get_line($fh) )[1] );
+
+    $self->add_stats( { 'Number Cells Masked' => $self->get_num_masked() } );
+
+    if ( $self->_get_line($fh) ne "CellHeader=X\tY" ) {
+        croak("Error: unrecognized CEL [MASKS] column headings.\n");
+    }
+
+    LINE:
+    while ( $line = $self->_get_line($fh) ) {
+
+        last LINE if ( $line =~ m/\A \s* \z/xms );
+
+        my @line_array = split /\t/, $line;
+
+        my $colno = $line_array[0];
+        my $rowno = $line_array[1];
+
+        $data->{"$colno\t$rowno"} =~ s{\t false \z}{\ttrue}xms;
+    }
+
+    ############
+    # OUTLIERS #
+    ############
+    until ( $line =~ m/\A \[OUTLIERS\]/xms || ! defined( $line ) ) {
+	$line = $self->_get_line($fh);
+    }
+
+    $self->set_num_outliers( ( split /\=/, $self->_get_line($fh) )[1] );
+
+    $self->add_stats( { 'Number Outlier Cells' => $self->get_num_outliers() } );
+
+    if ( $self->_get_line($fh) ne "CellHeader=X\tY" ) {
+        croak("Error: unrecognized CEL [OUTLIERS] column headings.\n");
+    }
+
+    LINE:
+    while ( $line = $self->_get_line($fh) ) {
+
+        last LINE if ( $line =~ m/\A \s* \z/xms );
+
+        my @line_array = split /\t/, $line;
+
+        my $colno = $line_array[0];
+        my $rowno = $line_array[1];
+
+        $data->{"$colno\t$rowno"} =~ s{\t false \t (?= \w+ \z)}
+	                              {\ttrue\t}xms;
+    }
+
+    ############
+    # MODIFIED #
+    ############
+    until ( $line =~ m/\A \[MODIFIED\]/xms || ! defined( $line ) ) {
+	$line = $self->_get_line($fh);
+    }
+
+    $self->set_num_modified( ( split /\=/, $self->_get_line($fh) )[1] );
+
+    $self->add_stats( { 'Number Cells Modified' => $self->get_num_modified() } );
+
+    warn( "Warning: ignoring " . $self->get_num_modified() . " cells.\n" )
+        if $self->get_num_modified();
+
+    if ( $self->_get_line($fh) ne "CellHeader=X\tY\tORIGMEAN" ) {
+        croak("Error: unrecognized CEL [MODIFIED] column headings.\n");
+    }
+
+    # Insert the data table into the object
+    $self->set_data($data);
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CEL/CELv4.pm b/lib/ArrayExpress/Datafile/Affymetrix/CEL/CELv4.pm
new file mode 100644
index 0000000..939db7f
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CEL/CELv4.pm
@@ -0,0 +1,298 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: CELv4.pm 1852 2007-12-13 10:14:27Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../../index.html">
+                <img src="../../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: CELv4.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CEL::CELv4.pm - CELv4 data file parsing
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::CEL::CELv4;
+
+ my $cel = ArrayExpress::Datafile::Affymetrix::CEL::CELv4->new({
+     input => 'mydatafile.CEL',
+ });
+ $cel->parse();
+ $cel->export($output_fh);
+
+=head1 DESCRIPTION
+
+This module implements parsing and export of data from Affymetrix XDA
+CEL (v4) files.
+
+Please see L<ArrayExpress::Datafile::Affymetrix::Parser> for
+methods common to all the Affymetrix parser classes.
+
+=head1 METHODS
+
+Most parsing methods and accessors are implemented in the
+superclasses. See L<ArrayExpress::Datafile::Affymetrix::CEL::CELv3> for
+information. The following attributes are specific to CELv4 files:
+
+=over 2
+
+=item get_cell_margin()
+
+The cell margin.
+
+=item get_num_subgrids()
+
+The number of subgrids used on the chip (N.B. this is currently
+UNTESTED).
+
+=item get_subgrids()
+
+A reference to a hash containing subgrid information (N.B. this is
+currently UNTESTED).
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CEL::CELv4;
+use base 'ArrayExpress::Datafile::Affymetrix::CEL';
+
+use strict;
+use warnings;
+
+use Readonly;
+use Carp;
+use Scalar::Util qw(openhandle);
+use IO::File;
+use Class::Std;
+
+use ArrayExpress::Curator::Common qw(
+    round
+);
+
+use ArrayExpress::Datafile::Binary qw(
+    get_integer
+    get_DWORD
+    get_unsigned_short
+    get_unsigned_char
+    get_signed_char
+    get_float
+    get_ascii
+);
+
+# Subgrids are v4 CEL only UNTESTED FIXME.
+my %num_subgrids  : ATTR( :name<num_subgrids>, :default<undef> );
+my %subgrids      : ATTR( :name<subgrids>,     :default<{}>    );
+my %cell_margin   : ATTR( :name<cell_margin>,  :default<undef> );
+
+sub START {
+    my ( $self, $id, $args ) = @_;
+
+    $self->set_required_magic(64);
+
+    return;
+}
+
+#################
+# CELv4 methods #
+#################
+
+sub parse_v4_parameters : PRIVATE {
+
+    my ( $self, $paramstr ) = @_;
+
+    ref $paramstr and confess( 'Bad parameters passed to method' );
+
+    my @params = split /[;\s]+/, $paramstr;
+
+    my $parameter_hash;
+    foreach my $param (@params) {
+        my ( $key, $value ) = split /[:=]/, $param;
+        $parameter_hash->{$key} = $value;
+    }
+
+    $self->add_parameters($parameter_hash);
+
+    return;
+}
+
+sub parse_cel_header : RESTRICTED {
+
+    my ( $self, $fh ) = @_;
+
+    openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+    # Binary-mode filehandle, rewind the file
+    binmode( $fh, ":raw" );
+    sysseek( $fh, 0, 0 )
+        or croak("Error rewinding filehandle for input: $!\n");
+
+    # We will read the data in as a series of arrays/hashes:
+
+    my $magic = get_integer($fh);
+    unless ( $magic == 64 ) {
+        croak("Error: Unrecognized CEL type: $magic\n");
+    }
+
+    # Header parsing
+    $self->set_version( get_integer($fh) );
+    $self->set_num_columns( get_integer($fh) );
+    $self->set_num_rows( get_integer($fh) );
+    $self->set_num_cells( get_integer($fh) );    # col x row - tested below
+
+    $self->add_stats(
+        {   'Number of Cells' => $self->get_num_cells(),
+            'Rows'            => $self->get_num_rows(),
+            'Columns'         => $self->get_num_columns(),
+        }
+    );
+
+    if ( $self->get_num_cells() != ( $self->get_num_rows() * $self->get_num_columns() ) ) {
+        carp(
+            "Format error: number of cells does not agree with row and column numbers"
+        );
+    }
+
+    $self->parse_v3_header_tags( get_ascii( $fh, get_integer($fh) ) );
+
+    $self->set_algorithm( get_ascii( $fh, get_integer($fh) ) );
+
+    $self->parse_v4_parameters( get_ascii( $fh, get_integer($fh) ) );
+
+    $self->set_cell_margin( get_integer($fh) );
+    $self->set_num_outliers( get_DWORD($fh) );
+    $self->set_num_masked( get_DWORD($fh) );
+    $self->set_num_subgrids( get_integer($fh) );
+
+    $self->add_stats(
+        {   'Number Cells Masked'  => $self->get_num_masked(),
+            'Number Outlier Cells' => $self->get_num_outliers(),
+        }
+    );
+
+    return;
+}
+
+sub parse_cel_body : RESTRICTED {
+
+    # Parses CEL files (passed diff test against old CEL file format,
+    # so more-or-less okay, but see subgrid comment at end)
+
+    my ( $self, $fh ) = @_;
+
+    openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+    # Data starts here. These are sizable arrays.  Our filehandle should
+    # have been set to the start of the main data loop section.
+
+    # Read intensities
+    # $self->get_data() points to a hash of data values
+    my $data;
+
+    # Y is rows, X is columns.
+    for ( my $rowno = 0; $rowno < $self->get_num_rows(); $rowno++ ) {
+        for ( my $colno = 0; $colno < $self->get_num_columns(); $colno++ ) {
+
+            $data->{"$colno\t$rowno"} = sprintf(
+                "%.2f\t%.2f\t%d\tfalse\tfalse",
+                round( get_float($fh), 1 ),
+                round( get_float($fh), 1 ),
+                get_unsigned_short($fh),
+            );
+
+        }
+    }
+
+    # Read masked cells
+    for ( my $cell = 0; $cell < $self->get_num_masked(); $cell++ ) {
+        my $colno = get_unsigned_short($fh);
+        my $rowno = get_unsigned_short($fh);
+        $data->{"$colno\t$rowno"} =~ s{\t false \z}{\ttrue}xms;
+    }
+
+    # Read outlier cells
+    for ( my $cell = 0; $cell < $self->get_num_outliers(); $cell++ ) {
+        my $colno = get_unsigned_short($fh);
+        my $rowno = get_unsigned_short($fh);
+        $data->{"$colno\t$rowno"} =~ s{\t false \t (?= \w+ \z)}
+	                              {\ttrue\t}xms;
+    }
+
+    # NB we should parse out the subgrids here FIXME
+    # Note - the following is untested code
+    my $subgrids;
+    for ( my $cell = 0; $cell < $self->get_num_subgrids(); $cell++ ) {
+        $subgrids->[$cell]{row}    = get_integer($fh);
+        $subgrids->[$cell]{column} = get_integer($fh);
+
+        $subgrids->[$cell]{ul_x} = get_float($fh);    # pixel coords
+        $subgrids->[$cell]{ul_y} = get_float($fh);
+        $subgrids->[$cell]{ur_x} = get_float($fh);
+
+        # typo in spec fixed here, I hope
+        $subgrids->[$cell]{ur_y} = get_float($fh);
+        $subgrids->[$cell]{ll_x} = get_float($fh);
+        $subgrids->[$cell]{ll_y} = get_float($fh);
+        $subgrids->[$cell]{lr_x} = get_float($fh);
+
+        # typo in spec fixed here, I hope
+        $subgrids->[$cell]{lr_y} = get_float($fh);
+
+        $subgrids->[$cell]{left_pos}   = get_integer($fh);    # cell positions
+        $subgrids->[$cell]{top_pos}    = get_integer($fh);
+        $subgrids->[$cell]{right_pos}  = get_integer($fh);
+        $subgrids->[$cell]{bottom_pos} = get_integer($fh);
+
+        # We currently have no information on the format of these entries in
+        # text files, so we don't print them out FIXME
+    }
+
+    $self->set_subgrids($subgrids);
+
+    # Insert the data table into the object
+    $self->set_data($data);
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CELFactory.pm b/lib/ArrayExpress/Datafile/Affymetrix/CELFactory.pm
new file mode 100644
index 0000000..9621093
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CELFactory.pm
@@ -0,0 +1,131 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: CELFactory.pm 1984 2008-03-03 17:47:18Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../index.html">
+                <img src="../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: CELFactory.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CELFactory - a factory class for generating
+Affymetrix CEL file parsing objects.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::CELFactory;
+
+ my $fac = ArrayExpress::Datafile::Affymetrix::CELFactory->new();
+
+ my $cel = $fac->make_parser( 'Data1.CEL' );
+
+ $cel->parse();
+
+ $cel->export($output_filehandle);
+
+=head1 DESCRIPTION
+
+This module is a factory class used in to create data file parsers for
+Affymetrix CEL files. Both old (GDAC) and new (GCOS/XDA) file formats
+can be parsed.
+
+=head1 METHODS
+
+=over 2
+
+=item new()
+
+The class constructor.
+
+=item make_parser($file)
+
+This method takes a filename argument and returns a parser object that
+can then be used to process the data file and return interesting
+values.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CELFactory;
+use base 'ArrayExpress::Datafile::Affymetrix';
+
+use strict;
+use warnings;
+
+use Carp;
+use IO::File;
+use Class::Std;
+
+sub make_parser {
+
+    my ( $self, $input ) = @_;
+
+    my ( $magic, $fh ) = $self->read_magic( $input );
+
+    # FIXME it would be good to derive this automatically from what's
+    # available.
+    my %dispatch = (
+	1279607643 => 'ArrayExpress::Datafile::Affymetrix::CEL::CELv3',
+	64         => 'ArrayExpress::Datafile::Affymetrix::CEL::CELv4',
+	315        => 'ArrayExpress::Datafile::Affymetrix::Calvin::CEL',
+    );
+
+    my $class;
+    unless ( $class = $dispatch{ $magic } ) {
+	croak("Error: Unrecognized CEL file type: $magic");
+    }
+
+    # Return the appropriate parser object. Allow Perl::Critic to ignore this line:
+    ## no critic ProhibitStringyEval
+    eval "require $class";
+    ## use critic ProhibitStringyEval
+    if ( $@ ) {
+	confess("Error loading subclass $class: $@");
+    }
+    return $class->new({
+	filehandle => $fh,
+    });
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CHP.pm b/lib/ArrayExpress/Datafile/Affymetrix/CHP.pm
new file mode 100644
index 0000000..37f5a1c
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CHP.pm
@@ -0,0 +1,375 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: CHP.pm 2025 2008-04-15 23:10:14Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../../index.html">
+                <img src="../../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: CHP.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CHP - GDAC CHP data file
+parsing.
+
+=head1 SYNOPSIS
+
+ use base qw( ArrayExpress::Datafile::Affymetrix::CHP );
+
+=head1 DESCRIPTION
+
+Abstract superclass for parsing and export of data from Affymetrix CHP
+files. 
+
+Please see the L<ArrayExpress::Datafile::Affymetrix::Parser>
+documentation for methods common to all the Affymetrix file classes.
+
+=head1 METHODS
+
+=over 2
+
+=item C<parse_header()>
+
+This method will take the C<input> attribute and parse only the header
+metadata.
+
+=item C<export($fh, $cdf)>
+
+This method takes a filehandle and prints the parsed expression data
+out to it. The method also requires a pre-parsed CDF object to be
+passed to it. The QuantitationTypeDimension and DesignElementDimension
+of the resulting matrix are given by the C<get_qtd> and C<get_ded>
+methods, respectively.
+
+=item C<get_ded($cdf, $chip_type)>
+
+This method takes a pre-parsed CDF object, and an optional string
+argument representing the chip type (e.g., "HG-U133A"). The method
+uses these arguments to generate a reference to an array of
+CompositeSequence identifiers, arranged in the order that the
+B<export> method outputs them (i.e., a DesignElementDimension). The
+chip type can be derived in a number of ways; if EXP files are
+available, for instance, the $exp->get_chip_type() method should
+return an appropriate string. If this argument is not supplied then
+the chip_type() methods for the CHP and CDF objects are inspected.
+
+=pod
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CHP;
+use base 'ArrayExpress::Datafile::Affymetrix::Parser';
+
+use strict;
+use warnings;
+
+use Readonly;
+use Carp;
+use Scalar::Util qw( openhandle );
+use IO::File;
+use Class::Std;
+
+use ArrayExpress::Datafile::Binary qw(
+    get_integer
+);
+
+my %affy_qtd_chp               : ATTR( :get<affy_qtd_chp> );
+my %affy_qtd_chp_comparison    : ATTR( :get<affy_qtd_chp_comparison> );
+my %affy_qtd_chp_snp           : ATTR( :get<affy_qtd_chp_snp> );
+my %affy_qtd_chp_snp100        : ATTR( :get<affy_qtd_chp_snp100> );
+
+sub BUILD {
+
+    my ( $self, $id, $args ) = @_;
+
+    # Standard CHP QTDs.
+    $affy_qtd_chp{ident $self} = [
+	qw(
+	   ProbeSetName
+	   CHPPairs
+	   CHPPairsUsed
+	   CHPSignal
+	   CHPDetection
+	   CHPDetectionPvalue
+       )
+    ];
+
+    $affy_qtd_chp_comparison{ident $self} = [
+	qw(
+	   ProbeSetName
+	   CHPPairs
+	   CHPPairsUsed
+	   CHPSignal
+	   CHPDetection
+	   CHPDetectionPvalue
+	   CHPCommonPairs
+	   CHPSignalLogRatio
+	   CHPSignalLogRatioLow
+	   CHPSignalLogRatioHigh
+	   CHPChange
+	   CHPChangePvalue
+       )
+    ];
+
+    $affy_qtd_chp_snp{ident $self} = [
+	qw(
+	   ProbeSetName
+	   CHPAllele
+	   CHPAllelePvalue
+	   CHPAlleleRAS1
+	   CHPAlleleRAS2
+       )
+    ];
+
+    $affy_qtd_chp_snp100{ident $self} = [
+	qw(
+	   ProbeSetName
+	   CHPAllele
+	   CHPAllelePvalue
+	   CHPAllelePvalueAA
+	   CHPAllelePvalueAB
+	   CHPAllelePvalueBB
+	   CHPAllelePvalueNoCall
+       )
+    ];
+
+    return;
+}
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    $self->set_required_magic(1701733703);
+
+    return;
+}
+
+sub parse {
+
+    my ( $self ) = @_;
+
+    my $fh = $self->parse_header();
+
+    $self->parse_chp($fh);
+
+    return;
+}
+
+sub parse_header {
+
+    my ( $self ) = @_;
+
+    my $fh = $self->get_filehandle();
+
+    binmode($fh);
+    sysseek( $fh, 0, 0 )
+        or croak(
+        qq{Error rewinding filehandle for "magic" integer check : $!\n});
+
+    $self->set_magic( get_integer($fh) );
+
+    unless ($self->get_magic() == $self->get_required_magic()) {
+	croak("Error: Incorrect parser class used for CHP type ("
+		  . $self->get_magic() . ")");
+    }
+
+    $self->parse_chp_header($fh);
+
+    return $fh;    # Filehandle now set for reading main data body.
+}
+
+sub export {    # CONFIRMED BY DIFF
+
+    my ( $self, $fh, $cdf ) = @_;
+
+    openhandle($fh) or confess( 'Bad parameters passed to method' );
+    $cdf->isa('ArrayExpress::Datafile::Affymetrix::Parser')
+        or confess( 'Bad parameters passed to method' );
+
+    # Basic sanity check here. If the parse hasn't been run yet, do so.
+    unless ( scalar @{ $cdf->get_probeset_ids() } ) {
+	$cdf->parse();
+    }
+
+    my $data      = $self->get_data();
+    my $probesets = $cdf->get_probeset_ids();
+    my @qtd       = @{ $self->get_headings() };
+
+    my @detection = ( q{Present}, q{Marginal}, q{Absent}, q{No Call}, );
+    my @change = (
+        q{null},
+        q{Increase},
+        q{Decrease},
+        q{Marginal Increase},
+        q{Marginal Decrease},
+        q{No change},
+        q{No call},
+    );
+    my @allele = (
+        q{NoCall}, q{NoCall}, q{NoCall}, q{NoCall}, q{NoCall}, q{NoCall},
+        q{AA},     q{BB},     q{AB},     q{AB_A},   q{AB_B},   q{NoCall},
+    );
+
+    for ( my $i = 0; $i <= $#{$data}; $i++ ) {
+
+        my @line_array;
+
+        carp(     "WARNING: No CDF data for CHP unit "
+                . ( $i + 1 )
+                . ". Incorrect CDF file used?\n" )
+            unless $probesets->[$i];
+        push( @line_array, ( $probesets->[$i] || q{} ) );  # ProbeSetName
+
+        # Skip the first item (ProbeSetName).
+        foreach my $qt ( @qtd[ 1 .. $#qtd ] ) {
+
+            my $value = $data->[$i]{$qt};
+
+            QTYPE:
+            {    # some reformatting required
+
+                ( $qt eq 'CHPDetection' )
+                    && do { $value = $detection[$value] || q{null}; last QTYPE; };
+
+                ( $qt eq 'CHPSignal' )
+                    && do { $value = sprintf( "%.1f", $value ); last QTYPE; };
+
+                ( $qt eq 'CHPDetectionPvalue' )
+                    && do { $value = sprintf( "%.5f", $value ); last QTYPE; };
+
+                ( $qt eq 'CHPSignalLogRatio' )
+                    && do { $value = sprintf( "%.1f", $value ); last QTYPE; };
+
+                ( $qt eq 'CHPSignalLogRatioLow' )
+                    && do { $value = sprintf( "%.1f", $value ); last QTYPE; };
+
+                ( $qt eq 'CHPSignalLogRatioHigh' )
+                    && do { $value = sprintf( "%.1f", $value ); last QTYPE; };
+
+                ( $qt eq 'CHPChange' )
+                    && do { $value = $change[$value] || q{null}; last QTYPE; };
+
+                ( $qt eq 'CHPChangePvalue' )
+                    && do { $value = sprintf( "%.5f", $value ); last QTYPE; };
+
+                ( $qt eq 'CHPAllele' )
+                    && do { $value = $allele[$value] || q{null}; last QTYPE; };
+
+                ( $qt eq 'CHPAllelePvalue' )
+                    && do { $value = sprintf( "%.6f", $value ); last QTYPE; };
+
+                ( $qt eq 'CHPAlleleRAS1' )
+                    && do { $value = sprintf( "%.4f", $value ); last QTYPE; };
+
+                ( $qt eq 'CHPAlleleRAS2' )
+                    && do { $value = sprintf( "%.4f", $value ); last QTYPE; };
+
+                ( $qt eq 'CHPAllelePvalueAA' )
+                    && do { $value = sprintf( "%.6f", $value ); last QTYPE; };
+
+                ( $qt eq 'CHPAllelePvalueAB' )
+                    && do { $value = sprintf( "%.6f", $value ); last QTYPE; };
+
+                ( $qt eq 'CHPAllelePvalueBB' )
+                    && do { $value = sprintf( "%.6f", $value ); last QTYPE; };
+
+                ( $qt eq 'CHPAllelePvalueNoCall' )
+                    && do { $value = sprintf( "%.6f", $value ); last QTYPE; };
+
+            }
+
+            # Safety net
+            unless ( defined($value) ) { $value = q{} }
+
+            push( @line_array, $value );
+
+        }
+
+        print $fh ( join( "\t", @line_array ), "\n" );
+
+    }
+
+    return;
+}
+
+sub get_ded {
+
+    my ( $self, $cdf, $chip_type ) = @_;
+
+    $cdf->isa('ArrayExpress::Datafile::Affymetrix::CDF')
+        or confess( 'Bad parameters passed to method' );
+    ref $chip_type and confess( 'Bad parameters passed to method' );
+
+    # If no chip type passed, try and figure it out on our own
+    $chip_type ||= $self->get_chip_type() || $cdf->get_chip_type();
+
+    # Basic sanity check here.
+    croak("Error: No chip type information available to CHP->get_ded()\n")
+        unless $chip_type;
+
+    # If the parse hasn't been run yet, do so.
+    unless ( scalar @{ $cdf->get_probeset_ids() } ) {
+	$cdf->parse();
+    }
+
+    my $data      = $self->get_data();
+    my $probesets = $cdf->get_probeset_ids();
+
+    my @composite_sequences;
+
+    for ( my $i = 0; $i <= $#{$data}; $i++ ) {
+        carp(     "WARNING: No CDF data for CHP unit "
+                . ( $i + 1 )
+                . ". Incorrect CDF file used?\n" )
+            unless $probesets->[$i];
+        push( @composite_sequences,
+            "Affymetrix:CompositeSequence:$chip_type:"
+                . ( $probesets->[$i] || q{} ) );
+    }
+
+    return \@composite_sequences;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv12.pm b/lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv12.pm
new file mode 100644
index 0000000..5d4aa0d
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv12.pm
@@ -0,0 +1,229 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: CHPv12.pm 1906 2008-01-23 10:05:42Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../../index.html">
+                <img src="../../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: CHPv12.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CHP::CHPv12 - CHPv12 data file
+parsing.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::CHP::CHPv12;
+
+ my $chp = ArrayExpress::Datafile::Affymetrix::CHP::CHPv12->new({
+     input => 'mydata.CHP',
+ });
+
+ $chp->parse();
+
+=head1 DESCRIPTION
+
+This module implements parsing and export of data from Affymetrix GDAC
+CHP (v12) files.
+
+Please see the L<ArrayExpress::Datafile::Affymetrix::Parser>
+documentation for methods common to all the Affymetrix file classes.
+
+=head1 METHODS
+
+Most methods are implemented in the superclass. Please see
+L<ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP> for details.
+
+=over 2
+
+=pod
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CHP::CHPv12;
+use base 'ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP';
+
+use strict;
+use warnings;
+use Readonly;
+use Carp;
+use Scalar::Util qw( openhandle );
+use IO::File;
+use Class::Std;
+
+use ArrayExpress::Curator::Common qw(
+    round
+);
+
+use ArrayExpress::Datafile::Binary qw(
+    get_integer
+    get_DWORD
+    get_unsigned_short
+    get_unsigned_char
+    get_signed_char
+    get_float
+    get_ascii
+    get_hexadecimal
+);
+
+###########################
+# CHPv12-specific methods #
+###########################
+
+# flag to reduce method call overhead.
+my $comparison_data_found;
+
+sub parse_expression_cell : RESTRICTED {
+
+    # NB quite a lot of stuff is being discarded here. This relieves
+    # memory overhead, but we may want to add some things back at some
+    # stage.
+
+    my ( $self, $fh, $cell ) = @_;
+
+    openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+    $cell->{'CHPPairs'}     = get_integer($fh);
+    $cell->{'CHPPairsUsed'} = get_integer($fh);
+
+    # N.B. CHP versions 8 and 12 are interchangable throughout
+    # this section, but the v8 float format defeated me, which
+    # is why version 8 gets such short shrift.
+    for ( 1 .. 5 ) { get_integer($fh); }    # Unused
+
+    $cell->{'CHPDetectionPvalue'} = round( get_float($fh), 5 );
+
+    get_float($fh);    # Unused
+
+    $cell->{'CHPSignal'}    = round( get_float($fh), 1 );
+    $cell->{'CHPDetection'} = get_integer($fh);
+    
+    #################################################################
+    # ***** N.B. *****
+    #
+    # Most of the rest of the parsing below is unused, but is left
+    # here for reference and because removing it would really screw
+    # up the main data value parsing
+    #################################################################
+
+    # Pairs section is not used
+    # Initialize the sub-array
+    my $pairs;
+    for ( my $pair = 0; $pair < $cell->{'CHPPairs'}; $pair++ ) {
+	$pairs->[$pair] = {};
+    }
+
+    foreach my $pair (@$pairs) {
+
+	get_float($fh);      # pair background
+	get_integer($fh);    # 2 = used, 0 = not # pair used
+	get_integer($fh);           # pair PM_Xcoord
+	get_integer($fh);           # pair PM_Ycoord
+	get_float($fh);             # pair PM_intensity
+	get_float($fh);             # pair PM_stdev
+	get_integer($fh);           # pair PM_pixels
+	get_signed_char($fh);       # pair PM_masked
+	get_signed_char($fh);       # pair PM_outlier
+	get_integer($fh);           # pair MM_Xcoord
+	get_integer($fh);           # pair MM_Ycoord
+	get_float($fh);             # pair MM_intensity
+	get_float($fh);             # pair MM_stdev
+	get_integer($fh);           # pair MM_pixels
+	get_signed_char($fh);       # pair MM_masked
+	get_signed_char($fh);       # pair MM_outlier
+    }
+
+    # This part below has now been tested.
+
+    my $comparison_exists = get_integer($fh);
+
+    if ($comparison_exists) {
+
+	# set the qtd for ExpressionStat data with comparison data
+	$self->set_headings( $self->get_affy_qtd_chp_comparison() )
+	    unless $comparison_data_found;
+	$comparison_data_found++;
+
+	$cell->{'CHPCommonPairs'} = get_integer($fh);
+
+	for ( 1 .. 3 ) { get_integer($fh); }    # Unused
+
+	$cell->{'CHPChange'} = get_integer($fh);
+
+	# This is not actually exported at the moment
+	$cell->{baseline_absent} = get_signed_char($fh);
+
+	get_signed_char($fh);    # Unused
+	get_integer($fh);        # Unused
+	get_integer($fh);        # Unused
+
+	my $signal_log_ratio_high_1000
+	    = get_integer($fh);    # divide by 1000 to get float value
+	$cell->{'CHPSignalLogRatioHigh'}
+	    = round( ( $signal_log_ratio_high_1000 / 1000 ), 1 );
+
+	get_integer($fh);          # Unused
+	get_integer($fh);          # Unused
+
+	my $signal_log_ratio_1000
+	    = get_integer($fh);    # divide by 1000 to get float value
+	$cell->{'CHPSignalLogRatio'}
+	    = round( ( $signal_log_ratio_1000 / 1000 ), 1 );
+
+	get_integer($fh);      # Unused
+
+	my $signal_log_ratio_low_1000
+	    = get_integer($fh);    # divide by 1000 to get float value
+	$cell->{'CHPSignalLogRatioLow'}
+	    = round( ( $signal_log_ratio_low_1000 / 1000 ), 1 );
+	
+	$cell->{'CHPChangePvalue'} = round( get_float($fh), 5 );
+	
+    }
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv13.pm b/lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv13.pm
new file mode 100644
index 0000000..3af2b3b
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv13.pm
@@ -0,0 +1,208 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: CHPv13.pm 1906 2008-01-23 10:05:42Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../../index.html">
+                <img src="../../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: CHPv13.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CHP::CHPv13 - CHPv13 data file
+parsing.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::CHP::CHPv13;
+
+ my $chp = ArrayExpress::Datafile::Affymetrix::CHP::CHPv13->new({
+     input => 'mydata.CHP',
+ });
+
+ $chp->parse();
+
+=head1 DESCRIPTION
+
+This module implements parsing and export of data from Affymetrix GDAC
+CHP (v12) files.
+
+Please see the L<ArrayExpress::Datafile::Affymetrix::Parser>
+documentation for methods common to all the Affymetrix file classes.
+
+=head1 METHODS
+
+Most methods are implemented in the superclass. Please see
+L<ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP> for details.
+
+=over 2
+
+=pod
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CHP::CHPv13;
+use base 'ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP';
+
+use strict;
+use warnings;
+use Readonly;
+use Carp;
+use Scalar::Util qw( openhandle );
+use IO::File;
+use Class::Std;
+
+use ArrayExpress::Curator::Common qw(
+    round
+);
+
+use ArrayExpress::Datafile::Binary qw(
+    get_integer
+    get_DWORD
+    get_unsigned_short
+    get_unsigned_char
+    get_signed_char
+    get_float
+    get_ascii
+    get_hexadecimal
+);
+
+###########################
+# CHPv13-specific methods #
+###########################
+
+# flag to reduce method call overhead
+my $comparison_data_found;
+
+sub parse_expression_cell : RESTRICTED {
+
+    # NB quite a lot of stuff is being discarded here. This relieves
+    # memory overhead, but we may want to add some things back at some
+    # stage.
+
+    my ( $self, $fh, $cell ) = @_;
+
+    openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+    $cell->{'CHPPairs'}     = get_integer($fh);
+    $cell->{'CHPPairsUsed'} = get_integer($fh);
+
+    # N.B. CHP versions 8 and 12 are interchangable throughout
+    # this section, but the v8 float format defeated me, which
+    # is why version 8 gets such short shrift.
+    get_integer($fh);    # cell num_pairs_used2
+
+    $cell->{'CHPDetectionPvalue'} = round( get_float($fh), 5 );
+
+    $cell->{'CHPSignal'}    = round( get_float($fh), 1 );
+    $cell->{'CHPDetection'} = get_integer($fh);
+    
+    #################################################################
+    # ***** N.B. *****
+    #
+    # Most of the rest of the parsing below is unused, but is left
+    # here for reference and because removing it would really screw
+    # up the main data value parsing
+    #################################################################
+
+    # Pairs section is not used
+    # Initialize the sub-array
+    my $pairs;
+    for ( my $pair = 0; $pair < $cell->{'CHPPairs'}; $pair++ ) {
+	$pairs->[$pair] = {};
+    }
+
+    foreach my $pair (@$pairs) {
+
+	get_float($fh);      # pair background
+	get_integer($fh);    # 2 = used, 0 = not # pair used
+
+	get_unsigned_short($fh);    # pair PM_Xcoord
+	get_unsigned_short($fh);    # pair PM_Ycoord
+	get_unsigned_short($fh);    # pair MM_Xcoord
+	get_unsigned_short($fh);    # pair MM_Ycoord
+    }
+
+    # This part below has now been tested.
+
+    my $comparison_exists = get_integer($fh);
+
+    if ($comparison_exists) {
+
+	# set the qtd for ExpressionStat data with comparison data
+	$self->set_headings( $self->get_affy_qtd_chp_comparison() )
+	    unless $comparison_data_found;
+	$comparison_data_found++;
+
+	$cell->{'CHPCommonPairs'} = get_integer($fh);
+	$cell->{'CHPChange'}      = get_integer($fh);
+
+	# This is not actually exported at the moment
+	$cell->{baseline_absent} = get_signed_char($fh);
+
+	my $signal_log_ratio_high_1000
+	    = get_integer($fh);    # divide by 1000 to get float value
+	$cell->{'CHPSignalLogRatioHigh'}
+	    = round( ( $signal_log_ratio_high_1000 / 1000 ), 1 );
+
+	get_integer($fh);          # Unused
+
+	my $signal_log_ratio_1000
+	    = get_integer($fh);    # divide by 1000 to get float value
+	$cell->{'CHPSignalLogRatio'}
+	    = round( ( $signal_log_ratio_1000 / 1000 ), 1 );
+
+	my $signal_log_ratio_low_1000
+	    = get_integer($fh);    # divide by 1000 to get float value
+	$cell->{'CHPSignalLogRatioLow'}
+	    = round( ( $signal_log_ratio_low_1000 / 1000 ), 1 );
+	
+	$cell->{'CHPChangePvalue'} = round( get_float($fh), 5 );
+	
+    }
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv8.pm b/lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv8.pm
new file mode 100644
index 0000000..08940a5
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv8.pm
@@ -0,0 +1,115 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: CHPv8.pm 1906 2008-01-23 10:05:42Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../../index.html">
+                <img src="../../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: CHPv8.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CHP::CHPv8 - CHPv8 data file
+parsing.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::CHP::CHPv8;
+
+ my $chp = ArrayExpress::Datafile::Affymetrix::CHP::CHPv8->new({
+     input => 'mydata.CHP',
+ });
+
+ $chp->parse_header();
+
+=head1 DESCRIPTION
+
+This module implements parsing and export of data from Affymetrix GDAC
+CHP (v8) files. Note that only the data file header can be parsed
+using this module.
+
+Please see the L<ArrayExpress::Datafile::Affymetrix::Parser>
+documentation for methods common to all the Affymetrix file classes.
+
+=head1 METHODS
+
+Most methods are implemented in the superclass. Please see
+L<ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP> for details.
+
+=over 2
+
+=pod
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CHP::CHPv8;
+use base 'ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP';
+
+use strict;
+use warnings;
+use Readonly;
+use Carp;
+use Class::Std;
+
+###########################
+# CHPv8-specific methods #
+###########################
+
+sub parse_chp : RESTRICTED {
+
+    # N.B. CHP versions 8 and 12 are very similar in many respects,
+    # but the v8 float format defeated me, which is why version 8 gets
+    # such short shrift.
+
+    # We can't parse CHPv8 files fully. See the GDAC_CHP superclass
+    # for CHPv8 header parsing.
+
+    my ( $self, $fh ) = @_;
+
+    # Caller has to trap this in an eval. We really don't like these
+    # files.
+    croak("Error: CHP file version 8 not fully supported.\n");
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CHP/GDAC_CHP.pm b/lib/ArrayExpress/Datafile/Affymetrix/CHP/GDAC_CHP.pm
new file mode 100644
index 0000000..2399827
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CHP/GDAC_CHP.pm
@@ -0,0 +1,379 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: GDAC_CHP.pm 1906 2008-01-23 10:05:42Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../../index.html">
+                <img src="../../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: GDAC_CHP.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP - GDAC CHP data file
+parsing.
+
+=head1 SYNOPSIS
+
+ use base qw( ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP );
+
+=head1 DESCRIPTION
+
+Abstract superclass for parsing and export of data from Affymetrix CHP
+files. For older GDAC CHP files this class also implements a file
+header parsing method, which can be used to determine the version of
+the file (e.g. v8, v12, v13).
+
+Please see the L<ArrayExpress::Datafile::Affymetrix::Parser>
+documentation for methods common to all the Affymetrix file classes.
+
+=head1 METHODS
+
+=over 2
+
+=item C<parse_header()>
+
+This method will take the C<input> attribute and parse only the header
+metadata.
+
+=item C<export($fh, $cdf)>
+
+This method takes a filehandle and prints the parsed expression data
+out to it. The method also requires a pre-parsed CDF object to be
+passed to it. The QuantitationTypeDimension and DesignElementDimension
+of the resulting matrix are given by the C<get_qtd> and C<get_ded>
+methods, respectively.
+
+=item C<get_ded($cdf, $chip_type)>
+
+This method takes a pre-parsed CDF object, and an optional string
+argument representing the chip type (e.g., "HG-U133A"). The method
+uses these arguments to generate a reference to an array of
+CompositeSequence identifiers, arranged in the order that the
+B<export> method outputs them (i.e., a DesignElementDimension). The
+chip type can be derived in a number of ways; if EXP files are
+available, for instance, the $exp->get_chip_type() method should
+return an appropriate string. If this argument is not supplied then
+the chip_type() methods for the CHP and CDF objects are inspected.
+
+=pod
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP;
+use base 'ArrayExpress::Datafile::Affymetrix::CHP';
+
+use strict;
+use warnings;
+
+use Readonly;
+use Carp;
+use Scalar::Util qw( openhandle );
+use IO::File;
+use Class::Std;
+
+use ArrayExpress::Curator::Common qw(
+    round
+);
+
+use ArrayExpress::Datafile::Binary qw(
+    get_integer
+    get_DWORD
+    get_unsigned_short
+    get_unsigned_char
+    get_signed_char
+    get_float
+    get_ascii
+    get_hexadecimal
+);
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    $self->set_required_magic(1701733703);
+
+    return;
+}
+
+#############################
+# GDAC CHP specific methods #
+#############################
+
+sub _parse_gdac_chp_params : PRIVATE {
+
+    my ( $self, $paramstr ) = @_;
+
+    ref $paramstr and confess( 'Bad parameters passed to method' );
+
+    my @params = split /\s+/, $paramstr;
+
+    my $parameter_hash;
+    foreach my $param (@params) {
+        my ( $name, $value ) = split /=/, $param;
+        $parameter_hash->{$name} = $value;
+    }
+
+    $self->add_parameters($parameter_hash);
+
+    return;
+}
+
+sub _parse_gdac_chp_stats : PRIVATE {
+
+    my ( $self, $statstr ) = @_;
+
+    ref $statstr and confess( 'Bad parameters passed to method' );
+
+    my @statlists = split /[\s]+/, $statstr;
+
+    my $stats_hash;
+    foreach my $statlist (@statlists) {
+        my ( $prefix, $valuestring ) = split /=/, $statlist;
+        my @stats = split /,/, $valuestring;
+        foreach my $stat (@stats) {
+            my ( $value, $name ) = reverse( split /:/, $stat );
+            my $fullkey = $name ? "$prefix $name" : $prefix;
+            $stats_hash->{$fullkey} = $value;
+        }
+    }
+
+    $self->add_stats($stats_hash);
+
+    return;
+}
+
+sub parse_chp_header : RESTRICTED {
+
+    my ( $self, $fh ) = @_;
+
+    openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+    # Set fh to binary parsing, and rewind the file
+    binmode( $fh, ":raw" );
+    sysseek( $fh, 0, 0 )
+        or croak("Error rewinding filehandle for input: $!\n");
+
+    # Read the data in as a series of arrays/hashes
+    my $label = get_ascii( $fh, 22 );
+    unless ( $label eq 'GeneChip Sequence File' ) {
+        croak("Error: unknown CHP file format: $label\n");
+    }
+
+    # Set the stardard qtd for ExpressionStat data.
+    $self->set_headings( $self->get_affy_qtd_chp() );
+
+    # Store this in a scalar var for speed of access.
+    my $version = get_integer($fh);
+
+    $self->set_version( $version );
+
+    if ( $version == 8 ) {
+
+	# Get what we can (version, algorithm, parameters, cell
+	# numbers and chip_type). We don't attempt to parse the actual data.
+	$self->set_algorithm( get_ascii( $fh, get_integer($fh) ) );
+	$self->_parse_gdac_chp_params( get_ascii( $fh, get_integer($fh) ) );
+
+	$self->set_num_columns( get_integer($fh) );
+	$self->set_num_rows( get_integer($fh) );
+	$self->set_num_cells( get_integer($fh) );    # Total
+
+	my $max_cell_no  = get_integer($fh);     # Deprecated
+	my $num_qc_cells = get_integer($fh);
+
+	# Seek to the chip_type record.
+	my $skipped = ( $max_cell_no + $self->get_num_cells() ) * 2 * 4;
+	sysseek($fh, $skipped, 1);   # FIXME find a way to die meaningfully here.
+
+	# header probe_array_type; NB contains a lot of junk.
+	my $probe_array_type = get_ascii( $fh, 256 );
+	$probe_array_type = ( split /[\n\r]+/, $probe_array_type )[0];
+	$probe_array_type =~ s{\A ([\w-]*) .*}{$1}xms;
+	$self->set_chip_type($probe_array_type);
+
+	# And that's all we can parse for now.
+	return;
+    }
+    elsif ( $version != 12 && $version != 13 ) {
+
+	# We have no idea what this might be.
+        croak("Fatal error: unrecognized CHP file version $version");
+    }
+
+    $self->set_algorithm( get_ascii( $fh, get_integer($fh) ) );
+
+    # Algorithm version (skip for version 8 CHP)
+    get_ascii( $fh, get_integer($fh) );
+
+    $self->_parse_gdac_chp_params( get_ascii( $fh, get_integer($fh) ) );
+
+    # Next line skipped for version 8 CHP
+    $self->_parse_gdac_chp_stats( get_ascii( $fh, get_integer($fh) ) );
+
+    $self->set_num_columns( get_integer($fh) );
+    $self->set_num_rows( get_integer($fh) );
+    $self->set_num_cells( get_integer($fh) );    # Total
+
+    return;
+}
+
+sub parse_chp : RESTRICTED {    # CONFIRMED BY DIFF
+
+    # NB quite a lot of stuff is being discarded here. This relieves
+    # memory overhead, but we may want to add some things back at some
+    # stage.
+
+    my ( $self, $fh ) = @_;
+
+    openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+    my $max_cell_no  = get_integer($fh);     # Deprecated
+    my $num_qc_cells = get_integer($fh);
+
+    if ( $self->get_num_cells > $max_cell_no ) {
+        croak(
+            "Error: Number of probe sets greater than the maximum possible!\n"
+        );
+    }
+
+    # Initialize the array. Makes subsequent steps safer & easier
+    # NB. when num_cells doesn't agree with num_rows*num_cells, it
+    # appears that num_cells is the one to trust. This may not be a
+    # good assumption in future.
+    my $data;
+    $data->[ $self->get_num_cells() - 1 ] = {};
+
+    # Cell numbers are deprecated
+    foreach my $cell (@$data) { get_integer($fh) }    # cell number
+    foreach my $cell (@$data) { get_integer($fh) }    # cell num_pairs2
+    for ( $self->get_num_cells() .. ( $max_cell_no - 1 ) ) {
+        get_integer($fh);                             # Unused
+    }
+
+    foreach my $cell (@$data) { $cell->{type} = get_integer($fh) }
+
+    for ( $self->get_num_cells() .. ( $max_cell_no - 1 ) ) {
+        get_integer($fh);
+    }    # Unused
+    foreach my $cell (@$data) { get_integer($fh) }    # cell num_probes
+
+    # header probe_array_type; NB contains a lot of junk.
+    my $probe_array_type = get_ascii( $fh, 256 );
+
+    $probe_array_type = ( split /[\n\r]+/, $probe_array_type )[0];
+    $probe_array_type =~ s{\A ([\w-]*) .*}{$1}xms;
+
+    $self->set_chip_type($probe_array_type);
+
+    # header parent_cel_filename; NB contains a lot of junk.
+    my $parent_cel = get_ascii( $fh, 256 );
+
+    # header programmatic_id
+    my $programmatic_id = get_ascii( $fh, get_integer($fh) );
+
+    foreach my $cell (@$data) {
+
+        if ( $cell->{type} == 3 ) {    # Expression data
+
+	    # This call will vary between versions 12 and 13 of the
+	    # CHP file format.
+	    $self->parse_expression_cell( $fh, $cell );
+
+        }
+        elsif ( $cell->{type} == 2 ) {     # Genotyping data
+
+            croak(
+                "Error: Genotyping data file parsing not yet implemented for CHP v3.\n"
+            );
+
+        }
+        else {
+
+            croak("Error: Cell type $cell->{type} not known.\n");
+
+        }
+    }
+
+    # Insert the data table into the object
+    $self->set_data($data);
+
+    # The next few sections are a work-in-progress
+    my $reseq_length = get_integer($fh);
+
+    if ($reseq_length) {
+
+        croak("Error: Resequencing data file parsing not yet implemented.\n");
+
+    }
+
+    my @qc_cells;
+    foreach my $qc_cell ( @qc_cells[ 0 .. ( $num_qc_cells - 1 ) ] ) {
+
+        my $num_probes = get_integer($fh);
+        get_integer($fh);    # qc_cell type
+
+        my @probes;
+        foreach my $probe ( @probes[ 0 .. ( $num_probes - 1 ) ] ) {
+
+            get_integer($fh);    # probe X_coord
+            get_integer($fh);    # probe Y_coord
+            get_float($fh);      # probe intensity
+            get_float($fh);      # probe stdev
+            get_integer($fh);    # probe pixels
+            get_float($fh);      # probe background
+
+        }
+    }
+
+    return;
+}
+
+sub parse_expression_cell : RESTRICTED {
+
+    my ( $self, $fh, $cell ) = @_;
+
+    confess ("Error: Stub method called in abstract superclass.");
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CHP/XDA_CHP.pm b/lib/ArrayExpress/Datafile/Affymetrix/CHP/XDA_CHP.pm
new file mode 100644
index 0000000..6f7da05
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CHP/XDA_CHP.pm
@@ -0,0 +1,335 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: XDA_CHP.pm 1906 2008-01-23 10:05:42Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../../index.html">
+                <img src="../../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: XDA_CHP.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CHP::XDA_CHP - XDA CHP data file
+parsing.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::CHP::XDA_CHP;
+
+ my $chp = ArrayExpress::Datafile::Affymetrix::CHP::XDA_CHP->new({
+     input => 'mydata.CHP',
+ });
+
+ $chp->parse();
+
+=head1 DESCRIPTION
+
+This module implements parsing and export of data from Affymetrix XDA
+CHP files.
+
+Please see the L<ArrayExpress::Datafile::Affymetrix::Parser>
+documentation for methods common to all the Affymetrix file classes.
+
+=head1 METHODS
+
+Most methods are implemented in the superclass. Please see
+L<ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP> for details.
+
+=over 2
+
+=pod
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CHP::XDA_CHP;
+use base 'ArrayExpress::Datafile::Affymetrix::CHP';
+
+use strict;
+use warnings;
+use Readonly;
+use Carp;
+use Scalar::Util qw( openhandle );
+use IO::File;
+use Class::Std;
+
+use ArrayExpress::Curator::Common qw(
+    round
+);
+
+use ArrayExpress::Datafile::Binary qw(
+    get_integer
+    get_DWORD
+    get_unsigned_short
+    get_unsigned_char
+    get_signed_char
+    get_float
+    get_ascii
+    get_hexadecimal
+);
+
+my %results_type : ATTR( :name<results_type>, :default<undef> );
+
+# The number of cells above which a genotyping chip is considered a
+# "100k" chip, rather than a "10k" chip. Currently the former start at
+# around 50k.
+Readonly my $LARGE_CHIP_THRESHOLD => 25000;
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    $self->set_required_magic(65);
+
+    return;
+}
+
+############################
+# XDA_CHP specific methods #
+############################
+
+# This sub parses CHP files. This has now been tested and found to work.
+
+sub parse_chp_header : RESTRICTED {
+
+    my ( $self, $fh ) = @_;
+
+    openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+    # Binary-mode filehandle, rewind the file.
+    binmode( $fh, ":raw" );
+    sysseek( $fh, 0, 0 )
+        or croak("Error rewinding filehandle for input: $!\n");
+
+    my $magic = get_integer($fh);
+    unless ( $magic == 65 ) {
+        croak("Error: Unrecognized CHP type: $magic\n");
+    }
+
+    # Set the stardard qtd for ExpressionStat data.
+    $self->set_headings( $self->get_affy_qtd_chp() );
+
+    $self->set_version( get_integer($fh) );
+    $self->set_num_columns( get_unsigned_short($fh) );
+    $self->set_num_rows( get_unsigned_short($fh) );
+    $self->set_num_cells( get_integer($fh) );    # Non-QC cells only
+    my $num_qc_cells = get_integer($fh);
+    my $results_type = get_integer($fh);
+
+    $self->set_results_type( $results_type );
+
+    # Fix QTD for Genotyping (SNP) data
+    if ( $results_type == 1 ) {
+	if ( $self->get_num_cells < $LARGE_CHIP_THRESHOLD ) {
+
+	    # 10k chip.
+	    $self->set_headings( $self->get_affy_qtd_chp_snp() );
+	}
+	else {
+
+	    # 100k chip
+	    $self->set_headings( $self->get_affy_qtd_chp_snp100() );
+	}
+    }
+
+    my $programmatic_id = get_ascii( $fh, get_integer($fh) );
+    my $cel_filename    = get_ascii( $fh, get_integer($fh) );
+    $self->set_chip_type( get_ascii( $fh, get_integer($fh) ) );
+    $self->set_algorithm( get_ascii( $fh, get_integer($fh) ) );
+    my $algorithm_version = get_ascii( $fh, get_integer($fh) );
+
+    # Parameters
+    my $num_params = get_integer($fh);
+    my $parameter_hash;
+    for ( 1 .. $num_params ) {
+
+        # Listed in the file as name, value
+        my $name  = get_ascii( $fh, get_integer($fh) );
+        my $value = get_ascii( $fh, get_integer($fh) );
+
+        # Remove stray whitespace from value
+        $value =~ s{\A \s* (.*?) \s* \z}{$1}xms;
+
+        $parameter_hash->{$name} = $value;
+    }
+    $self->add_parameters($parameter_hash);
+
+    # Summary stats
+    my $num_stats = get_integer($fh);  # not sure about this (not in the spec)
+    my $stats_hash;
+    for ( 1 .. $num_stats ) {
+
+        # Listed in the file as name, value
+        my $name  = get_ascii( $fh, get_integer($fh) );
+        my $value = get_ascii( $fh, get_integer($fh) );
+
+        # Remove stray whitespace from value
+        $value =~ s{\A \s* (.*?) \s* \z}{$1}xms;
+
+        if ( $value =~ m/,/ ) {        # Complex values may exist, e.g. Noise
+            foreach my $substat ( split /,/, $value ) {
+                my ( $subname, $subval ) = split /:/, $substat;
+                $stats_hash->{"$name $subname"} = $subval;
+            }
+        }
+        else {
+            $stats_hash->{$name} = $value;
+        }
+    }
+    $self->add_stats($stats_hash);
+
+    return;
+}
+
+sub parse_chp : RESTRICTED {
+
+    my ( $self, $fh ) = @_;
+
+    openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+    # Background zones
+    my $num_bkd_zones     = get_integer($fh);
+    my $bkd_smooth_factor = get_float($fh);
+    for ( 1 .. $num_bkd_zones ) {
+        my $bkd_zone;    # discarded at the moment
+        $bkd_zone->{'x'}     = get_float($fh);
+        $bkd_zone->{'y'}     = get_float($fh);
+        $bkd_zone->{'value'} = get_float($fh);
+    }
+
+    my $results_type = $self->get_results_type();
+
+    # This next is only present in expression results type files:
+    my $expression_analysis_type;
+    if ( $results_type == 0 ) {
+        $expression_analysis_type = get_unsigned_char($fh);
+        if (   $expression_analysis_type == 1
+            || $expression_analysis_type == 3 ) {
+
+            # Set the qtd for ExpressionStat data with comparison data
+            $self->set_headings( $self->get_affy_qtd_chp_comparison() );
+        }
+    }
+
+    my $data_size = get_integer($fh);    # The data record size in bytes
+
+    # Initialize the array. Makes subsequent steps safer & easier
+    my $data;
+    $data->[ $self->get_num_cells() - 1 ] = {};
+
+    foreach my $cell (@$data) {
+
+        # Expression results type files (N.B. this is the one we're mainly
+        # interested in.)
+        if ( $results_type == 0 ) {
+
+            $cell->{'CHPDetection'}       = get_unsigned_char($fh);
+            $cell->{'CHPDetectionPvalue'} = round( get_float($fh), 5 );
+            $cell->{'CHPSignal'}          = round( get_float($fh), 1 );
+            $cell->{'CHPPairs'}           = get_unsigned_short($fh);
+            $cell->{'CHPPairsUsed'}       = get_unsigned_short($fh);
+
+            if (   $expression_analysis_type == 1
+                || $expression_analysis_type == 3 ) {
+
+                $cell->{'CHPChange'}             = get_unsigned_char($fh);
+                $cell->{'CHPChangePvalue'}       = round( get_float($fh), 5 );
+                $cell->{'CHPSignalLogRatio'}     = round( get_float($fh), 1 );
+                $cell->{'CHPSignalLogRatioLow'}  = round( get_float($fh), 1 );
+                $cell->{'CHPSignalLogRatioHigh'} = round( get_float($fh), 1 );
+                $cell->{'CHPCommonPairs'}        = get_unsigned_short($fh);
+
+            }
+
+        }
+
+        # Genotyping results type files.
+        elsif ( $results_type == 1 ) {
+
+            $cell->{CHPAllele}       = get_unsigned_char($fh);
+            $cell->{CHPAllelePvalue} = get_float($fh);
+
+            # we distinguish between 10k and 100k arrays here:
+            if ( $self->get_num_cells() > $LARGE_CHIP_THRESHOLD ) {
+
+                # 100k arrays
+                $cell->{CHPAllelePvalueAA}     = get_float($fh);
+                $cell->{CHPAllelePvalueAB}     = get_float($fh);
+                $cell->{CHPAllelePvalueBB}     = get_float($fh);
+                $cell->{CHPAllelePvalueNoCall} = get_float($fh);
+
+            }
+            else {
+
+                # 10k arrays
+                $cell->{CHPAlleleRAS1} = get_float($fh);
+                $cell->{CHPAlleleRAS2} = get_float($fh);
+                get_float($fh);    # unused?
+                get_float($fh);    # unused?
+
+            }
+        }
+
+        # Resequencing results type files.
+        elsif ( $results_type == 2 ) {
+            my $sequence_length = get_integer($fh);
+            $cell->{sequence} = get_ascii( $fh, $sequence_length );
+
+            # This should work. 4 is the integer size.
+            $cell->{base_call_score}
+                = get_float( $fh, ( $data_size - $sequence_length - 4 ) );
+        }
+
+        # Universal results type files.
+        elsif ( $results_type == 3 ) {
+            $cell->{bkd_value} = get_float($fh);
+        }
+    }
+
+    # Insert the data into the object
+    $self->set_data($data);
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CHPFactory.pm b/lib/ArrayExpress/Datafile/Affymetrix/CHPFactory.pm
new file mode 100644
index 0000000..3bdbf37
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CHPFactory.pm
@@ -0,0 +1,159 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: CHPFactory.pm 1984 2008-03-03 17:47:18Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../index.html">
+                <img src="../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: CHPFactory.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CHPFactory - a factory class for generating
+Affymetrix CHP file parsing objects.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::CHPFactory;
+
+ my $fac = ArrayExpress::Datafile::Affymetrix::CHPFactory->new();
+
+ my $cel = $fac->make_parser( 'Data1.CHP' );
+
+ $cel->parse();
+
+ $cel->export($output_filehandle);
+
+=head1 DESCRIPTION
+
+This module is a factory class used in to create data file parsers for
+Affymetrix CHP files. Both old (GDAC) and new (GCOS/XDA) file formats
+can be parsed.
+
+=head1 METHODS
+
+=over 2
+
+=item new()
+
+The class constructor.
+
+=item make_parser($file)
+
+This method takes a filename argument and returns a parser object that
+can then be used to process the data file and return interesting
+values.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CHPFactory;
+use base 'ArrayExpress::Datafile::Affymetrix';
+
+use strict;
+use warnings;
+
+use Carp;
+use IO::File;
+use Class::Std;
+
+sub make_parser {
+
+    my ( $self, $input ) = @_;
+
+    my ( $magic, $fh ) = $self->read_magic( $input );
+
+    # FIXME it would be good to derive this automatically from what's
+    # available.
+    my %dispatch = (
+	1701733703 => 'ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP',
+	65         => 'ArrayExpress::Datafile::Affymetrix::CHP::XDA_CHP',
+	315        => 'ArrayExpress::Datafile::Affymetrix::Calvin::CHP',
+    );
+
+    my $class;
+    unless ( $class = $dispatch{ $magic } ) {
+	croak("Error: Unrecognized CHP file type: $magic");
+    }
+
+    # Return the appropriate parser object. Allow Perl::Critic to ignore this line:
+    ## no critic ProhibitStringyEval
+    eval "require $class";
+    ## use critic ProhibitStringyEval
+    if ( $@ ) {
+	confess("Error loading subclass $class: $@");
+    }
+    my $parser = $class->new({
+	filehandle => $fh,
+    });
+
+    # GDAC CHP files come in at least three different flavours.
+    if ( $parser->isa('ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP') ) {
+	$parser->parse_header();
+	my %gdac_dispatch = (
+	    8    => 'ArrayExpress::Datafile::Affymetrix::CHP::CHPv8',
+	    12   => 'ArrayExpress::Datafile::Affymetrix::CHP::CHPv12',
+	    13   => 'ArrayExpress::Datafile::Affymetrix::CHP::CHPv13',
+	);
+
+	my $class;
+	unless ( $class = $gdac_dispatch{ $parser->get_version() } ) {
+	    croak("Error: Unrecognized CHP file version: " . $parser->get_version());
+	}
+
+	# Return the appropriate parser object. Allow Perl::Critic to ignore this line:
+	## no critic ProhibitStringyEval
+	eval "require $class";
+	## use critic ProhibitStringyEval
+	if ( $@ ) {
+	    confess("Error loading subclass $class: $@");
+	}
+	$parser = $class->new({
+	    filehandle => $fh,
+	});
+    }
+
+    return $parser;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Binary.pm b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Binary.pm
new file mode 100644
index 0000000..2871b9c
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Binary.pm
@@ -0,0 +1,145 @@
+#!/usr/bin/env perl
+#
+# Module to provide basic binary file data parsing functions.used in
+# e.g. Affymetrix file parsing module(s)
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Binary.pm 1985 2008-03-03 18:32:27Z tfrayner $
+#
+
+package ArrayExpress::Datafile::Affymetrix::Calvin::Binary;
+
+use strict;
+use warnings;
+
+use Carp;
+use Readonly;
+
+use base 'Exporter';
+our @EXPORT_OK = qw(
+    get_unsigned_short
+    get_signed_short
+    get_unsigned_char
+    get_signed_char
+    get_unsigned_integer
+    get_signed_integer
+    get_wchar
+    get_wstring
+    get_string
+    get_datetime
+    get_locale
+    get_network_float
+);
+
+# This constant determines how floats are handled on big-endian systems.
+# NB. it is unclear at this stage whether 64-bit systems will work.
+Readonly my $IS_BIG_ENDIAN => unpack( "h*", pack( "s", 1 ) ) =~ /01/;
+
+#################################
+# Binary data parsing functions #
+#################################
+
+sub get_unsigned_short {
+    my $fh = shift;
+    my $num_bytes = sysread( $fh, my $value, 2 );
+    croak($!) unless defined($num_bytes);
+    return ( unpack "n*", $value );
+}
+
+sub get_signed_short {
+    my $fh = shift;
+    my $num_bytes = sysread( $fh, my $value, 2 );
+    croak($!) unless defined($num_bytes);
+    return ( unpack "n*", $value );
+}
+
+sub get_unsigned_char {
+    my $fh = shift;
+    my $num_bytes = sysread( $fh, my $value, 1 );
+    croak($!) unless defined($num_bytes);
+    return ( unpack "C*", $value );
+}
+
+sub get_signed_char {
+    my $fh = shift;
+    my $num_bytes = sysread( $fh, my $value, 1 );
+    croak($!) unless defined($num_bytes);
+    return ( unpack "c*", $value );
+}
+
+sub get_ascii {
+    my ( $fh, $length ) = @_;
+    my $num_bytes = sysread( $fh, my $value, $length );
+    croak($!) unless defined($num_bytes);
+    return ( unpack "a*", $value );
+}
+
+sub get_unsigned_integer {
+    my $fh = shift;
+    my $num_bytes = sysread( $fh, my $value, 4 );
+    croak($!) unless defined($num_bytes);
+    return ( unpack "N*", $value );
+}
+
+sub get_signed_integer {
+    my $fh = shift;
+    my $num_bytes = sysread( $fh, my $value, 4 );
+    croak($!) unless defined($num_bytes);
+    return ( unpack "N*", $value );
+}
+
+sub get_wchar {
+    my ( $fh, $length ) = @_;
+    my $num_bytes = sysread( $fh, my $value, $length );
+    croak($!) unless defined($num_bytes);
+    return $value;
+}
+
+sub get_wstring {
+    my ( $fh, $total ) = @_;
+    my $length = get_signed_integer( $fh ) * 2;
+    my $value  = get_wchar( $fh, $length );
+    if ( $total ) {
+	sysseek( $fh, $total - ( $length + 4 ), 1 );
+    }
+    my $ascii = pack "C*", unpack "n*", $value;
+    return $ascii;
+}
+
+sub get_string {
+    my ( $fh, $total ) = @_;
+    my $length = get_signed_integer( $fh );
+    my $value  = get_ascii( $fh, $length );
+    if ( $total ) {
+	sysseek( $fh, $total - ( $length + 4 ), 1 );
+    }
+    return $value;
+}
+
+sub get_datetime {
+    my $fh = shift;
+    return get_wstring( $fh );
+}
+
+sub get_locale {
+    my $fh = shift;
+    return get_wchar( $fh, 14 );
+}
+
+sub get_network_float {
+    my ( $fh ) = @_;
+    my $num_bytes = sysread( $fh, my $value, 4 );
+    croak($!) unless defined($num_bytes);
+
+    my $retval;
+    if ($IS_BIG_ENDIAN) {
+        $retval = unpack 'f*', $value;
+    }
+    else {
+        $retval = unpack "f*", pack "N*", unpack "V*", $value;
+    }
+    return sprintf( "%.5f", $retval );
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/Calvin/CEL.pm b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/CEL.pm
new file mode 100644
index 0000000..622e794
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/CEL.pm
@@ -0,0 +1,125 @@
+#!/usr/bin/env perl
+#
+# $Id: CEL.pm 1984 2008-03-03 17:47:18Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::Datafile::Affymetrix::Calvin::CEL;
+use base qw(ArrayExpress::Datafile::Affymetrix::Calvin::Facade);
+
+use Class::Std;
+use Carp;
+use Readonly;
+
+# The parent class knows how to populate these.
+my %num_columns   : ATTR( :name<num_columns>,   :default<undef> );
+my %num_rows      : ATTR( :name<num_rows>,      :default<undef> );
+
+Readonly my @DATA_SET_NAMES => qw(Intensity StdDev Pixel Outlier Mask);
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    # Sort out the QTD here.
+    my $data_group = $self->get_data_group(0);
+    my $num_sets   = $data_group->get_num_data_sets();
+
+    my @qtd = map { "Affymetrix:QuantitationType:$_" } @DATA_SET_NAMES;
+    $self->set_qtd( \@qtd );
+    $self->set_headings( \@DATA_SET_NAMES );
+
+    return;
+}
+
+sub export {
+
+    my ( $self, $output_fh ) = @_;
+
+    my $fh = $self->get_filehandle() or croak("Error: No filehandle.");
+
+    # The systell function doesn't exist, we use this instead.
+    my $init_pos = sysseek( $fh, 0, 1 );
+
+    my $data_group = $self->get_data_group(0);
+    my $num_sets   = $data_group->get_num_data_sets();
+
+    # One big data set in memory. Not a good way to do things, but the
+    # alternative is a lot of seeking in the file, which we're not
+    # really set up for. Why does Affy insist on breaking up the CEL
+    # data this way?
+    my ( @data, %outlier, %mask );
+    for ( my $i = 0; $i < $num_sets; $i++ ) {
+
+	my $data_set = $data_group->get_data_set( $i );
+	my $set_name = $data_set->get_name();
+	my $readers  = $data_set->get_data_readers();
+
+	sysseek( $fh, $data_set->get_data_table_start(), 0 )
+	    or croak("Error seeking in filehandle: $!");
+
+	# N.B. the following assumes that Intensity, StdDev and Pixel
+	# data sets all have the same number and order of design
+	# elements.
+	for ( 1..$data_set->get_num_data_rows() ) {
+
+	    if ( $set_name eq 'Outlier' ) {
+		my ( $x, $y ) = map { $_->() } @$readers;
+		$outlier{"$x,$y"}++;
+	    }
+	    elsif ( $set_name eq 'Mask' ) {
+		my ( $x, $y ) = map { $_->() } @$readers;
+		$mask{"$x,$y"}++ if ( defined $x && defined $y );
+	    }
+	    else {
+
+		# There should only be one reader per set here, but if
+		# not for some reason the values are joined with '|'.
+		my $row = join("|", map { $_->() } @$readers);
+		push @{ $data[$i] }, $row;
+	    }
+	}
+    }
+
+    # Y is rows, X is columns.
+    for ( my $y = 0; $y < $self->get_num_rows(); $y++ ) {
+        for ( my $x = 0; $x < $self->get_num_columns(); $x++ ) {
+	    my @values = map { shift( @{ $data[$_] } ) } qw( 0 1 2 );
+	    push @values, $outlier{ "$x,$y" } ? 'true' : 'false';
+	    push @values, $mask   { "$x,$y" } ? 'true' : 'false';
+	    print $output_fh (join("\t", @values), "\n");
+        }
+    }
+
+    # Reset the filehandle.
+    sysseek( $fh, $init_pos, 0 )
+	or croak("Error resetting filehandle: $!");
+
+    return;
+}
+
+sub generate_ded : RESTRICTED {
+
+    my ( $self ) = @_;
+
+    my $chip_type = $self->get_chip_type()
+	or croak("Error: Chip type is not known.");
+
+    my @ded;
+
+    # Y is rows, X is columns.
+    for ( my $y = 0; $y < $self->get_num_rows(); $y++ ) {
+        for ( my $x = 0; $x < $self->get_num_columns(); $x++ ) {
+	    push @ded, sprintf(
+		"Affymetrix:Feature:$chip_type:Probe(%d,%d)", $x, $y,
+	    );
+        }
+    }
+
+    $self->set_ded( \@ded );
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/Calvin/CHP.pm b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/CHP.pm
new file mode 100644
index 0000000..2ce3c04
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/CHP.pm
@@ -0,0 +1,83 @@
+#!/usr/bin/env perl
+#
+# $Id: CHP.pm 1982 2008-02-28 22:48:40Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::Datafile::Affymetrix::Calvin::CHP;
+use base qw(ArrayExpress::Datafile::Affymetrix::Calvin::Facade);
+
+use Class::Std;
+use Carp;
+
+sub export {
+
+    my ( $self, $output_fh, $cdf ) = @_;
+
+    my $data_set  = $self->get_data_set();
+    my $data_type = $self->get_data_header()->get_data_type();
+
+    # MAS5 results incorporates a value mapping. FIXME how to fall
+    # back to 'No Call'?
+    if ( $data_type eq 'affymetrix-expression-probeset-analysis' ) {
+	$data_set->set_value_mapping(
+	    [undef,{
+		0 => 'Present',
+		1 => 'Marginal',
+		2 => 'Absent',
+	    },undef,undef,undef,undef]
+	);
+    }
+
+    $data_set->export( $output_fh );
+
+    return;
+}
+
+sub generate_ded : RESTRICTED {
+
+    my ( $self ) = @_;
+
+    my $data_set  = $self->get_data_set();
+    my $chip_type = $self->get_chip_type()
+	or croak("Error: Chip type is not known.");
+
+    my $fh  = $data_set->get_filehandle() or croak("Error: No filehandle.");
+    my $pos = $data_set->get_data_table_start()
+	or croak("Error: No data table start position.");
+
+    # The systell function doesn't exist, we use this instead.
+    my $init_pos = sysseek( $fh, 0, 1 );
+
+    # Seek to data_table_start, create array of column readers,
+    # and write the return values for each row to $output_fh.
+    sysseek( $fh, $pos, 0 )
+	or croak("Error resetting filehandle: $!");
+
+    my @ded;
+    my $columns  = $data_set->get_data_columns();
+    my $id_col   = $columns->[0];
+    my $reader   = $id_col->get_reader();
+    my $size     = $id_col->get_size();
+    my $other    = 0;
+    my $num_cols = scalar( @{ $columns } );
+    for ( my $i = 1; $i < $num_cols; $i++ ) {
+	$other += $columns->[$i]->get_size();
+    }
+    for ( 1..$data_set->get_num_data_rows() ) {
+	my $name = $reader->( $fh, $size );
+	push @ded, "Affymetrix:CompositeSequence:$chip_type:$name";
+	sysseek( $fh, $other, 1 );
+    }
+
+    $self->set_ded( \@ded );
+
+    # Reset the filehandle.
+    sysseek( $fh, $init_pos, 0 )
+	or croak("Error resetting filehandle: $!");
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Component.pm b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Component.pm
new file mode 100644
index 0000000..e282564
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Component.pm
@@ -0,0 +1,105 @@
+#!/usr/bin/env perl
+#
+# $Id: Component.pm 1984 2008-03-03 17:47:18Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::Datafile::Affymetrix::Calvin::Component;
+
+use Class::Std;
+use Carp;
+use Readonly;
+
+require ArrayExpress::Datafile::Affymetrix::Calvin::Parameter;
+
+use ArrayExpress::Datafile::Affymetrix::Calvin::Binary qw(
+    get_string
+    get_wstring
+);
+
+my %position   : ATTR( :name<position>,   :default<undef> );
+my %filehandle : ATTR( :name<filehandle>, :default<undef> );
+my %parameters : ATTR( :name<parameters>, :default<[]>    );
+
+# This constant determines how floats are handled on big-endian systems.
+# NB. it is unclear at this stage whether 64-bit systems will work.
+Readonly my $IS_BIG_ENDIAN => unpack( "h*", pack( "s", 1 ) ) =~ /01/;
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    unless ( $self->get_position() ) {
+	croak("Error: No file position given.\n");
+    }
+    unless ( $self->get_filehandle() ) {
+	croak("Error: No filehandle given.\n");
+    }
+
+    return;
+}
+
+sub parse_parameter : RESTRICTED {
+
+    # This method _does_ have an effect on filehandle position.
+    my ( $self, $fh ) = @_;
+
+    my $pname  = get_wstring( $fh );
+    my $pvalue = get_string( $fh );
+    my $ptype  = get_wstring( $fh );
+
+    my %mime_map = (
+	'text/x-calvin-integer-8'           => 'c*',
+	'text/x-calvin-unsigned-integer-8'  => 'C*',
+	'text/x-calvin-integer-16'          => 's*',
+	'text/x-calvin-unsigned-integer-16' => 'n*',
+	'text/x-calvin-integer-32'          => 'N*',
+	'text/x-calvin-unsigned-integer-32' => 'N*',
+	'text/plain'                        => 'a*',
+	'text/ascii'                        => 'a*',
+    );
+
+    my $conv_val;
+    if ( $ptype eq 'text/x-calvin-float' ) {
+	if ($IS_BIG_ENDIAN) {
+	    $conv_val = unpack 'f*', $pvalue;
+	}
+	else {
+	    $conv_val = unpack "f*", pack "N*", unpack "V*", $pvalue;
+	}
+    }
+    elsif (my $packtype = $mime_map{ $ptype }) {
+	$conv_val = (unpack( $packtype, $pvalue ))[0];
+    }
+    else {
+	croak("Unrecognized MIME type: $ptype");
+    }
+
+    if ( $ptype eq 'text/plain' ) {
+	$conv_val = pack "C*", unpack "n*", $conv_val;
+	$conv_val =~ s/\x{0}* \z//xms;
+    }
+    elsif ( $ptype eq 'text/x-calvin-float' ) {
+	$conv_val = sprintf("%.5f", $conv_val);
+    }
+
+    my $param = ArrayExpress::Datafile::Affymetrix::Calvin::Parameter->new({
+	name  => $pname,
+	value => $conv_val,
+	type  => $ptype,
+    });
+
+    return $param;
+}
+
+sub add_parameters : RESTRICTED {
+
+    my ( $self, @params ) = @_;
+
+    push @{ $parameters{ ident $self } }, @params;
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataColumn.pm b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataColumn.pm
new file mode 100644
index 0000000..6fb6e05
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataColumn.pm
@@ -0,0 +1,68 @@
+#!/usr/bin/env perl
+#
+# $Id: DataColumn.pm 1973 2008-02-27 18:10:51Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::Datafile::Affymetrix::Calvin::DataColumn;
+
+# Simple class to store column definitions.
+
+use Class::Std;
+use Carp;
+
+use ArrayExpress::Datafile::Affymetrix::Calvin::Binary qw(
+    get_signed_char
+    get_unsigned_char
+    get_signed_short
+    get_unsigned_short
+    get_signed_integer
+    get_unsigned_integer
+    get_network_float
+    get_string
+    get_wstring
+);
+
+my %name   : ATTR( :name<name>,    :default<undef> );
+my %type   : ATTR( :name<type>,    :default<undef> );
+my %size   : ATTR( :name<size>,    :default<undef> );
+my %reader : ATTR( :name<reader>,  :default<undef> );
+
+# FIXME some of these may need sprintf-style reformatting.
+my %readermap = (
+    0 => \&get_signed_char,
+    1 => \&get_unsigned_char,
+    2 => \&get_signed_short,
+    3 => \&get_unsigned_short,
+    4 => \&get_signed_integer,
+    5 => \&get_unsigned_integer,
+    6 => \&get_network_float,
+    7 => \&get_string,
+    8 => \&get_wstring,
+);
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    unless ( $self->get_name() ) {
+	croak("Error: Column name not set.");
+    }
+
+    if ( my $type = $self->get_type() ) {
+	if ( my $coderef = $readermap{$type} ) {
+	    $self->set_reader( $coderef );
+	}
+	else {
+	    croak("Error: Unrecognized column type $type.");
+	}
+    }
+    else {
+	croak("Error: Column header type not set.");
+    }
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataGroup.pm b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataGroup.pm
new file mode 100644
index 0000000..a9c4968
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataGroup.pm
@@ -0,0 +1,105 @@
+#!/usr/bin/env perl
+#
+# $Id: DataGroup.pm 1973 2008-02-27 18:10:51Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::Datafile::Affymetrix::Calvin::DataGroup;
+use base qw(ArrayExpress::Datafile::Affymetrix::Calvin::Component);
+
+use Class::Std;
+use Carp;
+
+require ArrayExpress::Datafile::Affymetrix::Calvin::DataSet;
+
+use ArrayExpress::Datafile::Affymetrix::Calvin::Binary qw(
+    get_signed_integer
+    get_unsigned_integer
+    get_wstring
+);
+
+my %next_group_position : ATTR( :name<next_group_position>, :default<undef> );
+my %name                : ATTR( :name<name>,                :default<undef> );
+my %num_data_sets       : ATTR( :name<num_data_sets>,       :default<undef> );
+my %data_set            : ATTR( :set<data_set>,             :default<[]>    );
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    my $fh  = $self->get_filehandle() or croak("Error: No filehandle.");
+    my $pos = $self->get_position()   or croak("Error: No file position.");
+
+    # The systell function doesn't exist, we use this instead.
+    my $init_pos = sysseek( $fh, 0, 1 );
+
+    # File header is at the beginning of the file (no surprises there
+    # then).
+    sysseek( $fh, $pos, 0 )
+	or croak("Error resetting filehandle: $!");
+
+    my $next_pos = get_unsigned_integer( $fh );
+    $self->set_next_group_position( $next_pos ) if $next_pos;
+
+    # We recurse into data set metadata here.
+    my $first_set_pos   = get_unsigned_integer( $fh );
+
+    $self->set_num_data_sets( get_signed_integer( $fh ) );
+    $self->set_name( get_wstring( $fh ) );
+
+    my $first_set = ArrayExpress::Datafile::Affymetrix::Calvin::DataSet->new({
+	filehandle => $fh,
+	position   => $first_set_pos,
+    });
+
+    $data_set{ident $self}[0] = $first_set;
+
+    $self->populate_data_sets( $first_set, $self->get_num_data_sets() - 1 );
+
+    # Reset the filehandle.
+    sysseek( $fh, $init_pos, 0 )
+	or croak("Error resetting filehandle: $!");
+
+    return;
+}
+
+sub populate_data_sets : PRIVATE {
+
+    my ( $self, $set, $num_data_sets ) = @_;
+
+    # Recursion end point.
+    return unless $num_data_sets;
+
+    $set ||= $self->get_data_set( 0 )
+	or croak("Error: Initial data set not created.");
+
+    my $next_pos = $set->get_next_set_position()
+	or croak("Error: Data set gives no position for next set.");
+
+    my $next_set = ArrayExpress::Datafile::Affymetrix::Calvin::DataSet->new({
+	filehandle => $self->get_filehandle(),
+	position   => $next_pos,
+    });
+    push @{ $data_set{ ident $self } }, $next_set;
+
+    # We need to recurse across sets here until we run out of data
+    # sets.
+    $self->populate_data_sets( $next_set, $num_data_sets - 1 );
+
+    return;
+}
+
+sub get_data_set {
+
+    my ( $self, $num ) = @_;
+
+    # These should all have been created in START.
+    unless ( defined $data_set{ ident $self }[ $num ] ) {
+	croak("Error: No data set found for number $num");
+    }
+
+    return $data_set{ ident $self }[ $num ];
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataHeader.pm b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataHeader.pm
new file mode 100644
index 0000000..4f1ca86
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataHeader.pm
@@ -0,0 +1,80 @@
+#!/usr/bin/env perl
+#
+# $Id: DataHeader.pm 1973 2008-02-27 18:10:51Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::Datafile::Affymetrix::Calvin::DataHeader;
+use base qw(ArrayExpress::Datafile::Affymetrix::Calvin::Component);
+
+use Class::Std;
+use Carp;
+
+use ArrayExpress::Datafile::Affymetrix::Calvin::Binary qw(
+    get_signed_integer
+    get_datetime
+    get_locale
+    get_string
+);
+
+my %file_identifier : ATTR( :name<file_identifier>, :default<undef> );
+my %data_type       : ATTR( :name<data_type>,       :default<undef> );
+my %creation_date   : ATTR( :name<creation_date>,   :default<undef> );
+my %locale_info     : ATTR( :name<locale_info>,     :default<undef> );
+my %parent_headers  : ATTR( :name<parent_headers>,  :default<[]>    );
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    my $fh  = $self->get_filehandle() or croak("Error: No filehandle.");
+    my $pos = $self->get_position()   or croak("Error: No file position.");
+
+    # The systell function doesn't exist, we use this instead.
+    my $init_pos = sysseek( $fh, 0, 1 );
+
+    # In principle this is probably unnecessary, but we do it anyway
+    # just to be on the safe side.
+    sysseek( $fh, $pos, 0 )
+	or croak("Error resetting filehandle: $!");
+
+    $self->set_data_type( get_string( $fh ) );
+    $self->set_file_identifier( get_string( $fh ) );
+    $self->set_creation_date( get_datetime( $fh ) );
+    $self->set_locale_info( get_locale( $fh ) );
+
+    my $num_parameters = get_signed_integer( $fh );
+    for ( 1..$num_parameters ) {
+	my $param = $self->parse_parameter( $fh );
+	$self->add_parameters( $param );
+    }
+
+    # Recurse into the data headers, populating a nested hashref.
+    my $num_parents = get_signed_integer( $fh );
+    foreach my $num ( 1..$num_parents ) {
+	my $current_pos = sysseek( $fh, 0, 1 );
+	my $parent = __PACKAGE__->new({
+	    filehandle => $fh,
+	    position   => $current_pos,
+	});
+	$self->add_parent_headers( $parent );
+    }
+
+    # Reset the filehandle.
+    sysseek( $fh, $init_pos, 0 )
+	or croak("Error resetting filehandle: $!");    
+
+    return;
+}
+
+sub add_parent_headers : PRIVATE {
+
+    my ( $self, @parents ) = @_;
+
+    push @{ $parent_headers{ ident $self } }, @parents;
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataSet.pm b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataSet.pm
new file mode 100644
index 0000000..d6a6f9c
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataSet.pm
@@ -0,0 +1,143 @@
+#!/usr/bin/env perl
+#
+# $Id: DataSet.pm 1984 2008-03-03 17:47:18Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::Datafile::Affymetrix::Calvin::DataSet;
+use base qw(ArrayExpress::Datafile::Affymetrix::Calvin::Component);
+
+use Class::Std;
+use Carp;
+
+require ArrayExpress::Datafile::Affymetrix::Calvin::DataColumn;
+
+use ArrayExpress::Datafile::Affymetrix::Calvin::Binary qw(
+    get_signed_char
+    get_signed_integer
+    get_unsigned_integer
+    get_wstring
+);
+
+my %next_set_position   : ATTR( :name<next_set_position>,   :default<undef> );
+my %name                : ATTR( :name<name>,                :default<undef> );
+my %num_data_rows       : ATTR( :name<num_data_rows>,       :default<undef> );
+my %data_table_start    : ATTR( :name<data_table_start>,    :default<undef> );
+my %data_columns        : ATTR( :name<data_columns>,        :default<[]>    );
+my %value_mapping       : ATTR( :name<value_mapping>,       :default<[]>    );
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    my $fh  = $self->get_filehandle() or croak("Error: No filehandle.");
+    my $pos = $self->get_position()   or croak("Error: No file position.");
+
+    # The systell function doesn't exist, we use this instead.
+    my $init_pos = sysseek( $fh, 0, 1 );
+
+    sysseek( $fh, $pos, 0 )
+	or croak("Error resetting filehandle: $!");
+
+    $self->set_data_table_start( get_unsigned_integer( $fh ) );
+    $self->set_next_set_position( get_unsigned_integer( $fh ) );
+    $self->set_name( get_wstring( $fh ) );
+
+    my $num_parameters = get_signed_integer( $fh );
+    for ( 1..$num_parameters ) {
+	my $param = $self->parse_parameter( $fh );
+	$self->add_parameters( $param );
+    }
+
+    my $num_columns = get_unsigned_integer( $fh );
+
+    for ( 1..$num_columns ) {
+	my $cname = get_wstring( $fh );
+	my $ctype = get_signed_char( $fh );
+	my $csize = get_signed_integer( $fh );
+	my $column = ArrayExpress::Datafile::Affymetrix::Calvin::DataColumn->new({
+	    name => $cname,
+	    type => $ctype,
+	    size => $csize,
+	});
+	$self->add_data_columns( $column );
+    }
+
+    $self->set_num_data_rows( get_unsigned_integer( $fh ) );
+
+    # Reset the filehandle.
+    sysseek( $fh, $init_pos, 0 )
+	or croak("Error resetting filehandle: $!");
+
+    return;
+}
+
+sub export {
+
+    my ( $self, $output_fh ) = @_;
+
+    my $fh  = $self->get_filehandle() or croak("Error: No filehandle.");
+    my $pos = $self->get_data_table_start()
+	or croak("Error: No data table start position.");
+
+    # The systell function doesn't exist, we use this instead.
+    my $init_pos = sysseek( $fh, 0, 1 );
+
+    # Seek to data_table_start, create array of column readers,
+    # and write the return values for each row to $output_fh.
+    sysseek( $fh, $pos, 0 )
+	or croak("Error resetting filehandle: $!");
+
+    # Our column readers are created as an array of closures.
+    my $readers = $self->get_data_readers();
+    my $mappers = $self->get_value_mapping();
+
+    # Read the values in, and use any (optional) mappings to modify
+    # the output data on the fly.
+    for ( 1..$self->get_num_data_rows() ) {
+	my @row_values;
+	foreach my $i ( 0..$#$readers ) {
+	    my $val = $readers->[$i]->();
+	    if ( my $map = $mappers->[$i] ) {
+		$val = $map->{$val};
+	    }
+	    push @row_values, $val;
+	}
+	print $output_fh (join("\t", @row_values), "\n");
+    }
+
+    # Reset the filehandle.
+    sysseek( $fh, $init_pos, 0 )
+	or croak("Error resetting filehandle: $!");
+
+    return;
+}
+
+sub get_data_readers {
+
+    my ( $self ) = @_;
+
+    my $fh = $self->get_filehandle() or croak("Error: No filehandle.");
+
+    # Our column readers are created as an array of closures. Note
+    # that before you use these you need to sysseek to
+    # $self->get_data_table_start().
+    my @readers = map {
+	my $x = $_;
+	sub { $x->get_reader()->( $fh, $x->get_size() ) };
+    } @{ $self->get_data_columns() };
+
+    return \@readers;
+}
+
+sub add_data_columns : PRIVATE {
+
+    my ( $self, @columns ) = @_;
+
+    push @{ $data_columns{ ident $self } }, @columns;
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Facade.pm b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Facade.pm
new file mode 100644
index 0000000..7847c51
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Facade.pm
@@ -0,0 +1,127 @@
+#!/usr/bin/env perl
+#
+# $Id: Facade.pm 1984 2008-03-03 17:47:18Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::Datafile::Affymetrix::Calvin::Facade;
+use base qw(ArrayExpress::Datafile::Affymetrix::Calvin::Generic);
+
+use Class::Std;
+use Carp;
+
+my %data_set    : ATTR( :name<data_set>,    :default<undef> );
+my %chip_type   : ATTR( :name<chip_type>,   :default<undef> );
+my %algorithm   : ATTR( :name<algorithm>,   :default<undef> );
+my %parameters  : ATTR( :get<parameters>,   :default<{}> );
+my %stats       : ATTR( :get<stats>,        :default<{}> );
+my %qtd         : ATTR( :name<qtd>,         :default<[]> );
+my %ded         : ATTR( :set<ded>,          :default<[]> );
+my %headings    : ATTR( :name<headings>,    :default<[]> );
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    my $group    = $self->get_data_group(0);
+    my $data_set = $group->get_data_set(0);
+
+    $self->set_data_set( $data_set );
+
+    # Num_columns and num_rows are only really available to the CEL
+    # subclass.
+    my %param2attr = (
+	'affymetrix-algorithm-name' => 'set_algorithm',
+	'affymetrix-array-type'     => 'set_chip_type',
+	'affymetrix-cel-cols'       => 'set_num_columns',
+	'affymetrix-cel-rows'       => 'set_num_rows',
+    );
+
+    my $params = $self->get_data_header()->get_parameters();
+    my $pname;
+    foreach my $param ( @$params ) {
+	my $setter = $param2attr{ $param->get_name() };
+	if ( $setter && $self->can($setter) ) {
+	    $self->$setter( $param->get_value() );
+	}
+	elsif ( ( $pname )
+	     = ( $param->get_name() =~ m/affymetrix-algorithm-param-(.*)/xms ) ) {
+	    $self->add_parameter( $pname, $param->get_value() );
+	}
+	elsif ( ( $pname )
+	     = ( $param->get_name() =~ m/affymetrix-chipsummary-(.*)/xms ) ) {
+	    $self->add_stat( $pname, $param->get_value() );
+	}
+    }
+
+    my $columns = $data_set->get_data_columns();
+    my ( @headings, @qts );
+    foreach my $col ( @$columns ) {
+	push @headings, $col->get_name();
+	push @qts, 'Affymetrix:QuantitationType:' . $col->get_name();
+    }
+
+    $self->set_headings( \@headings );
+    $self->set_qtd( \@qts );
+}
+
+sub add_parameter : RESTRICTED {
+
+    my ( $self, $key, $value ) = @_;
+
+    $parameters{ ident $self }{ $key } = $value;
+
+    return;
+}
+
+sub add_stat : RESTRICTED {
+
+    my ( $self, $key, $value ) = @_;
+
+    $stats{ ident $self }{ $key } = $value;
+
+    return;
+}
+
+sub export {
+
+    my ( $self, $output_fh, $cdf ) = @_;
+
+    my $data_set  = $self->get_data_set();
+    my $data_type = $self->get_data_header()->get_data_type();
+
+    $data_set->export( $output_fh );
+
+    return;
+}
+
+sub parse {
+    # Dummy method - the Calvin parser does everything on
+    # instantiation, but this is part of the published API.
+}
+
+sub parse_header {
+    # Dummy method - the Calvin parser does everything on
+    # instantiation, but this is part of the published API.
+}
+
+sub generate_ded : RESTRICTED {
+
+    my ( $self ) = @_;
+
+    croak("Error: Stub method called in abstract superclass.");
+}
+
+sub get_ded {
+
+    my ( $self, $cdf, $chip_type ) = @_;
+
+    unless ( scalar @{ $ded{ ident $self } } ) {
+	$self->generate_ded();
+    }
+
+    return $ded{ ident $self };
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Generic.pm b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Generic.pm
new file mode 100644
index 0000000..ada77f5
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Generic.pm
@@ -0,0 +1,128 @@
+#!/usr/bin/env perl
+#
+# $Id: Generic.pm 1984 2008-03-03 17:47:18Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::Datafile::Affymetrix::Calvin::Generic;
+
+use Class::Std;
+use Carp;
+
+require ArrayExpress::Datafile::Affymetrix::Calvin::DataGroup;
+require ArrayExpress::Datafile::Affymetrix::Calvin::DataHeader;
+
+use ArrayExpress::Datafile::Affymetrix::Calvin::Binary qw(
+    get_signed_integer
+    get_unsigned_char
+    get_unsigned_integer
+);
+
+my %filename   : ATTR( :get<filename>,    :init_arg<filename>,   :default<undef> );
+my %filehandle : ATTR( :set<filehandle>,  :init_arg<filehandle>, :default<undef> );
+
+my %magic            : ATTR( :name<magic>,            :default<undef> );
+my %version          : ATTR( :name<version>,          :default<undef> );
+my %num_data_groups  : ATTR( :name<num_data_groups>,  :default<undef> );
+my %data_group       : ATTR( :set<data_group>,        :default<[]>    );
+my %data_header      : ATTR( :name<data_header>,      :default<undef>  );
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    unless ( $filename{ ident $self } || $filehandle{ ident $self } ) {
+	croak("Error: no filename or filehandle attribute set.\n");
+    }
+
+    my $fh = $self->get_filehandle();
+
+    # The systell function doesn't exist, we use this instead.
+    my $init_pos = sysseek( $fh, 0, 1 );
+
+    # File header is at the beginning of the file (no surprises there
+    # then).
+    sysseek( $fh, 0, 0 )
+	or croak("Error resetting filehandle: $!");
+
+    my $magic = get_unsigned_char( $fh );
+    unless( $magic == 59 ) {
+	croak("Error: Unrecognized file magic number: $magic");
+    }
+    $magic{ ident $self } = $magic;
+
+    $version{ ident $self }         = get_unsigned_char( $fh );
+    $num_data_groups{ ident $self } = get_signed_integer( $fh );
+    my $first_group_pos = get_unsigned_integer( $fh );
+    my $first_group = ArrayExpress::Datafile::Affymetrix::Calvin::DataGroup->new({
+	filehandle => $fh,
+	position   => $first_group_pos,
+    });
+    $data_group{ ident $self }[0] = $first_group;
+
+    # This recurses through the file to set the positions of all the
+    # data groups.
+    $self->populate_data_groups($first_group, $self->get_num_data_groups() - 1);
+
+    # This iterates and recurses over the data headers.
+    my $data_header = ArrayExpress::Datafile::Affymetrix::Calvin::DataHeader->new({
+	filehandle => $fh,
+	position   => 10,
+    });
+
+    $self->set_data_header( $data_header );
+
+    # Reset the filehandle.
+    sysseek( $fh, $init_pos, 0 )
+	or croak("Error resetting filehandle: $!");
+
+    return;
+}
+
+sub get_filehandle : RESTRICTED {
+
+    my ( $self ) = @_;
+
+    unless ( $filehandle{ ident $self } ) {
+	open( my $fh, '<', $self->get_filename() )
+	    or croak("Error: Unable to open input file: $!");
+	$filehandle{ ident $self } = $fh;
+    }
+
+    return $filehandle{ ident $self };
+}
+
+sub get_data_group {
+
+    my ( $self, $num ) = @_;
+
+    # These should all have been created in START.
+    unless ( defined $data_group{ ident $self }[ $num ] ) {
+	croak("Error: No data group found for number $num");
+    }
+
+    return $data_group{ ident $self }[ $num ];
+}
+
+sub populate_data_groups : PRIVATE {
+
+    my ( $self, $group, $num_groups ) = @_;
+
+    return unless $num_groups;
+
+    $group ||= $self->get_data_group( 0 )
+	or croak("Error: Initial data group not created.\n");
+
+    # The last data group should point to zero (but we don't rely on it).
+    my $next_group = ArrayExpress::Datafile::Affymetrix::Calvin::DataGroup->new({
+	filehandle => $self->get_filehandle(),
+	position   => $group->get_next_group_position(),
+    });
+    push @{ $data_group{ ident $self } }, $next_group;
+    $self->populate_data_groups($next_group, $num_groups - 1);
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Parameter.pm b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Parameter.pm
new file mode 100644
index 0000000..fe6efc6
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Parameter.pm
@@ -0,0 +1,19 @@
+#!/usr/bin/env perl
+#
+# $Id: Parameter.pm 1973 2008-02-27 18:10:51Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::Datafile::Affymetrix::Calvin::Parameter;
+
+# Simple class to store NVT-style parameter values.
+
+use Class::Std;
+use Carp;
+
+my %name  : ATTR( :name<name>,  :default<undef> );
+my %value : ATTR( :name<value>, :default<undef> );
+my %type  : ATTR( :name<type>,  :default<undef> );
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/EXP.pm b/lib/ArrayExpress/Datafile/Affymetrix/EXP.pm
new file mode 100644
index 0000000..e40c387
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/EXP.pm
@@ -0,0 +1,396 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix EXP files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: EXP.pm 1852 2007-12-13 10:14:27Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../../index.html">
+                <img src="../../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: CDF.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::EXP - EXP data file parsing
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::EXP;
+
+ my $exp = ArrayExpress::Datafile::Affymetrix::EXP->new({
+     input => 'data1.EXP',
+ });
+ $exp->parse();
+
+=head1 DESCRIPTION
+
+This module implements parsing and export of data from Affymetrix EXP files.
+
+Please see L<ArrayExpress::Datafile::Affymetrix::Parser> for
+methods common to all the Affymetrix parser classes.
+
+=head1 METHODS
+
+=over 2
+
+=item export($fh)
+
+This method takes a filehandle and prints out the EXP file in its
+original format. This method is not particularly useful and is really
+for testing purposes only.
+
+=item get_chip_lot()
+
+The lot number of the chip.
+
+=item get_operator()
+
+The person who performed the procedure.
+
+=item get_protocol()
+
+The name of the hybridization protocol used (e.g. EukGE-WS2v4).
+
+=item get_station()
+
+The station number.
+
+=item get_module()
+
+The module number.
+
+=item get_hyb_date()
+
+The date on which the hybridization was performed. This is returned in
+the same format as in the EXP file; no sanitization is performed.
+
+=item get_pixel_size()
+
+Pixel size (integer).
+
+=item get_filter()
+
+Filter (570nm).
+
+=item get_scan_temp()
+
+Scan temperature.
+
+=item get_scan_date()
+
+The date on which the scanning was performed. This is returned in
+the same format as in the EXP file; no sanitization is performed.
+
+=item get_scanner_id()
+
+Scanner ID.
+
+=item get_num_scans()
+
+Number of scans performed.
+
+=item get_scanner_type()
+
+Scanner type.
+
+=item get_hyb_parameters()
+
+A reference to an array of named hybridization parameters. Each
+parameter is coded as a separate hash with a single {name => value}
+pair. Typically this method is not very useful; you should probably be
+using the B<parameters> or B<add_parameters> methods instead.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::EXP;
+use base 'ArrayExpress::Datafile::Affymetrix::Parser';
+
+use strict;
+use warnings;
+
+use English qw( -no_match_vars );
+use Carp;
+use Scalar::Util qw( openhandle );
+use IO::File;
+use Class::Std;
+
+use ArrayExpress::Curator::Common qw(
+    check_linebreaks
+);
+
+my %chip_lot       : ATTR( :name<chip_lot>,       :default<undef> );
+my %operator       : ATTR( :name<operator>,       :default<undef> );
+my %protocol       : ATTR( :name<protocol>,       :default<undef> );
+my %station        : ATTR( :name<station>,        :default<undef> );
+my %module         : ATTR( :name<module>,         :default<undef> );
+my %hyb_date       : ATTR( :name<hyb_date>,       :default<undef> );
+my %pixel_size     : ATTR( :name<pixel_size>,     :default<undef> );
+my %filter         : ATTR( :name<filter>,         :default<undef> );
+my %scan_temp      : ATTR( :name<scan_temp>,      :default<undef> );
+my %scan_date      : ATTR( :name<scan_date>,      :default<undef> );
+my %scanner_id     : ATTR( :name<scanner_id>,     :default<undef> );
+my %num_scans      : ATTR( :name<num_scans>,      :default<undef> );
+my %scanner_type   : ATTR( :name<scanner_type>,   :default<undef> );
+my %hyb_parameters : ATTR( :name<hyb_parameters>, :default<[]>    );
+
+sub parse {
+
+    my ( $self ) = @_;
+
+    my $input = $self->get_input();
+
+    my ( $fh, $linebreak );
+    if ( openhandle($input) ) {
+        $fh = $input;
+    }
+    else {
+        my $counts;
+        ( $counts, $linebreak ) = check_linebreaks($input);
+        unless ($linebreak) {
+            croak(    "Error: Unable to parse line endings in file $input: "
+                    . "($counts->{unix} Unix, $counts->{dos} DOS, $counts->{mac} Mac)\n"
+            );
+        }
+        $fh = IO::File->new( $input, '<' )
+            or croak("Unable to open EXP file $input : $!\n");
+    }
+
+    # Localise our line ending variable to the scope of this subroutine.
+    local $INPUT_RECORD_SEPARATOR = $linebreak;
+
+    # Setting binmode here interferes with the linebreak processing, so
+    # we don't.
+    #  binmode($fh, ":crlf");
+    seek( $fh, 0, 0 ) or croak("Error rewinding filehandle for input: $!\n");
+
+    my $label = <$fh>;
+    $label =~ s/[\r\n]* \z//xms;
+
+    # Allow following whitespace - not in the Affy spec, but all too common.
+    unless ( $label
+        =~ m{\A Affymetrix\ GeneChip\ Experiment\ Information \s* \z}xms ) {
+
+        # Strip out Mac line endings from the error message - they play
+        # havoc with the terminal and obscure the error.
+        $label =~ s/\r//g;
+        croak("Error: Unrecognized EXP file format: $label\n");
+    }
+
+    my $version = <$fh>;
+    $version =~ s{\A Version \t (\d+) [\r\n]*}{$1}xms;  # Just want the number
+    $self->set_version($version);
+
+    my $line = q{};
+
+    until ( $line =~ m{\A \[Sample\ Info\]}xms ) {
+        $line = <$fh>;
+        $line =~ s/[\r\n]* \z//xms;
+    }
+
+    LINE:
+    while ( my $line = <$fh> ) {
+        $line =~ s/[\r\n]* \z//xms
+            ;    # just to be sure about DOS-style line endings
+        last LINE if ( $line =~ m/\A \s* \z/xms );
+        my ( $parameter, $value ) = split /\t/, $line;
+
+        SAMPLEINFO:
+        {
+
+            ( $parameter eq 'Chip Type' )
+                && do { $self->set_chip_type($value); last SAMPLEINFO; };
+
+            ( $parameter eq 'Chip Lot' )
+                && do { $self->set_chip_lot($value); last SAMPLEINFO; };
+
+            ( $parameter eq 'Operator' )
+                && do { $self->set_operator($value); last SAMPLEINFO; };
+
+        }
+    }
+
+    until ( $line =~ m{\A \[Fluidics\]}xms ) {
+        $line = <$fh>;
+        $line =~ s/[\r\n]* \z//xms;
+    }
+    my $param_count = 0;    # Hyb parameter numbering starts at zero
+
+    LINE:
+    while ( my $line = <$fh> ) {
+        $line =~ s/[\r\n]* \z//xms
+            ;               # just to be sure about DOS-style line endings
+        last LINE if ( $line =~ m/\A \s* \z/xms );
+        my ( $parameter, $value ) = split /\t/, $line;
+
+        FLUIDICS:
+        {
+
+            ( $parameter eq 'Protocol' )
+                && do { $self->set_protocol($value); last FLUIDICS; };
+
+            ( $parameter eq 'Station' )
+                && do { $self->set_station($value); last FLUIDICS; };
+
+            ( $parameter eq 'Module' )
+                && do { $self->set_module($value); last FLUIDICS; };
+
+            ( $parameter eq 'Hybridize Date' )
+                && do { $self->set_hyb_date($value); last FLUIDICS; };
+
+            # Wash params here. We put the originals in hyb_parameters(),
+            # and the mage-sanitized ones in parameters()
+            my $mage_param
+                = "HybridizationStep$param_count-" . $self->get_protocol();
+            $self->add_parameters( { $mage_param => $value } );
+            $self->add_hyb_parameters( { $parameter => $value } );
+            $param_count++;
+        }
+    }
+
+    until ( $line =~ m{\A \[Scanner\]}xms ) {
+        $line = <$fh>;
+        $line =~ s/[\r\n]* \z//xms;
+    }
+
+    LINE:
+    while ( my $line = <$fh> ) {
+        $line =~ s/[\r\n]* \z//xms
+            ;    # just to be sure about DOS-style line endings
+        last LINE if ( $line =~ m/\A \s* \z/xms );
+        my ( $parameter, $value ) = split /\t/, $line;
+
+        SCANNER:
+        {
+
+            ( $parameter eq 'Pixel Size' )
+                && do { $self->set_pixel_size($value); last SCANNER; };
+
+            ( $parameter eq 'Filter' )
+                && do { $self->set_filter($value); last SCANNER; };
+
+            ( $parameter eq 'Scan Temperature' )
+                && do { $self->set_scan_temp($value); last SCANNER; };
+
+            ( $parameter eq 'Scan Date' )
+                && do { $self->set_scan_date($value); last SCANNER; };
+
+            ( $parameter eq 'Scanner ID' )
+                && do { $self->set_scanner_id($value); last SCANNER; };
+
+            ( $parameter eq 'Number of Scans' )
+                && do { $self->set_num_scans($value); last SCANNER; };
+
+            ( $parameter eq 'Scanner Type' )
+                && do { $self->set_scanner_type($value); last SCANNER; };
+
+        }
+    }
+
+    return;
+}
+
+####################
+# Accessor Methods #
+####################
+
+# The hyb_parameters accessor is a special case
+
+sub add_hyb_parameters : PRIVATE {    # arrayref (adds to old params)
+
+    my ( $self, $params ) = @_;
+    
+    ref $params eq 'HASH'
+	or confess( 'Bad parameters passed to method' );
+    push( @{ $hyb_parameters{ident $self} }, $params );
+
+    return;
+}
+
+sub export {    # This is really only for checking purposes
+
+    my ( $self, $fh ) = @_;
+
+    openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+    print $fh (
+        "Affymetrix GeneChip Experiment Information\n",
+        "Version\t", $self->get_version, "\n",
+        "\n",
+
+        "[Sample Info]\n",
+        "Chip Type\t", $self->get_chip_type, "\n",
+        "Chip Lot\t",  $self->get_chip_lot,  "\n",
+        "Operator\t",  $self->get_operator,  "\n"
+    );
+    print $fh ("\n");
+
+    printf $fh ( "[Fluidics]\nProtocol\t%s\n", $self->get_protocol() );
+
+    foreach my $parameter ( @{ $self->get_hyb_parameters() } ) {
+        while ( my ( $key, $value ) = each %$parameter ) {
+            print $fh ( "$key\t", $value, "\n" );
+        }
+    }
+
+    printf $fh (
+        "Station\t%s\nModule\t%s\nHybridize Date\t%s\n\n",
+        $self->get_station,
+        $self->get_module,
+	$self->get_hyb_date,
+    );
+
+    printf $fh (
+        "[Scanner]\nPixel Size\t%s\nFilter\t%s\nScan Temperature\t%s\n"
+      . "Scan Date\t%s\nScanner ID\t%s\nNumber of Scans\t%s\nScanner Type\t%s\n",
+        $self->get_pixel_size,
+	$self->get_filter,
+        $self->get_scan_temp,
+	$self->get_scan_date,
+        $self->get_scanner_id,
+	$self->get_num_scans,
+        $self->get_scanner_type,
+    );
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/EXPFactory.pm b/lib/ArrayExpress/Datafile/Affymetrix/EXPFactory.pm
new file mode 100644
index 0000000..8c3af01
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/EXPFactory.pm
@@ -0,0 +1,108 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: EXPFactory.pm 1808 2007-11-01 18:55:34Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../index.html">
+                <img src="../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: EXPFactory.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::EXPFactory - a factory class for generating
+Affymetrix EXP file parsing objects.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::EXPFactory;
+
+ my $fac = ArrayExpress::Datafile::Affymetrix::EXPFactory->new();
+
+ my $exp = $fac->make_parser( 'Data1.EXP' );
+
+ $exp->parse();
+
+ $exp->export($output_filehandle);
+
+=head1 DESCRIPTION
+
+This module is a factory class used in to create data file parsers for
+Affymetrix EXP files.
+
+=head1 METHODS
+
+=over 2
+
+=item new()
+
+The class constructor.
+
+=item make_parser($file)
+
+This method takes a filename argument and returns a parser object that
+can then be used to process the data file and return interesting
+values.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::EXPFactory;
+use base 'ArrayExpress::Datafile::Affymetrix';
+
+use strict;
+use warnings;
+
+use Class::Std;
+
+sub make_parser {
+
+    my ( $self, $input ) = @_;
+
+    # There's only one EXP file type, making this extremely simple.
+    require ArrayExpress::Datafile::Affymetrix::EXP;
+    return ArrayExpress::Datafile::Affymetrix::EXP->new({
+	input => $input,
+    });
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/Parser.pm b/lib/ArrayExpress/Datafile/Affymetrix/Parser.pm
new file mode 100644
index 0000000..aca3060
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/Parser.pm
@@ -0,0 +1,355 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix data files.
+#
+# Tim Rayner 2005, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Parser.pm 2021 2008-04-09 09:55:25Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../index.html">
+                <img src="../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: Affymetrix/Parser.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::Parser - an Affymetrix data file parsing
+module.
+
+=head1 SYNOPSIS
+
+ use base qw( ArrayExpress::Datafile::Affymetrix::Parser );
+
+=head1 DESCRIPTION
+
+This module is an abstract superclass used in the parsing and export
+of data from Affymetrix file formats. CEL, CHP and EXP formats are
+supported, with limited CDF parsing. Both old (GDAC) and new
+(GCOS/XDA) file formats can be parsed.
+
+There is a set of methods common to all the Affymetrix file classes,
+listed below. There are additional methods, specific to each class,
+which are documented in the relevant pages.
+
+=head1 METHODS
+
+The following methods are common to all classes.
+
+=over 2
+
+=item new({ input => 'myfile.CEL' })
+
+The class constructor. This method returns a an object of the
+appropriate class, without performing any additional processing.
+
+=item parse()
+
+This method will take the value for the C<input> attribute and parse the
+data into memory so that it can be interrogated using the methods below.
+
+=back
+
+=head2 Accessor methods
+
+Each of these methods acts as both setter and getter for the
+attributes in question. Typically these will be used to access the
+data and metadata which was extracted using the B<parse>
+method. Please see the respective subclass documentation for
+information on the B<export> and B<get_ded> methods. Note that many of the
+following will have no meaning for EXP file metadata.
+
+=over 2
+
+=item get_version()
+
+The version number or string associated with the file. For CEL and CHP
+files this should be either 3 or 4.
+
+=item get_num_columns()
+
+The number of columns on the array.
+
+=item get_num_rows()
+
+The number of rows on the array.
+
+=item get_num_cells()
+
+The number of cells on the array. For CEL files this corresponds to
+the number of columns multiplied by the number of rows.
+
+=item get_algorithm()
+
+The name of the algorithm used to produce the data (e.g. "Percentile",
+"ExpressionStat").
+
+=item get_chip_type()
+
+The type of chip used (e.g., HG-U133A). This is supported for EXP
+files, CEL files, CHP files and GDAC format CDF files. Note that for
+CEL files this relies on parsing a header tag which is not actually
+documented by Affymetrix, and so it is possible that this method is
+not to be trusted in such cases.
+
+=item get_parameters()
+
+A reference to a hash with parameter {name => value} pairs. Parameters
+are grouped as follows:
+
+ CEL: Feature extraction parameters
+ CHP: Normalization parameters
+ EXP: Hybridization parameters (numbered).
+
+=item get_stats()
+
+A reference to a hash with statistic {name => value} pairs. Statistics
+are grouped as follows:
+
+ CEL: Feature extraction summary statistics
+ CHP: Normalization summary statistics
+
+=item get_qtd()
+
+A reference to an array listing the QuantitationType identifiers (long
+form) in the column order that they are output by the B<export>
+method. See also the B<headings> method below.
+
+=item get_headings()
+
+A reference to an array listing the QuantitationType names (short
+form) in the column order that they are output by the B<export>
+method. Using this method also populates the B<qtd> method data
+structure, but not vice versa.
+
+=cut
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::Parser;
+
+use strict;
+use warnings;
+
+use Class::Std;
+use Carp;
+use Readonly;
+use Scalar::Util qw(openhandle);
+
+use ArrayExpress::Curator::Common qw(clean_hash);
+
+my %input          : ATTR( :name<input>,          :default<undef> );
+my %filehandle     : ATTR( :init_arg<filehandle>, :default<undef> );
+my %magic          : ATTR( :name<magic>,          :default<undef> );
+my %required_magic : ATTR( :name<required_magic>, :default<undef> );
+
+my %num_columns  : ATTR( :name<num_columns>, :default<undef> );
+my %num_rows     : ATTR( :name<num_rows>,    :default<undef> );
+my %num_cells    : ATTR( :name<num_cells>,   :default<undef> );
+my %version      : ATTR( :name<version>,     :default<undef> );
+my %algorithm    : ATTR( :name<algorithm>,   :default<undef> );
+my %chip_type    : ATTR( :name<chip_type>,   :default<undef> );
+my %parameters   : ATTR( :get<parameters>,   :default<{}> );
+my %stats        : ATTR( :get<stats>,        :default<{}> );
+my %qtd          : ATTR( :name<qtd>,         :default<[]> );
+my %headings     : ATTR( :get<headings>,     :default<[]> );
+my %data_matrix  : ATTR( :default<[]> );
+my %data_storage : ATTR( :name<data_storage>, :default<'ARRAY'> );
+
+# Prefix used in the construction of QT identifiers
+Readonly my $AFFY_QT_PREFIX => 'Affymetrix:QuantitationType:';
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    unless ( $input{ ident $self } || $filehandle{ ident $self } ) {
+	confess("Error: no input to parsing module.");
+    }
+
+    return;
+}
+
+sub get_filehandle : RESTRICTED {
+
+    my ( $self ) = @_;
+
+    my $input = $self->get_input();
+    if ( ! $filehandle{ ident $self } && $input ) {
+	if ( openhandle( $input ) ) {
+	    $filehandle{ ident $self } = $input;
+	}
+	else {
+	    $filehandle{ ident $self } = IO::File->new( $input, '<' )
+		or croak(qq{Unable to open file "$input" : $!\n});
+	}
+    }
+
+    return $filehandle{ ident $self };
+}
+
+sub parse {
+    confess("Error: Stub method called in abstract superclass.");
+}
+
+sub set_parameters : RESTRICTED {
+
+    my ( $self, $hashref ) = @_;
+
+    ref $hashref eq 'HASH'
+	or confess( 'Bad parameters passed to method' );
+
+    # strip out undef or empty string values
+    my $cleaned = clean_hash($hashref);
+
+    $parameters{ident $self} = $cleaned;
+
+    return;
+}
+
+sub add_parameters : RESTRICTED {
+
+    # When called with a reference to a hash of parameter {name =>
+    # value} pairs this method adds those parameters to any
+    # pre-existing ones. Parameters having the same name are
+    # overwritten.
+
+    my ( $self, $hashref ) = @_;
+
+    ref $hashref eq 'HASH'
+	or confess( 'Bad parameters passed to method' );
+
+    # strip out undef or empty string values
+    my $cleaned = clean_hash($hashref);
+
+    @{ $parameters{ident $self} }{ keys %$cleaned } = values %$cleaned;
+
+    return;
+}
+
+sub set_stats : RESTRICTED {
+
+    my ( $self, $hashref ) = @_;
+
+    ref $hashref eq 'HASH'
+	or confess( 'Bad parameters passed to method' );
+
+    # strip out undef or empty string values
+    my $cleaned = clean_hash($hashref);
+
+    $stats{ident $self} = $cleaned;
+
+    return;
+}
+
+sub add_stats : RESTRICTED {
+
+    # When called with a reference to a hash of statistic {name =>
+    # value} pairs this method adds those statistics to any
+    # pre-existing ones. Statistics having the same name are
+    # overwritten.
+
+    my ( $self, $hashref ) = @_;
+
+    ref $hashref eq 'HASH'
+	or confess( 'Bad parameters passed to method' );
+
+    # strip out undef or empty string values
+    my $cleaned = clean_hash($hashref);
+
+    @{ $stats{ident $self} }{ keys %$cleaned } = values %$cleaned;
+
+    return;
+}
+
+sub set_headings : RESTRICTED {    # arrayref (replaces old qtd); CEL and CHP only
+    my ( $self, $list ) = @_;
+
+    ref $list eq 'ARRAY'
+	or confess( 'Bad parameters passed to method' );
+    $headings{ident $self} = $list;
+
+    my @qtd;
+    foreach my $heading ( @{ $list } ) {
+	push( @qtd, $AFFY_QT_PREFIX . $heading );
+    }
+    $qtd{ident $self} = \@qtd;
+
+    return;
+}
+
+# set/get_data is a restricted method used in the Affymetrix subclasses
+# only. It provides access to the parsed data.
+
+sub set_data : RESTRICTED {
+
+    # Arrayref by default; CEL.pm however uses a hashref. We make this
+    # restricted to hide this complexity.
+
+    my ( $self, $data ) = @_;
+
+    # Subclasses using non-arrayref storage need to set_data with
+    # e.g. an empty hashref before calling this.
+    ref $data eq $self->get_data_storage()
+	or confess( 'Bad parameters passed to method' );
+
+    $data_matrix{ident $self} = $data;
+
+    return;
+}
+
+sub get_data : RESTRICTED {
+
+    my ( $self, $data ) = @_;
+
+    return $data_matrix{ident $self};
+}
+
+sub _get_line : RESTRICTED {
+
+    my ( $self, $fh ) = @_;
+
+    openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+    my $line = <$fh>;
+    $line =~ s/[\r\n]* \z//xms if $line;
+    return $line;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/ArrayDesign.pm b/lib/ArrayExpress/Datafile/ArrayDesign.pm
new file mode 100644
index 0000000..d75e4a3
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/ArrayDesign.pm
@@ -0,0 +1,215 @@
+#!/usr/bin/env perl
+#
+# ArrayDesign.pm
+#
+# Tim Rayner 2008 ArrayExpress team, EBI
+#
+# $Id: ArrayDesign.pm 1987 2008-03-06 11:29:22Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../index.html">
+                <img src="../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: ExperimentChecker.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::ArrayDesign - memory-efficient handling of
+array design information.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile:::ArrayDesign
+
+=head1 DESCRIPTION
+
+ArrayDesign objects are used during experiment checking to handle
+array design elements in a memory-efficient manner.
+
+=head2 Accessor methods
+
+=over 2
+
+=item set_accession
+
+Setter method for the array accession number relating to this design.
+
+=item get_accession
+
+Getter method for the array accession number relating to this design.
+
+=item set_adf_features
+
+Setter method for a hashref with keys which are period-delim strings
+of the feature coordinates found in the array design associated with
+the file.
+
+=item get_adf_features
+
+Getter method for a hashref with keys which are period-delim strings
+of the feature coordinates found in the array design associated with
+the file.
+
+=item set_adf_reporters
+
+Setter method for a hashref with keys which are the reporter
+identifiers found in the array design associated with the file.
+
+=item get_adf_reporters
+
+Getter method for a hashref with keys which are the reporter
+identifiers found in the array design associated with the file.
+
+=item set_adf_compseqs
+
+Setter method for a hashref with keys which are the composite sequence
+identifiers found in the array design associated with the file.
+
+=item get_adf_compseqs
+
+Getter method for a hashref with keys which are the composite sequence
+identifiers found in the array design associated with the file.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2004.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments. 
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::ArrayDesign;
+
+use strict;
+use warnings;
+
+use Carp;
+use IO::File;
+use Class::Std;
+use Storable qw(store_fd fd_retrieve);
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+my %feature_fh    : ATTR( :default<undef> ); 
+my %reporter_fh   : ATTR( :default<undef> ); 
+my %compseq_fh    : ATTR( :default<undef> );
+
+my %accession     : ATTR( :name<accession>, :default<undef> );
+
+sub set_features {
+
+    my ( $self, $features ) = @_;
+
+    $self->set_design_elements( \%feature_fh, $features );
+}
+
+sub get_features {
+
+    my ( $self ) = @_;
+
+    return $self->get_design_elements( \%feature_fh );
+}
+
+sub set_reporters {
+
+    my ( $self, $reporters ) = @_;
+
+    $self->set_design_elements( \%reporter_fh, $reporters );
+}
+
+sub get_reporters {
+
+    my ( $self ) = @_;
+
+    return $self->get_design_elements( \%reporter_fh );
+}
+
+sub set_compseqs {
+
+    my ( $self, $compseqs ) = @_;
+
+    $self->set_design_elements( \%compseq_fh, $compseqs );
+}
+
+sub get_compseqs {
+
+    my ( $self ) = @_;
+
+    return $self->get_design_elements( \%compseq_fh );
+}
+
+sub set_design_elements : PRIVATE {
+
+    my ( $self, $attr_var, $elements ) = @_;
+
+    defined($attr_var)
+	or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    ref $elements eq 'HASH'
+	or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my $fh = IO::File->new_tmpfile();
+
+    store_fd( $elements, $fh )
+	or confess("Error caching array design element list: $!");
+    seek($fh, 0, 0) or confess("Error rewinding design element cache file: $!");
+
+    $attr_var->{ident $self} = $fh;
+
+    return;
+}
+
+sub get_design_elements : PRIVATE {
+
+    my ( $self, $attr_var ) = @_;
+
+    defined($attr_var)
+	or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    # Return empty hash if array not in cache.
+    my $elements = {};
+    if ( my $fh = $attr_var->{ident $self} ) {
+
+	my $pos = tell($fh);
+
+	seek($fh, 0, 0) or confess("Error rewinding design element cache file: $!");
+
+	$elements = fd_retrieve( $fh )
+	    or confess("Error retrieving cached array design elements: $!");
+
+	seek($fh, $pos, 0) or confess("Error resetting design element cache file: $!");
+    }
+
+    return $elements;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Binary.pm b/lib/ArrayExpress/Datafile/Binary.pm
new file mode 100644
index 0000000..e66f233
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Binary.pm
@@ -0,0 +1,189 @@
+#!/usr/bin/env perl
+#
+# Module to provide basic binary file data parsing functions.used in
+# e.g. Affymetrix file parsing module(s)
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Binary.pm 1973 2008-02-27 18:10:51Z tfrayner $
+#
+
+package ArrayExpress::Datafile::Binary;
+
+use strict;
+use warnings;
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../index.html">
+                <img src="../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: Binary.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Binary.pm - a module providing functions for
+reading files in binary form.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Binary qw(get_integer get_float);
+ 
+ my $int   = get_integer($fh);
+ my $float = get_float($fh);
+
+=head1 DESCRIPTION
+
+This is a simple module providing functions which read an appropriate
+number of bytes from the passed filehandle, and return the
+appropriately unpacked value for further processing. Since this module
+was originally developed to aid Affymetrix file parsing there is a
+certain bias as to which data types are handled (the list is not
+comprehensive; nor is it intended to be).
+
+=head1 FUNCTIONS
+
+=over 2
+
+=item get_integer( $fh )
+
+=item get_unsigned_short( $fh )
+
+=item get_unsigned_char( $fh )
+
+=item get_signed_char( $fh )
+
+=item get_float( $fh, $length )
+
+=item get_ascii( $fh, $length )
+
+=item get_hexadecimal( $fh )
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2008.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments. 
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+use Carp;
+use Readonly;
+
+use base 'Exporter';
+our @EXPORT_OK = qw(
+    get_integer
+    get_DWORD
+    get_unsigned_short
+    get_unsigned_char
+    get_signed_char
+    get_float
+    get_ascii
+    get_hexadecimal
+);
+
+# This constant determines how floats are handled on big-endian systems.
+# NB. it is unclear at this stage whether 64-bit systems will work.
+Readonly my $IS_BIG_ENDIAN => unpack( "h*", pack( "s", 1 ) ) =~ /01/;
+
+#################################
+# Binary data parsing functions #
+#################################
+
+sub get_integer {    # Signed integer only.
+    my $fh = shift;
+    my $num_bytes = sysread( $fh, my $value, 4 );
+    croak($!) unless defined($num_bytes);
+
+    # Little-endian long, for portability's sake
+    return ( unpack "V*", $value );
+}
+
+sub get_DWORD {
+    my $fh = shift;
+    my $num_bytes = sysread( $fh, my $value, 4 );
+    croak($!) unless defined($num_bytes);
+    return ( unpack "V*", $value );
+}
+
+sub get_unsigned_short {
+    my $fh = shift;
+    my $num_bytes = sysread( $fh, my $value, 2 );
+    croak($!) unless defined($num_bytes);
+    return ( unpack "v*", $value );
+}
+
+sub get_unsigned_char {
+    my $fh = shift;
+    my $num_bytes = sysread( $fh, my $value, 1 );
+    croak($!) unless defined($num_bytes);
+    return ( unpack "C*", $value );
+}
+
+sub get_signed_char {
+    my $fh = shift;
+    my $num_bytes = sysread( $fh, my $value, 1 );
+    croak($!) unless defined($num_bytes);
+    return ( unpack "c*", $value );
+}
+
+sub get_float {
+    my ( $fh, $length ) = @_;
+    $length ||= 4;    # defaults to a length of 4 bytes
+    my $num_bytes = sysread( $fh, my $value, $length );
+    croak($!) unless defined($num_bytes);
+    if ($IS_BIG_ENDIAN) {
+        return ( unpack 'f*', pack 'V*', unpack 'N*', $value );
+    }
+    else {
+        return ( unpack "f*", $value );
+    }
+}
+
+sub get_ascii {
+    my ( $fh, $length ) = @_;
+
+    # No longer default to a length of 1 byte - caused too many bugs
+    #  $length ||= 1;
+    my $num_bytes = sysread( $fh, my $value, $length );
+    croak($!) unless defined($num_bytes);
+    return ( unpack "a*", $value );
+}
+
+sub get_hexadecimal {    # High nybble first
+    my ($fh) = @_;
+    my $num_bytes = sysread( $fh, my $value, 1 );
+    croak($!) unless defined($num_bytes);
+    return ( unpack "H*", $value );
+}
+
+1;
+
diff --git a/lib/ArrayExpress/Datafile/DataMatrix.pm b/lib/ArrayExpress/Datafile/DataMatrix.pm
new file mode 100644
index 0000000..a821a03
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/DataMatrix.pm
@@ -0,0 +1,762 @@
+#!/usr/bin/env perl
+#
+# Module to help generation of DataMatrix MAGE objects.
+#
+# Tim Rayner 2005, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: DataMatrix.pm 2069 2008-06-04 14:33:52Z tfrayner $
+#
+
+package ArrayExpress::Datafile::DataMatrix;
+
+use strict;
+use warnings;
+
+use Carp;
+use English qw( -no_match_vars );
+use Class::Std;
+
+use Bio::MAGE qw(:ALL);
+
+require ArrayExpress::Datafile;
+use ArrayExpress::Curator::MAGE qw(
+    update_factorvalues
+    unique_identifier
+);
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+use ArrayExpress::Curator::MAGE::Definitions qw(
+    $EDF_TRANSFORMATIONNAME
+    $EDF_TRANSFORMATIONTYPE
+    $EDF_TRXN_PROTOCOL
+    $EDF_TRXN_SOFTWARE
+    $OE_VAL_NORMALIZATION
+    $OE_VAL_TRANSFORMATION_SOFTWARE
+);
+
+sub get_dimension_lists {
+
+    # Takes an AoA of hyb names and an array of qt names, returns the
+    # ba and qt dimensions as arrayrefs of names, and a BioDataCube
+    # order string.
+
+    my ( $self, $datamatrix_hybs, $datamatrix_qts ) = @_;
+
+    ref $datamatrix_qts eq 'ARRAY'
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    ref $datamatrix_hybs eq 'ARRAY'
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my $previous_qts = {};
+    my $ba_namerefs  = [];
+    my $qt_names     = [];
+    my $order;
+    my $period;
+
+    # Use hybs as the benchmark; QTs may have been dropped on parsing.
+    for ( my $i = 0; $i <= $#$datamatrix_hybs; $i++ ) {
+
+        # Get the variables for this column, for readability.
+        my $qt      = $datamatrix_qts->[$i];
+        my $ba_list = $datamatrix_hybs->[$i];
+
+        # QT dimension
+        my $qt_seen;
+        if ($qt) {
+            unless ( $qt_seen = $previous_qts->{$qt} ) {
+                push( @$qt_names, $qt );
+                $previous_qts->{$qt}++;
+            }
+        }
+
+        # BA dimension
+        my %ba_key = map { $_ => 1 } sort @{$ba_list};
+        my $ba_seen;
+        foreach my $old (@$ba_namerefs) {
+
+            # Compare arrays $bioassays and $old.
+            # Set $ba_seen unless there is a difference.
+            $ba_seen++ unless ( grep { !exists $ba_key{$_} } sort @$old );
+        }
+        unless ($ba_seen) {
+
+            # add the bioassay to @$ba_namerefs
+            push( @$ba_namerefs, $ba_list );
+        }
+
+        # Set our BioDataCube order. This should be settled by the
+        # second DataMatrix column. Setting this on any other iteration
+        # leads to spurious results. See below for the special cases
+        # where either B or Q is 1.
+        if ( $i == 1 ) {
+            if ( $qt_seen && !$ba_seen ) { $order ||= 'DQB'; }
+            if ( $ba_seen && !$qt_seen ) { $order ||= 'DBQ'; }
+        }
+
+        # Confirm that the detected order is followed in subsequent columns.
+        elsif ( $i > 1 && $order ) {
+
+            # Set the periodicity of the file as soon as the next (qt
+            # || ba) is seen (dependent on the order of the file).
+            if ( !$period ) {
+                if ( $order eq 'DBQ' && !$ba_seen ) {
+                    $period = scalar @$qt_names;
+                }
+                elsif ( $order eq 'DQB' && !$qt_seen ) {
+                    $period = scalar @$ba_namerefs;
+                }
+            }
+
+            # First block done, $order is determined, now we start
+            # checking that the rest of the file is okay.
+            if ($period) {
+                my $prev_qt      = $datamatrix_qts->[ $i - 1 ];
+                my $prev_ba_list = $datamatrix_hybs->[ $i - 1 ];
+
+                if ( $order eq 'DBQ' ) {
+                    if ( $qt ne $qt_names->[ $i % $period ] ) {
+                        $order = undef;
+                    }
+                    if (( $i % $period != 0 )
+                        && (grep { !exists $ba_key{$_} }
+                            sort @{$prev_ba_list}
+                        )
+                        ) {
+                        $order = undef;
+                    }
+                }
+
+                elsif ( $order eq 'DQB' ) {
+                    if (grep { !exists $ba_key{$_} }
+                        sort @{ $ba_namerefs->[ $i % $period ] }
+                        ) {
+                        $order = undef;
+                    }
+                    if ( ( $i % $period != 0 ) && ( $qt ne $prev_qt ) ) {
+                        $order = undef;
+                    }
+                }
+            }
+        }
+    }
+
+    # One extra check for special case where either Q==1 or B==1
+    # We prefer DBQ as ArrayExpress can handle it.
+    if ( @$qt_names == 1 || @$ba_namerefs == 1 ) { $order = 'DBQ'; }
+
+    if ($order) {
+        return ( $ba_namerefs, $qt_names, $order );
+    }
+    else {
+        return ( [], [], $order );
+    }
+
+}
+
+sub get_bioassay_qts : RESTRICTED {
+
+    my ($self, $bioassays) = @_;
+
+    ref $bioassays eq 'ARRAY'
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    # Find the highest-level QTs available for QuantitationTypeMapping
+    # N.B. this assumes a few things - that BioAssayData is associated
+    # with BioAssay (MAGE best practice), and that we've fully created
+    # these objects before trying to add the DataMatrix.
+    my %source_qts;
+    my %source_data;
+    foreach my $bioassay (@$bioassays) {
+        my $datalist;
+        if ( $bioassay->isa('Bio::MAGE::BioAssay::MeasuredBioAssay') ) {
+            $datalist = $bioassay->getMeasuredBioAssayData;
+        }
+        elsif ( $bioassay->isa('Bio::MAGE::BioAssay::DerivedBioAssay') ) {
+            $datalist = $bioassay->getDerivedBioAssayData;
+        }
+        if ($datalist) {
+            foreach my $data (@$datalist) {
+                $source_data{ $data->getIdentifier } = $data;
+                my $source_qtd;
+                if (   ( $source_qtd = $data->getQuantitationTypeDimension )
+                    && ( my $source_qts = $source_qtd->getQuantitationTypes )
+                    ) {
+                    foreach my $qt (@$source_qts) {
+                        $source_qts{ $qt->getIdentifier } = $qt;
+                    }
+                }
+            }
+        }
+    }
+
+    my @sorted_qts  = map { $source_qts{$_} } sort keys %source_qts;
+    my @sorted_data = map { $source_data{$_} } sort keys %source_data;
+
+    return ( \@sorted_qts, \@sorted_data );
+}
+
+sub create_mage {
+
+    my ( $self, $file, $datarow, $tab2mage, $identifier_template ) = @_;
+
+    $file->isa('ArrayExpress::Datafile')
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    ref $datarow eq 'HASH'
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    $tab2mage->isa('ArrayExpress::Curator::Tab2MAGE')
+	or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    ref $identifier_template
+        and confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my $datamatrix_hybs = $file->get_heading_hybs();
+    my $datamatrix_qts  = $file->get_heading_qts();
+
+    my $bag_of    = $tab2mage->get_bags();
+
+    # Construct the dimensions
+    my ( $ba_namerefs, $qt_names, $order )
+        = $self->get_dimension_lists( $datamatrix_hybs, $datamatrix_qts );
+
+    # Internal consistency check
+    warn("Warning: Order of DataMatrix data cube is indeterminate.\n")
+        unless $order;
+
+    # Warn on unsupported cube order
+    if ( $order && $order eq 'DQB' ) {
+        warn(
+            qq{WARNING: A BioDataCube order of "$order" is not supported by the ArrayExpress MAGE-ML loader.\n}
+        );
+    }
+
+    # Create the actual qtd here
+    my ( $qt_list, $qtd_key ) = $tab2mage->qts_from_names( $file, $qt_names );
+
+    $identifier_template .= '.DataMatrix.' . unique_identifier;
+
+    my $qtd = $bag_of->{qtd}->(
+        $qtd_key,
+        {   qt_list             => $qt_list,
+            identifier_template => $identifier_template,
+        }
+    );
+    $file->set_mage_qtd($qtd);
+
+    # The per-column bioassays are created here.
+    my $ba_list = [];
+    my %source_qts;
+    my %source_data;
+    foreach my $listref (@$ba_namerefs) {
+
+        my $bioassay;
+
+	if ( $file->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE() ) {
+
+	    # DBA can map to bag_of dba, mba or pba propagated up from
+	    # pba (and one day directly to dba). QT and data mapping info needed.
+	    my ( $qts, $datalist );
+	    ( $bioassay, $qts, $datalist ) = $self->find_or_create_dm_dba(
+		    $listref,
+		    $bag_of,
+		    $identifier_template,
+		);
+	    foreach my $qt   (@$qts) {
+		$source_qts { $qt->getIdentifier   } = $qt;
+	    }
+	    foreach my $data (@$datalist) {
+		$source_data{ $data->getIdentifier } = $data;
+	    }
+	}
+	elsif ( $file->get_data_type() eq $CONFIG->get_RAW_DM_FILE_TYPE() ) {
+
+	    # MBA maps only to PBA although multiple PBAs may be
+	    # combined into a single column.
+	    $bioassay = $self->find_or_create_mba(
+		    $listref,
+		    $bag_of,
+		    $identifier_template,
+		);
+	}
+	else {
+	    croak(sprinf("Error: unknown DataMatrix data_type %s\n",
+			 $file->get_data_type()) );
+	}
+        push( @$ba_list, $bioassay );
+    }
+
+    # Top-level DBA and DBAD. Note that a raw data matrix should
+    # ideally be coded as MBA here, but due to MAGEv1.1 limitations
+    # that's not possible. Instead we create a DBA pointing at the
+    # per-hyb MBAs.
+    $self->generate_top_level_dba_and_dbad(
+	$file,
+	$ba_list,
+	\%source_qts,
+	\%source_data,
+	$datarow,
+	$tab2mage,
+	$order,
+	$identifier_template,
+    );
+
+    return;
+}
+
+sub generate_top_level_dba_and_dbad : PRIVATE {
+
+    my ($self,
+	$file,
+	$ba_list,
+	$source_qts,
+	$source_data,
+	$datarow,
+	$tab2mage,
+	$order,
+	$identifier_template,
+    ) = @_;
+
+    ###################
+    # DerivedBioAssay #
+    ###################
+
+    my $bag_of = $tab2mage->get_bags();
+
+    # Convention dictates that Transformation name is 1:1 with file name.
+    my $trxn_name = $datarow->{$EDF_TRANSFORMATIONNAME} || $file->get_name();
+
+    # BioAssayMap and DBA both have the same $trxn_name internal identifier
+    my $dba = $bag_of->{datamatrix_dba}->(
+        $trxn_name,
+        {   identifier_template => $identifier_template,
+            normalization_type  => $datarow->{$EDF_TRANSFORMATIONTYPE},
+            source_bioassays    => $ba_list,
+	    bioassaymap_bag     => $bag_of->{bam},
+        },
+    );
+
+    # BioAssayDimension and BioAssayMap
+    my $ba_dimension = Bio::MAGE::BioAssayData::BioAssayDimension->new(
+        identifier => "$identifier_template.DerivedBioAssayDimension",
+        bioAssays  => $ba_list,
+    );
+    my $ba_map = $dba->getDerivedBioAssayMap;
+
+    # Propagate the FVs to the new DBA
+    my %fv_list;
+    foreach my $bioassay (@$ba_list) {
+        if ( $bioassay->getBioAssayFactorValues ) {
+            foreach my $fv ( @{ $bioassay->getBioAssayFactorValues } ) {
+                $fv_list{ $fv->getIdentifier } = $fv;
+            }
+        }
+    }
+    $dba->setBioAssayFactorValues( [ values %fv_list ] );
+
+    #######################
+    # DerivedBioAssayData #
+    #######################
+    my $protocol;
+    $datarow->{$EDF_TRXN_PROTOCOL} && do {
+        $protocol = $bag_of->{protocol}->(
+            $datarow->{$EDF_TRXN_PROTOCOL},
+            { protocol_type => $OE_VAL_NORMALIZATION }
+        );
+    };
+
+    my $trxn_software    = q{};
+    my $software_version = q{};
+    $datarow->{$EDF_TRXN_SOFTWARE} && do {
+        my $name;
+        ( $name, $software_version )
+            = $tab2mage->parse_software( $datarow->{$EDF_TRXN_SOFTWARE} );
+        $trxn_software = $bag_of->{software}->(
+            "TRXN:$name" . ( $software_version ? ":$software_version" : q{} ),
+            {   identifier_template => "$identifier_template.Transformation",
+                name                => $name,
+                type                => $OE_VAL_TRANSFORMATION_SOFTWARE,
+            }
+        );
+    };
+
+    # Allow empty ded_type, e.g. for checker graph visualization.
+    my $ded;
+    if ( my $dedtype = $file->get_ded_type() ) {
+	my $dedclass
+	    = "Bio::MAGE::BioAssayData::" . $file->get_ded_type() . "Dimension";
+	$ded = $dedclass->new( identifier => $file->get_ded_identifier() );
+    }
+
+    my @sorted_qts  = map { $source_qts->{$_} } sort keys %$source_qts;
+    my @sorted_data = map { $source_data->{$_} } sort keys %$source_data;
+
+    my $dbad = $bag_of->{datamatrix_dbad}->(
+        $file->get_name(),
+        {   filename                => $file->get_target_filename(),
+            de_dimension            => $ded,
+            qt_dimension            => $file->get_mage_qtd(),
+            ba_dimension            => $ba_dimension,
+            identifier_template     => $identifier_template,
+            protocol                => $protocol,
+            source_bioassay_dataset => \@sorted_data,
+            bioassay_map            => $ba_map,
+            software                => $trxn_software,
+            software_version        => $software_version,
+            order                   => $order,
+            source_qts              => \@sorted_qts,
+            qtm_bag                 => $bag_of->{qtm},
+        }
+    );
+
+    # Add data to the DBA.
+    $tab2mage->dba_add_data( $dba, $dbad );
+
+    return;
+}
+
+sub mbas_from_pbas : PRIVATE {
+
+    # Get a list of MBAs which were derived from a given list of PBAs.
+    my ( $self, $pbas, $bag_of ) = @_;
+    my @mbas;
+
+    foreach my $pba (@$pbas) {
+
+	# Handle the two-colour hyb coding. Assumes that merged PBAs
+	# have a .merged name suffix.
+	if (    $pba->getBioAssayCreation()
+	     && ( scalar @{ $pba->getChannels() || [] } > 1 ) ) {
+
+	    # FIXME we want to follow the BioAssayTreatment chain
+	    # here, instead of relying on naming conventions.
+	    my $pba_name = $pba->getName();
+	    $pba = $bag_of->{extended_pba}->(
+		$pba->getName() . ".merged"
+	    );
+	    unless ( $pba ) {
+		die(sprintf(
+		    "Internal error: Merged PBA undefined for two-color coding (%s).",
+		    $pba_name)
+		);
+	    }
+	}
+
+        foreach my $bioassay ( @{ $bag_of->{mba}->() } ) {
+            my $fext = $bioassay->getFeatureExtraction;
+            if ( $fext->getPhysicalBioAssaySource()->getIdentifier eq
+                $pba->getIdentifier ) {
+                push( @mbas, $bioassay );
+            }
+        }
+    }
+    return \@mbas;
+}
+
+sub dbas_from_mbas : PRIVATE {
+
+    # Get a list of non-DataMatrix DBAs derived from a given list of MBAs.
+    my ( $self, $mbas, $bag_of ) = @_;
+    my @dbas;
+
+    foreach my $mba (@$mbas) {
+        foreach my $dba ( @{ $bag_of->{dba}->() } ) {
+            my @bams = @{ $dba->getDerivedBioAssayMap() || [] };
+            foreach my $bam (@bams) {
+                foreach my $source ( @{ $bam->getSourceBioAssays || [] } ) {
+                    if ( $source->getIdentifier eq $mba->getIdentifier ) {
+                        push( @dbas, $dba );
+                    }
+                }
+            }
+        }
+    }
+    return \@dbas;
+}
+
+sub retrieve_bioassay : RESTRICTED {
+
+    # Here we check for the existence of a bioassay with internal key
+    # $heading_hyb, and die if there isn't. This prevents a much
+    # messier crash later on. Return the MAGE object if found.
+
+    my ( $self, $heading_hyb, $ba_bag, $id_column, $fallback_bag ) = @_;
+
+    $id_column ||= 'hybridization/normalization ID';
+
+    # We don't want errors here to email the autosubmissions system admin.
+    my $sighandler = $SIG{__DIE__};
+    delete $SIG{__DIE__};
+
+    my $bioassay = $ba_bag->( $heading_hyb );
+
+    # Fallback if possible (e.g. MAGE-TAB mapping to Scan for
+    # single-hyb experiments).
+    if ( ! $bioassay && $fallback_bag ) {
+	$bioassay = $self->find_pba_for_bat( $heading_hyb, $fallback_bag );
+    }
+    unless ( $bioassay ) {
+	croak("ERROR: unrecognized data matrix $id_column:"
+	    . " $heading_hyb\n");
+    }
+
+    # Reinstate the __DIE__ signal handler.
+    $SIG{__DIE__} = $sighandler if $sighandler;
+
+    return $bioassay;
+}
+
+sub find_pba_for_bat : PRIVATE {
+
+    my ( $self, $heading_hyb, $bag ) = @_;
+
+    foreach my $bioassay ( @{ $bag->() } ) {
+	foreach my $bat ( @{ $bioassay->getBioAssayTreatments() || [] } ) {
+
+	    # Success.
+	    return $bioassay if ( $bat->getName() eq $heading_hyb );
+	}
+    }
+    return;   # Failure.
+}
+
+sub find_or_create_mba : PRIVATE {
+
+    my ( $self, $hybs, $bag_of, $identifier_template ) = @_;
+
+    ref $hybs eq 'ARRAY' or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    ref $bag_of eq 'HASH' or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    ref $identifier_template
+        and confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my %contributing_bioassays;
+
+    my %overall_pba;
+
+    foreach my $heading_hyb (@$hybs) {
+
+	my $pba = $self->retrieve_bioassay( $heading_hyb, $bag_of->{pba} );
+	$overall_pba{ $pba->getName() } = $pba;
+    }
+
+    # Either return the column bioassay, or a new ba mapping to
+    # multiple column bioassays.
+    my $datamatrix_bioassay = $self->find_or_create_per_column_mba(
+	[ values %overall_pba ],
+	$identifier_template,
+	$bag_of,
+    );
+
+    return $datamatrix_bioassay;
+}
+
+sub find_or_create_dm_dba : PRIVATE {
+
+    my ( $self, $hybs, $bag_of, $identifier_template ) = @_;
+
+    ref $hybs eq 'ARRAY' or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    ref $bag_of eq 'HASH' or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    ref $identifier_template
+        and confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my %contributing_bioassays;
+
+    my %known_pba_names = map { $_->getName => 1 } @{ $bag_of->{pba}->() };
+
+    # We need to propagate PBA object up to top-level MBA/DBA for
+    # correct mapping.
+    my @overall_mbas;
+    foreach my $heading_hyb (@$hybs) {
+
+	my $pba;
+
+	# This may fail if the data matrix maps to Normalizations
+	# rather than Hybridizations.
+	eval {
+	    $pba = $self->retrieve_bioassay( $heading_hyb, $bag_of->{pba} );
+	};
+
+	if ($EVAL_ERROR) {
+
+	    # Try again with DBAs if PBAs no good. We now allow this
+	    # to croak on failure.
+	    my $dba = $self->retrieve_bioassay( $heading_hyb, $bag_of->{dba} );
+	    $contributing_bioassays{ $dba->getIdentifier } = $dba;
+	}
+	else {
+
+	    # Get lists of MBAs and/or DBAs linked to the PBA. N.B. Any
+	    # DBAs returned here should be certified non-DataMatrix.
+	    my $mbas = $self->mbas_from_pbas( [$pba], $bag_of );
+	    my $dbas = $self->dbas_from_mbas( $mbas, $bag_of );
+
+	    push( @overall_mbas, @$mbas );
+
+	    # Get a uniqued list of the highest-level bioassays possible
+	    if ( scalar(@$dbas) ) {
+		foreach my $bioassay (@$dbas) {
+		    $contributing_bioassays{ $bioassay->getIdentifier }
+			= $bioassay;
+		}
+	    }
+	    elsif ( scalar(@$mbas) ) {
+		foreach my $bioassay (@$mbas) {
+		    $contributing_bioassays{ $bioassay->getIdentifier }
+			= $bioassay;
+		}
+	    }
+	    else {
+		$contributing_bioassays{ $pba->getIdentifier } = $pba;
+	    }
+	}
+    }
+
+    # Get a list of QTs to use in QuantitationTypeMap(ping).
+    my ( $source_qts, $source_data )
+        = $self->get_bioassay_qts( [ values %contributing_bioassays ] );
+
+    # If no QTs returned from the top level, try again with all the MBAs
+    # we found.
+    unless (@$source_qts) {
+        ( $source_qts, $source_data )
+	    = $self->get_bioassay_qts( \@overall_mbas );
+    }
+
+    # Either return the column bioassay, or a new ba mapping to
+    # multiple column bioassays.
+    my $datamatrix_bioassay = $self->find_or_create_per_column_bioassay(
+	[ values %contributing_bioassays ],
+	$identifier_template,
+	$bag_of->{datamatrix_dba},  # future derived matrices can't map to these.
+	$bag_of->{bam},
+    );
+
+    return ( $datamatrix_bioassay, $source_qts, $source_data );
+}
+
+sub find_or_create_per_column_mba : PRIVATE {
+
+    # Get a list of MBAs from the PBAs; pass this on up to
+    # find_or_create_per_column_bioassay to either return a single
+    # MBA or a DBA linked to multiple MBAs (e.g. from multiple scans).
+    my ( $self,
+	 $pbas,
+	 $identifier_template,
+	 $bag_of,
+     ) = @_;
+
+    if ( scalar( @$pbas ) > 1 ) {
+
+	# This is a bit of a MAGE limitation - we'd need to come up
+	# with some kind of PBA-based hack to support this. These
+	# cases should be using DerivedBioAssays anyway.
+	croak(
+	    "Error: Cannot map to multiple hybridizations in a single data matrix column."
+	);
+    }
+    elsif ( scalar( @$pbas ) != 1 ) {
+
+	# This should be unusual, we crash earlier in such cases.
+	croak(
+	    "Error: Cannot retrieve PBA information."
+	);
+    }
+
+    my $mbas = $self->mbas_from_pbas( $pbas, $bag_of );
+
+    # Check that @$mbas has entries, if not create one and return it.
+    my $bioassay;
+
+    if ( scalar @$mbas ) {
+
+	$bioassay = $self->find_or_create_per_column_bioassay(
+	    $mbas,
+	    $identifier_template,
+	    $bag_of->{dba},    # future derived matrices can map to these.
+	    $bag_of->{bam},
+	);
+    }
+    else {
+
+	# MBA creation here.
+	my $unique = unique_identifier();
+	my $pba    = $pbas->[0];
+	my $mba    = $bag_of->{mba}->(
+	    $pba->getName(),
+	    {
+		identifier_template => $unique,
+		physical_bioassay   => $pba,
+	    }
+	);
+
+	# Propagate FVs from the PBA.
+	$self->update_bioassay_fvs(
+	    $mba,
+	    $pba->getBioAssayFactorValues(),
+	);
+
+	$bioassay = $mba;
+    }
+
+    return $bioassay;
+}
+
+sub find_or_create_per_column_bioassay : RESTRICTED {
+
+    my ( $self,
+	 $source_bioassays,
+	 $identifier_template,
+	 $bioassay_bag,
+	 $bam_bag,
+     ) = @_;
+
+    # $datamatrix_bioassay is the returned bioassay. It will be either a
+    # single contributing bioassay, or a new bioassay mapping to
+    # multiple contributing bioassays.
+    my $datamatrix_bioassay;
+
+    # Create BAM from @$source_bioassays unless there's only
+    # one bioassay involved.
+    if ( scalar( @$source_bioassays ) == 1 ) {
+
+        # Only one bioassay; simple case
+        $datamatrix_bioassay = $source_bioassays->[0];
+    }
+
+    else {
+
+        # Multiple bioassays specified
+        my $unique = unique_identifier;
+        my @ba_names;
+        foreach my $bioassay ( @$source_bioassays ) {
+            push( @ba_names, $bioassay->getName() );
+        }
+        my $name = join( ";", @ba_names ) || "DataMatrix.$unique";
+        my $identifier = "$identifier_template.$unique";
+        $datamatrix_bioassay = $bioassay_bag->(
+            $name,
+            {   identifier_template => $identifier,
+                source_bioassays    => $source_bioassays,
+		bioassaymap_bag     => $bam_bag,
+            },
+        );
+
+        # Propagate FVs
+        foreach my $bioassay ( @$source_bioassays ) {
+            $self->update_bioassay_fvs( $datamatrix_bioassay,
+                $bioassay->getBioAssayFactorValues() );
+        }
+    }
+
+    return $datamatrix_bioassay;
+}
+
+sub update_bioassay_fvs : RESTRICTED {
+
+    # Wrapper method for updating the FVs associated with a bioassay.
+    my ( $self, $bioassay, $fvs ) = @_;
+
+    update_factorvalues( $bioassay, $fvs );
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Metrics.pm b/lib/ArrayExpress/Datafile/Metrics.pm
new file mode 100644
index 0000000..909326c
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Metrics.pm
@@ -0,0 +1,195 @@
+#!/usr/bin/env perl
+#
+# DataMetrics.pm - a module derived from and used in the experiment
+# checker script. Contains routines which might be useful elsewhere.
+#
+# Tim Rayner 2005 ArrayExpress team, EBI
+#
+# $Id: Metrics.pm 1852 2007-12-13 10:14:27Z tfrayner $
+#
+
+package ArrayExpress::Datafile::Metrics;
+
+use strict;
+use warnings;
+
+use Carp;
+use Scalar::Util qw(looks_like_number);
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+use base 'Exporter';
+our @EXPORT_OK = qw(
+    check_data_metrics
+    calculate_benford
+    pearson_correlation
+);
+
+## Correlation subroutine originally written by Greg Grant from RAD
+sub pearson_correlation {
+
+    my ( $hash1, $hash2 ) = @_;
+
+    ref $hash1 eq 'HASH' or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    ref $hash2 eq 'HASH' or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my @vector1;
+    my @vector2;
+    my $correlation;
+
+    my $i = 0;
+
+    # If both datasets have a value that looks like a number, use it in
+    # the correlation calculation.
+    foreach my $key ( keys %$hash2 ) {
+        if (   defined $hash1->{$key}
+            && looks_like_number( $hash1->{$key} )
+            && looks_like_number( $hash2->{$key} ) ) {
+            $vector1[$i] = $hash1->{$key};
+            $vector2[$i] = $hash2->{$key};
+            $i++;
+        }
+    }
+
+    my $vector_length = $i - 1;
+
+    my $xy = 0;
+    my $x  = 0;
+    my $y  = 0;
+    my $xx = 0;
+    my $yy = 0;
+
+    for ( my $i = 0; $i < $vector_length; $i++ ) {
+        $xy = $xy + $vector1[$i] * $vector2[$i];
+        $x  = $x + $vector1[$i];
+        $y  = $y + $vector2[$i];
+        $xx = $xx + ( $vector1[$i] )**2;
+        $yy = $yy + ( $vector2[$i] )**2;
+    }
+
+    if (sqrt(
+            ( $vector_length * $xx - $x * $x )
+            * ( $vector_length * $yy - $y * $y )
+        ) != 0
+        ) {
+
+        $correlation = ( $vector_length * $xy - $x * $y ) / (
+            sqrt(
+                ( $vector_length * $xx - $x * $x )
+                * ( $vector_length * $yy - $y * $y )
+            )
+        );
+
+    }
+    else {
+
+        $correlation = -2;
+
+    }
+
+    return $correlation;
+
+}
+
+sub calculate_benford {
+
+    my $data_metrics = shift;
+
+    ref $data_metrics eq 'HASH'
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my @benford;
+    foreach my $heading ( keys %$data_metrics ) {
+        for ( my $digit = 1; $digit < 10; $digit++ ) {
+            $benford[$digit] +=
+                $data_metrics->{$heading}{benford}
+                ? vec( $data_metrics->{$heading}{benford}, $digit, 32 )
+                : 0;
+        }
+    }
+
+    my $bentotal = 0;
+    foreach my $digitcount ( @benford[ 1 .. 9 ] ) {
+        $bentotal += $digitcount if $digitcount;
+    }
+    my $benval = $bentotal
+        ? ( $benford[1] / $bentotal ) * 100
+        : 'N/A';   # Avoid illegal division by zero if we've got real problems
+
+    return $benval;
+
+}
+
+sub check_data_metrics {
+
+    my $data_metrics = shift;
+
+    ref $data_metrics eq 'HASH'
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    # Do some checks on the data here.
+    foreach my $header ( keys %$data_metrics ) {
+
+        # Log ratios should be between certain limits.
+        if ($data_metrics->{$header}{scale}
+            && (
+
+                # column heading looks like 'log ratio'
+                ( $header =~ m/log ?ratio/i )
+
+                || (
+
+                    # Or subclass is Ratio...
+                    $data_metrics->{$header}{subclass}
+                    && (( $data_metrics->{$header}{subclass} eq 'Ratio' )
+
+                        # and heading/scale looks like 'log'
+                        && ( ( $header =~ m/\blog/i ) )
+                        || ( $data_metrics->{$header}{scale} =~ m/^log_/i )
+                    )
+                )
+            )
+            ) {
+
+            LOGRATIOSCALECHECK:
+            {
+                my $error_message = 'Log ratios outside range';
+
+                ( $data_metrics->{$header}{scale} eq 'log_base_2' ) && do {
+                    $data_metrics->{$header}{errors}{$error_message}++
+
+         # Scanners seem to max out at 100,000 (i.e. log2(100,000) = 17 or so)
+                        if ( ( $data_metrics->{$header}{max} > 20 )
+                        || ( $data_metrics->{$header}{min} < -20 ) );
+                    last LOGRATIOSCALECHECK;
+                };
+
+                ( $data_metrics->{$header}{scale} eq 'log_base_10' ) && do {
+                    $data_metrics->{$header}{errors}{$error_message}++
+
+               # Scanners seem to max out at 100,000 (i.e. log10(100,000) = 5)
+                        if ( ( $data_metrics->{$header}{max} > 6 )
+                        || ( $data_metrics->{$header}{min} < -6 ) );
+                    last LOGRATIOSCALECHECK;
+                };
+
+                # Play it safe. We're unlikely to use this anyway
+                $data_metrics->{$header}{errors}{$error_message}++
+                    if ( ( $data_metrics->{$header}{max} > 1 )
+                    || ( $data_metrics->{$header}{min} < -1 ) );
+            }
+        }
+
+        # Check on any saturation data in the file (e.g. in GenePix files)
+        my $error_message = 'Saturation indicator too high';
+
+        # regexp may need work FIXME
+        if (   ( $header =~ m/\% ?sat/i )
+            && ( $data_metrics->{$header}{max} > 50 ) ) {
+            $data_metrics->{$header}{errors}{$error_message}++;
+        }
+    }
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Parser.pm b/lib/ArrayExpress/Datafile/Parser.pm
new file mode 100644
index 0000000..262ba33
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Parser.pm
@@ -0,0 +1,2016 @@
+#!/usr/bin/env perl
+#
+# Module to provide basic methods used by the tab2mage script.
+#
+# Tim Rayner 2005, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Parser.pm 2069 2008-06-04 14:33:52Z tfrayner $
+#
+
+package ArrayExpress::Datafile::Parser;
+
+use strict;
+use warnings;
+
+use Class::Std;
+use Digest::MD5 qw( md5 );    # Used for tracking DEDs
+use IO::File;
+use File::Copy;
+use Carp;
+use Readonly;
+use Scalar::Util qw( openhandle );
+use List::Util qw(first);
+use List::MoreUtils qw( any );
+use English qw( -no_match_vars );
+use Storable qw(store_fd fd_retrieve);
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+use ArrayExpress::Datafile::QT_list qw(get_QTs);
+
+use ArrayExpress::Curator::Database qw(
+    retrieve_AE_featurelist
+    map_affy_accno_to_name
+);
+
+use ArrayExpress::Curator::MAGE::Definitions qw(
+    $EDF_EXPTACCESSION
+    $EDF_EXPTDOMAIN
+    $EDF_EXPTNAME
+    $EDF_DYE
+    $EDF_HYB_PROTOCOL
+    $EDF_SCAN_PROTOCOL
+    $EDF_FEXT_PROTOCOL
+    $EDF_NORM_PROTOCOL
+    $EDF_SCAN_SOFTWARE
+    $EDF_FEXT_SOFTWARE
+    $EDF_NORM_SOFTWARE
+    $EDF_IMAGE_FORMAT
+    $EDF_ARRAYSERIAL
+    $EDF_FILE_PREFIX
+    $EDF_FILE_SUFFIX
+    $EDF_HYB_PARAMS
+    $EDF_SCAN_PARAMS
+    $EDF_FEXT_PARAMS
+    $EDF_NORM_PARAMS
+    $EDF_FEXT_STATS
+    $EDF_NORM_STATS
+    $EDF_HYB_HARDWARE
+    $EDF_SCAN_HARDWARE
+    $EDF_HYB_HW_PARAMS
+    $EDF_SCAN_HW_PARAMS
+    $EDF_NORMALIZATIONTYPE
+
+    $OE_VAL_NORMALIZATION
+    $OE_VAL_FEATUREEXTRACTION
+    $OE_VAL_SCANNING
+    $OE_VAL_HYBRIDIZATION
+
+    $AE_LABELCOMPOUND_PREFIX
+    $AE_CHANNEL_PREFIX
+);
+
+use ArrayExpress::Curator::Common qw(
+    get_filepath_from_uri
+    strip_discards
+    clean_hash
+    find_cdf
+    $RE_EMPTY_STRING
+);
+
+Readonly my $AFFY_PROTOCOL_PREFIX  => 'Affymetrix:Protocol:';
+Readonly my $AFFY_PARAMETER_PREFIX => 'Affymetrix:Parameter:';
+
+my %quantitation_types   : ATTR( :get<quantitation_types>,   :default<undef> );
+my %use_binary_datafiles : ATTR( :get<use_binary_datafiles>, :init_arg<use_binary_datafiles>, :default<undef> );
+my %output_directory     : ATTR( :get<output_directory>,     :init_arg<output_directory>,     :default<q{.}>  );
+my %source_directory     : ATTR( :get<source_directory>,     :init_arg<source_directory>,     :default<q{.}>  );
+my %include_known_qts    : ATTR( :get<include_known_qts>,    :init_arg<include_known_qts>,    :default<undef> );
+my %allow_undef_qts      : ATTR( :get<allow_undef_qts>,      :init_arg<allow_undef_qts>,      :default<undef> );
+my %reporter_prefix      : ATTR( :get<reporter_prefix>,      :init_arg<reporter_prefix>,      :default<undef> );
+my %compseq_prefix       : ATTR( :get<compseq_prefix>,       :init_arg<compseq_prefix>,       :default<undef> );
+my %ignore_size_limits   : ATTR( :name<ignore_size_limits>,  :default<undef> );
+
+my %error_fh             : ATTR( :name<error_fh>,      :default<\*STDOUT> );
+my %clobber              : ATTR( :name<clobber>,       :default<0>        );
+my %namespace            : ATTR( :name<namespace>,     :default<q{}>      );
+my %is_standalone        : ATTR( :name<is_standalone>, :default<undef>    );
+my %feature_fh           : ATTR( :set<feature_fh>,     :default<undef>    );
+my %cel_types            : ATTR( :get<cel_types>,      :default<{}>       );
+my %array_cache          : ATTR( :default<{}> );
+my %cdf_cache            : ATTR( :default<{}> );
+my %ded_cache            : ATTR( :default<{}> );
+
+my %protocol_bag         : ATTR( :get<protocol_bag>,  :init_arg<protocol_bag>,  :default<sub{ die("Error: Parser protocol_bag not initialized.")  }> );
+my %parameter_bag        : ATTR( :get<parameter_bag>, :init_arg<parameter_bag>, :default<sub{ die("Error: Parser parameter_bag not initialized.") }> );
+
+sub START {
+
+    my ($self, $id, $args) = @_;
+
+    # Initialize QTs (this is more complicated than a simple setter
+    # method as provided by Class::Std). This *MUST* be done after
+    # object initialization.
+    $self->set_quantitation_types($args->{quantitation_types});
+
+    return;
+}
+
+sub get_unique_de_dimension {
+
+    my ( $self, $ded_array, $array, $ded_type ) = @_;
+
+    ref $ded_array eq 'ARRAY'
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    ref $array    and confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    ref $ded_type and confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    # We use an md5 hash string here rather than the full DED to save
+    # on memory.
+    my $md5 = Digest::MD5->new();
+    foreach my $de_id (@$ded_array) { $md5->add($de_id); }
+    my $ded_md5 = $md5->digest;
+
+    # Tracker hash format: $ded_cache = {$ded_md5 => $ded_identifier}
+    # If we've already seen this DED, use the old $ded_identifier and
+    # skip adding it again.
+    my $ded_identifier;
+
+    unless ( $ded_identifier = $self->get_ded_cache($ded_md5) ) {
+
+        $ded_identifier = $self->set_ded_cache(
+	    $ded_md5,
+	    $ded_array,
+	    $array,
+	    $ded_type,
+	);
+
+    }
+
+    return wantarray ? ( $ded_identifier, $ded_type ) : $ded_identifier;
+
+}
+
+sub parse {
+
+    my ( $self, $file, $datarow ) = @_;
+
+    $datarow ||= {};
+
+    $file->isa('ArrayExpress::Datafile')
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    # recheck the file; some time may have elapsed
+    unless ( -e $file->get_path() ) {
+        croak(    "ERROR: Missing data file: "
+                . $file->get_path()
+                . ". This script is aborting.\n" );
+    }
+
+    print STDOUT ( "Processing file " . $file->get_name() . "\n" );
+
+    # Set the error filehandle on Datafile to redirect warnings/errors.
+    $file->set_error_fh( $self->get_error_fh() );
+
+    # Draw a line in the log file.
+    my $filler_length = 80 - ( 3 + 2 + length( $file->get_name() ) );
+    print { $self->get_error_fh() }
+        (     q{-} x 3 . q{ }
+            . $file->get_name() . q{ }
+            . q{-} x $filler_length
+            . qq{\n} );
+
+    # Test for file size
+    if ( ( -s $file->get_path() > $CONFIG->get_MAX_DATAFILE_SIZE() )
+         && ! $self->get_ignore_size_limits() ) {
+
+        # Set some needed parameters
+        if ( $file->get_data_type eq 'raw' ) {
+            $file->set_ded_type('Feature');
+        }
+        else {
+            $file->set_ded_type('Reporter');
+        }
+        $file->set_ded_identifier(
+            '+++++++++++++++++++++ERROR++++++++++++++++++++++');
+
+        # Alert the user
+        my $error_msg = "\n    *** PARSE ERROR: File "
+            . $file->get_name()
+            . " is too large to be processed safely.\n"
+            . "    *** This file will be skipped, but a dummy BioAssayData object will be created.\n\n";
+        print { $self->get_error_fh() } ($error_msg);
+        print STDERR ($error_msg);
+    }
+
+    # If the file is small enough (usually it will be) we process it here.
+    elsif ( $file->get_is_binary() || ( $file->get_path() =~ m/\. (CEL|CHP) \z/ixms ) ) {
+
+	# Deal with Affy files.
+        $self->_parse_affy_datafile( $file, $datarow );
+    }
+
+    else {                          # Text files, including Affy CELv3.
+        $self->_parse_text_datafile( $file, $datarow );
+    }
+
+    return;
+
+}
+
+####################
+# Accessor methods #
+####################
+
+sub get_feature_fh {
+
+    # defaults to temporary filehandle on first use (i.e. not on
+    # object initialization).
+    my ( $self ) = @_;
+
+    $feature_fh{ident $self} ||= IO::File->new_tmpfile;
+
+    return $feature_fh{ident $self};
+}
+
+sub get_ded_count {
+
+    # Method to return the number of DEDs held in the DED
+    # tracker. Used to detect whether DED processing should be invoked
+    # following MAGE-ML generation.
+    my $self = shift;
+
+    return scalar(grep { defined $_ } values %{ $ded_cache{ident $self} });
+}
+
+###################
+# Private methods #
+###################
+
+sub set_quantitation_types : PRIVATE {
+
+    my ( $self, $qtfile ) = @_;
+
+    if ( $qtfile || ! $quantitation_types{ident $self} ) {
+        $quantitation_types{ident $self}
+            = get_QTs( $qtfile, $self->get_include_known_qts() );
+    }
+
+    return $quantitation_types{ident $self};
+}
+
+sub set_cel_types : PRIVATE {
+
+    my ( $self, %new_cels ) = @_;
+
+    foreach my $new_key ( keys %new_cels ) {
+        ref $new_key and confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+        ref $new_cels{$new_key}
+            and confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+        $cel_types{ident $self}{$new_key} = $new_cels{$new_key};
+    }
+
+    return $cel_types{ident $self};
+}
+
+sub set_array_cache : PRIVATE {
+
+    # Requires an array id and featurelist hashref
+    my ( $self, $array, $featurelist ) = @_;
+
+    $array or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    ref $featurelist eq 'HASH'
+	   or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my $fh = IO::File->new_tmpfile();
+
+    store_fd( $featurelist, $fh )
+	or confess("Error caching array feature list: $!");
+    seek($fh, 0, 0) or confess("Error rewinding array cache file: $!");
+
+    $array_cache{ident $self}{$array} = $fh;
+
+    return $array_cache{ident $self}{$array};
+}
+
+sub get_array_cache : PRIVATE {
+
+    # Requires an array id
+    my ( $self, $array ) = @_;
+
+    $array or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    # Return undef if array not in cache.
+    my $featurelist;
+    if ( my $fh = $array_cache{ident $self}{$array} ) {
+
+	my $pos = tell($fh);
+
+	seek($fh, 0, 0) or confess("Error rewinding array cache file: $!");
+
+	$featurelist = fd_retrieve( $fh )
+	    or confess("Error retrieving cached array feature list: $!");
+
+	seek($fh, $pos, 0) or confess("Error resetting array cache file: $!");
+    }
+
+    return $featurelist;
+}
+
+sub set_cdf_cache : PRIVATE {
+
+    # Requires a CDF id/filename and cdffile object
+    my ( $self, $cdffile, $cdf ) = @_;
+
+    $cdffile or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    $cdf->isa('ArrayExpress::Datafile::Affymetrix::CDF')
+             or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    $cdf_cache{ident $self}{$cdffile} = $cdf;
+    return $cdf_cache{ident $self}{$cdffile};
+}
+
+sub get_cdf_cache : PRIVATE {
+
+    # Requires a CDF id/filename.
+    my ( $self, $cdffile ) = @_;
+
+    $cdffile or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    return $cdf_cache{ident $self}{$cdffile};
+}
+
+sub set_ded_cache : PRIVATE {
+
+    my ( $self, $ded_md5, $ded_array, $array, $ded_type ) = @_;
+
+    defined($ded_md5)  or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    defined($array)    or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    defined($ded_type) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    ref $ded_array eq 'ARRAY'
+	or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my $inverted_tracker;    # values to keys; we drop the ded_md5 keys
+    foreach my $value ( values %{ $ded_cache{ident $self} } ) {
+	$inverted_tracker->{$value}++;
+    }
+
+    # Determine the DesignElementDimension identifier
+    my $i = 0;
+    my $ded_identifier;
+    while ( !$ded_identifier || $inverted_tracker->{$ded_identifier} ) {
+	$ded_identifier = $self->get_namespace()
+	    . ".$array."
+            . ( ++$i ) . "."
+	    . $ded_type
+            . "Dimension";
+    }
+
+    $ded_cache{ident $self}{$ded_md5} = $ded_identifier;
+
+    $self->_append_designelementdimension(
+	$ded_array,
+	$ded_identifier,
+	$ded_type,
+    );
+
+    return $ded_cache{ident $self}{$ded_md5};
+
+}
+
+sub get_ded_cache : PRIVATE {
+
+    my $self    = shift;
+    my $ded_md5 = shift;
+
+    defined($ded_md5) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    return $ded_cache{ident $self}{$ded_md5};
+}
+
+sub _create_identifier_ded : PRIVATE {
+
+    my ( $self, $file, $indexcols, $output_fh, $array_id ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $file->get_linebreak_type();
+
+    my $input_fh = $file->get_filehandle()
+	or croak("Error retrieving filehandle.");
+
+    my $first_index_column = $file->get_column_headings()->[ $indexcols->[0] ];
+
+    my ( $ded_array, $ded_type, $de_prefix, $de_separator );
+
+    # Fix Affymetrix identifiers (this is AE-specific).
+    my $compseq_prefix  = $self->get_compseq_prefix();
+    my $reporter_prefix = $self->get_reporter_prefix();
+    my $accno           = $file->get_array_design_id();
+    if ( $accno && $accno =~ m/\A A-AFFY-\w+ \z/xms ) {
+	my $affyname = map_affy_accno_to_name($accno);
+	unless (defined $reporter_prefix) {
+	    $reporter_prefix = "Affymetrix:Reporter:$affyname:";
+	}
+	unless (defined $compseq_prefix) {
+	    $compseq_prefix = "Affymetrix:CompositeSequence:$affyname:";
+	}
+    }
+
+    # Only one index column suggests a Reporter/CS identifier column
+    if (   $first_index_column =~ m/Probe ?(Set)? ?(Name|ID)/i
+	|| $file->get_dm_chip_type() ) {
+
+	# Post-Affy normalized data
+	$de_prefix = defined( $compseq_prefix )
+	           ? $compseq_prefix
+		   : "Affymetrix:CompositeSequence:$array_id:";
+	
+	$ded_array = $self->_extract_designelementdimension(
+	    $input_fh,
+	    $output_fh,
+	    $indexcols,
+	    $de_prefix,
+	);
+	$ded_type  = 'CompositeSequence';
+    }
+
+    elsif ( $first_index_column =~ m/Composite *Sequence *Identifier/i ) {
+
+	$de_prefix = defined( $compseq_prefix )
+	           ? $compseq_prefix
+		   : "ebi.ac.uk:MIAMExpress:CompositeSequence:$array_id.";
+
+	$ded_array = $self->_extract_designelementdimension(
+	    $input_fh,
+	    $output_fh,
+	    $indexcols,
+	    $de_prefix,
+	);
+	$ded_type = 'CompositeSequence';
+    }
+
+    elsif ( $first_index_column =~ m/Reporter *Identifier/i
+         || $first_index_column eq 'ID_REF' ) {
+
+	$de_prefix = defined( $reporter_prefix )
+	           ? $reporter_prefix
+		   : "ebi.ac.uk:MIAMExpress:Reporter:$array_id.";
+
+	$ded_array = $self->_extract_designelementdimension(
+	    $input_fh,
+	    $output_fh,
+	    $indexcols,
+	    $de_prefix,
+	);
+	$ded_type = 'Reporter';
+    }
+
+    else {    # Unknown is flagged as a fatal error downstream
+
+	$ded_array = $self->_extract_designelementdimension(
+	    $input_fh,
+	    $output_fh,
+	    $indexcols,
+	    'Unknown.',
+	);
+	$ded_type = 'Unknown';
+    }
+
+    return ( $ded_array, $ded_type );
+}
+
+sub _create_feature_ded : PRIVATE {
+
+    my ( $self, $file, $indexcols, $output_fh, $array_id ) = @_;
+
+    local $INPUT_RECORD_SEPARATOR = $file->get_linebreak_type();
+
+    my $input_fh = $file->get_filehandle()
+	or croak("Error retrieving filehandle.");
+
+    my $ded_type     = 'Feature';
+    my $de_prefix    = "ebi.ac.uk:MIAMExpress:Feature:$array_id.";
+    my $ded_array;
+
+    if ( $self->get_is_standalone() ) {
+
+	# In standalone mode, we make up our own identifiers using the
+	# MIAMExpress convention.
+	$ded_array = $self->_extract_designelementdimension(
+	    $input_fh,
+	    $output_fh,
+	    $indexcols,
+	    $de_prefix,
+	);
+    }
+
+    else {
+
+	# Get the array feature info from the public ArrayExpress
+	# database.  Attempt to get features into the array cache, if
+	# we've not done so before.
+	my $featurelist;
+	unless ( $featurelist = $self->get_array_cache($array_id) ) {
+
+	    # Note that this call may croak if AE is down;
+	    # however, death here is the preferred outcome in such
+	    # cases (otherwise we will probably generate the wrong
+	    # feature ids). May revisit this later FIXME.
+	    $featurelist = retrieve_AE_featurelist($array_id);
+
+	    if ($featurelist) {
+		$self->set_array_cache( $array_id, $featurelist );
+	    }
+	    else {
+		print STDERR (
+		    "Warning: Unable to retrieve feature information from"
+		  . " ArrayExpress. Falling back to default Feature identifier format.\n"
+	      );
+	    }
+	}
+
+	# If the array is known to us, use the AE feature identifiers
+	if ( $featurelist ) {
+	    my $coord_array
+		= $self->_extract_feature_coord_strings(
+		    $input_fh,
+		    $output_fh,
+		    $indexcols,
+		);
+
+	    foreach my $coord (@$coord_array) {
+		my $feature_id;
+		unless ( $feature_id = $featurelist->{$coord} ) {
+
+                    # This is most likely a dummy array; dying here would just
+                    # be irritating. We create a dummy identifier instead, at
+                    # risk of failing to validate the output MAGE-ML against
+                    # the database. A better way to handle dummy arrays in AE
+                    # is needed to fix this kludge.
+
+		    print STDERR (
+			"WARNING: Feature with coordinates $coord not found for array $array_id. ",
+			"A generic dummy feature identifier will be created.\n"
+		    );
+
+		    $feature_id = "${de_prefix}${coord}";
+		}
+
+		# Construct the DED
+		push( @$ded_array, $feature_id );
+	    }
+	}
+
+	# If the array is not found in AE, we make up our own
+	# identifiers using the MIAMExpress convention.
+	else {
+	    $ded_array = $self->_extract_designelementdimension(
+		$input_fh,
+		$output_fh,
+		$indexcols,
+		$de_prefix,
+	    );
+	}
+    }
+
+    return ( $ded_array, $ded_type );
+}
+
+sub _create_designelementdimension : PRIVATE {
+
+    my ( $self, $file, $output_fh, $array_id ) = @_;
+
+    $file->isa('ArrayExpress::Datafile')
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my ( $ded_array, $ded_type );
+
+    my @indexcols = @{ $file->get_index_columns() };
+
+    # Set the DE type and identifier prefix. We default to MIAMExpress
+    # format here, as that is the commonest route for array submissions
+    # linked to Tab2MAGE.
+    if ( scalar @indexcols == 1 ) {
+
+	( $ded_array, $ded_type )
+	    = $self->_create_identifier_ded(
+		$file,
+		\@indexcols,
+		$output_fh,
+		$array_id,
+	    );
+
+    }
+    else {    # Normal MC/MR/C/R features
+
+	( $ded_array, $ded_type )
+	    = $self->_create_feature_ded(
+		$file,
+		\@indexcols,
+		$output_fh,
+		$array_id,
+	    );
+    }
+
+    my $ded_identifier
+        = $self->get_unique_de_dimension(
+	    $ded_array,
+	    $array_id,
+	    $ded_type,
+	);
+
+    # Fix up the Datafile object
+    $file->set_ded_identifier($ded_identifier);
+    $file->set_ded_type($ded_type);
+
+    # Strip out the index column information - the file columns have
+    # been stripped out by extract_designelementdimension.
+    my @original_headings = @{ $file->get_column_headings() };
+    my @fixed_headings;
+    foreach my $index ( 0 .. $#original_headings ) {
+        unless ( any { $index == $_ } @indexcols ) {
+            push @fixed_headings, $original_headings[$index];
+        }
+    }
+    $file->set_column_headings( \@fixed_headings );
+    $file->set_heading_qts( \@fixed_headings );
+    $file->set_index_columns( [] );
+    $file->set_filehandle( $output_fh );
+
+    return;
+}
+
+sub _append_designelementdimension : PRIVATE {
+
+    my ( $self, $ded_array, $ded_identifier, $ded_type ) = @_;
+
+    ref $ded_array eq 'ARRAY'
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    ref $ded_identifier and confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    ref $ded_type       and confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my $tmpfh = $self->get_feature_fh();
+
+    my $assnref_type
+        = ( $ded_type eq 'Feature' ) ? 'ContainedFeatures' : $ded_type . 's';
+
+    # seek to the end of the file
+    seek( $tmpfh, 0, 2 )
+        or croak(
+        "Error finding DesignElementDimension filehandle EOF to append: $!\n"
+        );
+
+    print $tmpfh (
+        qq{      <${ded_type}Dimension identifier="$ded_identifier">\n});
+
+    print $tmpfh ( qq{        <${assnref_type}_assnreflist>\n});
+
+    foreach my $designelement_id (@$ded_array) {
+        print $tmpfh (
+            qq{          <${ded_type}_ref identifier="$designelement_id"/>\n}
+        );
+    }
+
+    print $tmpfh ( qq{        </${assnref_type}_assnreflist>\n});
+
+    print $tmpfh ( qq{      </${ded_type}Dimension>\n});
+
+    # Not really necessary at this point, just here for future safety.
+    $self->set_feature_fh($tmpfh);
+
+    return;
+}
+
+sub _extract_designelementdimension : PRIVATE {
+
+    my ( $self,
+	 $input_fh,
+	 $output_fh,
+	 $indexcols,
+	 $prefix, ) = @_;
+
+    ref $indexcols eq 'ARRAY'
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    openhandle($input_fh)  or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    openhandle($output_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my $ded_array = $self->_extract_feature_coord_strings(
+	$input_fh,
+	$output_fh,
+        $indexcols,
+    );
+
+    @$ded_array = map {"${prefix}${_}"} @$ded_array;
+
+    return ($ded_array);
+
+}
+
+sub _extract_feature_coord_strings : PRIVATE {
+
+    my ( $self, $input_fh, $output_fh, $indexcols ) = @_;
+
+    ref $indexcols eq 'ARRAY'
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    openhandle($output_fh)
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my $coord_array = [];
+
+    DATA_LINE:
+    while ( my $line = <$input_fh> ) {
+
+        $line =~ s/[\r\n]*$//;
+
+	# Empty lines are skipped during data export, so we skip them
+	# here also.
+        if ( $line =~ $RE_EMPTY_STRING ) {
+            next DATA_LINE;
+        }
+
+        my @line_array = split /\t/, $line;
+
+        push( @$coord_array, ( join( ".", @line_array[@$indexcols] ) ) );
+
+        my $new_line = strip_discards( $indexcols, \@line_array );
+
+        print $output_fh ( join( "\t", @$new_line ), "\n" );
+
+    }
+
+    return ($coord_array);
+
+}
+
+sub _mage_param_hash : PRIVATE {
+
+    # Converts a hash of parameter values from the Affy parsers into a
+    # parameter hash suitable for feeding into MAGE::new_protocol().
+    my ( $param_values ) = @_;
+
+    my %param_hash =
+        map { $_ => { identifier => "${AFFY_PARAMETER_PREFIX}$_",
+		      name       => $_,                           } }
+        keys %$param_values;
+    return \%param_hash;
+}
+
+sub _extract_chp_data : PRIVATE {
+
+    my ( $self, $chp, $datarow ) = @_;
+
+    # Sort out the protocols
+    $datarow->{$EDF_NORM_PROTOCOL}
+	= "${AFFY_PROTOCOL_PREFIX}" . $chp->get_algorithm();
+
+    # FIXME these won't always be the case.
+    if ( $chp->get_version == 12 ) {
+	$datarow->{$EDF_NORM_SOFTWARE}
+	    ||= 'Affymetrix MicroArraySuite (5.0)';
+    }
+    if ( $chp->get_version == 8 ) {
+	$datarow->{$EDF_NORM_SOFTWARE}
+	    ||= 'Affymetrix MicroArraySuite (4.0)';
+    }
+    else {
+	$datarow->{$EDF_NORM_SOFTWARE}
+	    ||= 'Affymetrix GeneChip Operating Software (1.2)';
+    }
+
+    # Parameters
+    my %chp_params;
+    foreach my $param ( keys %{ $chp->get_parameters() } ) {
+	$chp_params{"${AFFY_PARAMETER_PREFIX}$param"}
+	    = $chp->get_parameters()->{$param};
+    }
+    $datarow->{$EDF_NORM_PARAMS} = \%chp_params;
+
+    # Make sure the protocol params are declared in any
+    # generated MAGE-ML.
+    $self->get_protocol_bag()->(
+	$datarow->{$EDF_NORM_PROTOCOL},
+	{   protocol_type => $OE_VAL_NORMALIZATION,
+	    name          => $chp->get_algorithm(),
+	    parameters    => _mage_param_hash( $chp->get_parameters() ),
+	    parameter_bag => $self->get_parameter_bag(),
+	}
+    );
+
+    # QC Stats
+    $datarow->{$EDF_NORM_STATS} = $chp->get_stats();
+
+    # DerivedBioAssayType. This is probably not foolproof FIXME
+    DBA_TYPE:
+    {
+	( $chp->get_algorithm() =~ /ExpressionStat/ ) && do {
+	    $datarow->{$EDF_NORMALIZATIONTYPE}
+		= "Statistical CHP Analysis";
+	    last DBA_TYPE;
+	};
+	( $chp->get_algorithm() =~ /ExpressionCall/ ) && do {
+	    $datarow->{$EDF_NORMALIZATIONTYPE}
+		= "Empirical CHP Analysis";
+	    last DBA_TYPE;
+	};
+    }
+
+    return;
+}
+
+sub _extract_cel_data : PRIVATE {
+
+    my ( $self, $cel, $datarow ) = @_;
+
+    # CEL parameters
+    my %cel_params;
+    foreach my $param ( keys %{ $cel->get_parameters() } ) {
+	$cel_params{"${AFFY_PARAMETER_PREFIX}$param"}
+	    = $cel->get_parameters()->{$param};
+    }
+    $datarow->{$EDF_FEXT_PARAMS} = \%cel_params;
+
+    # Make sure the protocol params are declared in any
+    # generated MAGE-ML.
+    $self->get_protocol_bag()->(
+	$datarow->{$EDF_FEXT_PROTOCOL},
+	{   protocol_type => $OE_VAL_FEATUREEXTRACTION,
+	    name          => 'Affymetrix CEL analysis (Percentile)',
+	    parameters    => _mage_param_hash( $cel->get_parameters() ),
+	    parameter_bag => $self->get_parameter_bag(),
+	}
+    );
+
+    # QC Stats
+    $datarow->{$EDF_FEXT_STATS} = $cel->get_stats();
+
+    # Hardware (This is an Affy-specific hack for now FIXME)
+    $datarow->{$EDF_HYB_HARDWARE}
+	||= 'Affymetrix:Hardware:FluidicsStation';
+    $datarow->{$EDF_SCAN_HARDWARE} ||= 'Affymetrix:Hardware:Scanner';
+
+    # FIXME this below won't always be the case
+    if ( $cel->get_version() == 3 ) {
+	$datarow->{$EDF_SCAN_SOFTWARE}
+	    ||= 'Affymetrix MicroArraySuite (5.0)';
+	$datarow->{$EDF_FEXT_SOFTWARE}
+	    ||= 'Affymetrix MicroArraySuite (5.0)';
+    }
+    elsif ( $cel->get_version() == 4 ) {
+	$datarow->{$EDF_SCAN_SOFTWARE}
+	    ||= 'Affymetrix GeneChip Operating Software (1.2)';
+	$datarow->{$EDF_FEXT_SOFTWARE}
+	    ||= 'Affymetrix GeneChip Operating Software (1.2)';
+    }
+    else {
+	$datarow->{$EDF_SCAN_SOFTWARE}
+	    ||= 'Affymetrix Expression Console (1.1)';
+	$datarow->{$EDF_FEXT_SOFTWARE}
+	    ||= 'Affymetrix Expression Console (1.1)';
+    }
+
+    # image format
+    $datarow->{$EDF_IMAGE_FORMAT} ||= 'Affymetrix_DAT';
+
+    return;
+}
+
+sub _read_exp_file : PRIVATE {
+
+    my ( $self, $expfile ) = @_;
+
+    # Check we don't have an alternate source directory
+    $expfile = get_filepath_from_uri(
+	$expfile,
+	$self->get_source_directory(),
+    );
+
+    # Parse the file.
+    my $factory = ArrayExpress::Datafile::Affymetrix->new();
+    my $exp = $factory->make_parser($expfile);
+    $exp->parse();
+
+    return $exp;
+}
+   
+sub _extract_exp_data : PRIVATE {
+
+    my ( $self, $expfile, $datarow ) = @_;
+
+    my $exp = $self->_read_exp_file( $expfile );
+
+    # Correct the hyb protocol, if it's known. EXP file
+    # information trumps all.
+    if ( $exp->get_protocol() ) {
+	$datarow->{$EDF_HYB_PROTOCOL}
+	    = "${AFFY_PROTOCOL_PREFIX}Hybridization-"
+		. $exp->get_protocol();
+    }
+
+    # EXP parameters
+    my %hyb_params;
+    foreach my $param ( keys %{ $exp->get_parameters() } ) {
+	$hyb_params{"${AFFY_PARAMETER_PREFIX}$param"}
+	    = $exp->get_parameters()->{$param};
+    }
+    $datarow->{$EDF_HYB_PARAMS}  = \%hyb_params;
+    $datarow->{$EDF_SCAN_PARAMS} = clean_hash(
+	{
+	    "${AFFY_PARAMETER_PREFIX}ScanPixelSize" =>
+		$exp->get_pixel_size(),
+	    "${AFFY_PARAMETER_PREFIX}NumberOfScans" =>
+		$exp->get_num_scans(),
+	}
+    );
+    $datarow->{$EDF_HYB_HW_PARAMS} = clean_hash(
+	{
+	    "${AFFY_PARAMETER_PREFIX}FluidicsStationNumber" =>
+		$exp->get_station(),
+	    "${AFFY_PARAMETER_PREFIX}FluidicsStationPosition" =>
+		$exp->get_module(),
+	}
+    );
+    $datarow->{$EDF_SCAN_HW_PARAMS} = clean_hash(
+	{
+	    "${AFFY_PARAMETER_PREFIX}ScannerID" =>
+		$exp->get_scanner_id(),
+	    "${AFFY_PARAMETER_PREFIX}ScannerType" =>
+		$exp->get_scanner_type(),
+	}
+    );
+
+    # Make sure the protocol params are declared in any
+    # generated MAGE-ML.
+    $self->get_protocol_bag()->(
+	$datarow->{$EDF_HYB_PROTOCOL},
+	{   protocol_type => $OE_VAL_HYBRIDIZATION,
+	    name          => $exp->get_protocol(),
+	    parameters    => _mage_param_hash( $exp->get_parameters() ),
+	    parameter_bag => $self->get_parameter_bag(),
+	}
+    );
+    $self->get_protocol_bag()->(
+	$datarow->{$EDF_SCAN_PROTOCOL},
+	{   protocol_type => $OE_VAL_SCANNING,
+	    name          => 'Affymetrix Scanning',
+	    parameters    => _mage_param_hash(
+		{
+		    'ScanPixelSize' => $exp->get_pixel_size(),
+		    'NumberOfScans' => $exp->get_num_scans(),
+		},
+	    ),
+	    parameter_bag => $self->get_parameter_bag(),
+	}
+    );
+
+    # Grab the array chip lot if it's available. We default to the
+    # EXP file value as it's more likely to be correct.
+    $exp->get_chip_lot()
+	&& do { $datarow->{$EDF_ARRAYSERIAL} = $exp->get_chip_lot(); };
+
+    return $exp->get_chip_type();
+}
+
+sub _get_bioassays_from_mbadata : PRIVATE {
+
+    my ( $self, $badata ) = @_;
+
+    my (@mbas, @pbas);
+    if ( my $dim = $badata->getBioAssayDimension() ) {
+	foreach my $mba ( @{ $dim->getBioAssays() || [] } ) {
+	    push @mbas, $mba;
+	    if ( my $fext = $mba->getFeatureExtraction() ) {
+		if ( my $pba = $fext->getPhysicalBioAssaySource() ) {
+		    push @pbas, $pba;
+		}
+	    }
+	}
+    }
+
+    return ( \@mbas, \@pbas );
+}
+
+sub _add_expdata_to_mbadata : PRIVATE {
+
+    # If the file has a ba_data with a NVT referring to what looks
+    # like an EXP file, we process it here and add whatever we can to
+    # the PBAs (which for single-colour hybs such as Affy will just be
+    # the actual hybridization PBA).
+    my ( $self, $expfile, $badata ) = @_;
+
+    my ( $mbas, $pbas ) = $self->_get_bioassays_from_mbadata( $badata );
+    my $exp = $self->_read_exp_file( $expfile );
+
+    if ( $exp->get_protocol() ) {
+	$self->_add_hybdata_to_pbas( $exp, $pbas );
+    }
+
+    $self->_add_scandata_to_pbas( $exp, $pbas );
+
+    return $exp->get_chip_type();
+}
+
+sub _add_scandata_to_pbas : PRIVATE {
+
+    my ( $self, $exp, $pbas ) = @_;
+
+    foreach my $pba ( @$pbas ) {
+
+	BATREAT:
+	foreach my $bat ( @{ $pba->getBioAssayTreatments() || [] } ) {
+
+	    # Don't add if there's already a protocolapp.
+	    unless ( scalar( @{ $bat->getProtocolApplications() || [] } ) ) {
+		my ($protocol, $param_hash)
+		    = $self->_create_affy_hyb_protocol( $exp );
+		my $pa = Bio::MAGE::Protocol::ProtocolApplication->new(
+		    protocol     => $protocol,
+		    activityDate => $exp->get_scan_date() || 'n/a',
+		);
+		while ( my ($name, $value) = each %{ $param_hash } ) {
+		    my $pv = Bio::MAGE::Protocol::ParameterValue->new(
+			parameterType => $self->get_parameter_bag->(
+			    $protocol->getIdentifier().$name
+			),
+			value         => $value,
+		    );
+		    $pa->addParameterValues( $pv );
+		}
+		$bat->addProtocolApplications( $pa );
+	    }
+
+	    # Should be only one treatment for Affy.
+	    last BATREAT;
+	}
+    }
+   
+    return;
+}
+    
+sub _create_affy_scan_protocol : PRIVATE {
+
+    my ( $self, $exp ) = @_;
+
+    my $accession = "${AFFY_PROTOCOL_PREFIX}Scan";
+
+    my $protocol = $self->get_protocol_bag()->(
+	$accession,
+	{
+	    accession     => $accession,
+	    type          => $OE_VAL_SCANNING,
+	    name          => 'Affymetrix Scanning Protocol',
+	}
+    );
+
+    my @scan_params;
+    my $param_hash = clean_hash(
+	{
+	    "ScanPixelSize" => $exp->get_pixel_size(),
+	    "NumberOfScans" => $exp->get_num_scans(),
+	}
+    );
+
+    my $mage_params = _mage_param_hash( $param_hash );
+    while ( my ( $name, $args ) = each %{ $mage_params } ) {
+	push @scan_params, $self->get_parameter_bag()->(
+	    $accession.$name,
+	    $args,
+	);
+    }
+    $self->_add_params_to_protocol( \@scan_params, $protocol );
+
+    return $protocol, $param_hash;
+}
+
+sub _add_hybdata_to_pbas : PRIVATE {
+
+    my ( $self, $exp, $pbas ) = @_;
+    
+    foreach my $pba ( @$pbas ) {
+	if ( my $hyb = $pba->getBioAssayCreation() ) {
+
+	    # Don't add if there's already a protocolapp.
+	    unless ( scalar( @{ $hyb->getProtocolApplications() || [] } ) ) {
+		my $protocol = $self->_create_affy_hyb_protocol($exp);
+		my $pa = Bio::MAGE::Protocol::ProtocolApplication->new(
+		    protocol     => $protocol,
+		    activityDate => $exp->get_hyb_date() || 'n/a',
+		);
+		while ( my ($name, $value) = each %{ $exp->get_parameters() } ) {
+		    my $pv = Bio::MAGE::Protocol::ParameterValue->new(
+			parameterType => $self->get_parameter_bag->(
+			    $protocol->getIdentifier().$name
+			),
+			value         => $value,
+		    );
+		    $pa->addParameterValues( $pv );
+		}
+		$hyb->addProtocolApplications( $pa );
+	    }
+	}
+    }
+
+    return;
+}
+
+sub _create_affy_hyb_protocol : PRIVATE {
+
+    my ( $self, $exp ) = @_;
+
+    my $accession = "${AFFY_PROTOCOL_PREFIX}Hybridization-"
+	. $exp->get_protocol();
+
+    my $protocol = $self->get_protocol_bag()->(
+	$accession,
+	{
+	    accession     => $accession,
+	    type          => $OE_VAL_HYBRIDIZATION,
+	    name          => $exp->get_protocol(),
+	}
+    );
+
+    my @params;
+    my $mage_params = _mage_param_hash( $exp->get_parameters() );
+    while ( my ( $name, $args ) = each %{ $mage_params } ) {
+	push @params, $self->get_parameter_bag()->(
+	    $accession.$name,
+	    $args,
+	);
+    }
+    $self->_add_params_to_protocol( \@params, $protocol );
+
+    return $protocol;
+}
+
+sub _add_celdata_to_mbadata : PRIVATE {
+
+    # Navigate to MBA and add a protocolapp,
+    # summarystats; FIXME also software, hardware etc?
+    my ( $self, $cel, $badata ) = @_;
+
+    my ( $mbas, $pbas ) = $self->_get_bioassays_from_mbadata( $badata );
+
+    foreach my $mba ( @$mbas ) {
+	if ( my $fext = $mba->getFeatureExtraction() ) {
+
+	    # Don't add if there's already a protocolapp.
+	    unless ( scalar( @{ $fext->getProtocolApplications() || {} } ) ) {
+		my $protocol
+		    = $self->_create_affy_fext_protocol( $cel );
+		my $pa = Bio::MAGE::Protocol::ProtocolApplication->new(
+		    protocol     => $protocol,
+		    activityDate => 'n/a',
+		);
+		while ( my ($name, $value) = each %{ $cel->get_parameters() } ) {
+		    my $pv = Bio::MAGE::Protocol::ParameterValue->new(
+			parameterType => $self->get_parameter_bag->(
+			    $protocol->getIdentifier().$name
+			),
+			value         => $value,
+		    );
+		    $pa->addParameterValues( $pv );
+		}
+		$fext->addProtocolApplications( $pa );
+	    }
+	}
+    }
+
+    $self->_add_summarystats_to_badata( $cel->get_stats(), $badata );
+
+    return;
+}
+
+sub _add_summarystats_to_badata : PRIVATE {
+
+    my ( $self, $stats, $badata ) = @_;
+
+    if ( $stats ) {
+        my @statistics;
+        foreach my $name ( sort keys %$stats ) {
+            push(
+                @statistics,
+                Bio::MAGE::NameValueType->new(
+                    name  => $name,
+                    value => $stats->{$name},
+                )
+            );
+        }
+        $badata->setSummaryStatistics( \@statistics );
+    }
+
+    return;
+}
+
+sub _create_affy_fext_protocol : PRIVATE {
+
+    my ( $self, $cel ) = @_;
+
+    my $accession = 'P-AFFY-6';
+
+    my $protocol = $self->get_protocol_bag()->(
+	$accession,
+	{
+	    accession     => $accession,
+	    type          => $OE_VAL_FEATUREEXTRACTION,
+	    name          => 'Affymetrix CEL analysis (Percentile)',
+	}
+    );
+
+    my @params;
+    my $mage_params = _mage_param_hash( $cel->get_parameters() );
+    while ( my ( $name, $args ) = each %{ $mage_params } ) {
+	push @params, $self->get_parameter_bag()->(
+	    $accession.$name,
+	    $args,
+	);
+    }
+    $self->_add_params_to_protocol( \@params, $protocol );
+
+    return $protocol;
+}
+
+sub _add_chpdata_to_dbadata : PRIVATE {
+
+    my ( $self, $chp, $badata ) = @_;
+
+    if ( my $txn = $badata->getProducerTransformation() ) {
+
+	# Don't add if there's already a protocolapp.
+	unless ( scalar( @{ $txn->getProtocolApplications() || {} } ) ) {
+	    my $protocol
+		= $self->_create_affy_norm_protocol( $chp );
+	    my $pa = Bio::MAGE::Protocol::ProtocolApplication->new(
+		protocol     => $protocol,
+		activityDate => 'n/a',
+	    );
+	    while ( my ($name, $value) = each %{ $chp->get_parameters() } ) {
+		my $pv = Bio::MAGE::Protocol::ParameterValue->new(
+		    parameterType => $self->get_parameter_bag->(
+			$protocol->getIdentifier().$name
+		    ),
+		    value         => $value,
+		);
+		$pa->addParameterValues( $pv );
+	    }
+	    $txn->addProtocolApplications( $pa );
+	}
+    }
+
+    $self->_add_summarystats_to_badata( $chp->get_stats(), $badata );
+
+    return;
+}
+    
+sub _create_affy_norm_protocol : PRIVATE {
+
+    my ( $self, $chp ) = @_;
+
+    my $accession = "${AFFY_PROTOCOL_PREFIX}" . $chp->get_algorithm();
+
+    my $protocol = $self->get_protocol_bag()->(
+	$accession,
+	{
+	    accession     => $accession,
+	    type          => $OE_VAL_NORMALIZATION,
+	    name          => $chp->get_algorithm(),
+	}
+    );
+
+    my @params;
+    my $mage_params = _mage_param_hash( $chp->get_parameters() );
+    while ( my ( $name, $args ) = each %{ $mage_params } ) {
+	push @params, $self->get_parameter_bag()->(
+	    $accession.$name,
+	    $args,
+	);
+    }
+    $self->_add_params_to_protocol( \@params, $protocol );
+
+    return $protocol;
+}
+
+sub _add_params_to_protocol : PRIVATE {
+
+    my ( $self, $params, $protocol ) = @_;
+
+    foreach my $param ( @$params ) {
+	my $new_id = $param->getIdentifier();
+	my $found = first {
+	    $_->getIdentifier() eq $new_id
+	} @{ $protocol->getParameterTypes() || [] };
+	$protocol->addParameterTypes($param) unless $found;
+    }
+
+    return;
+}
+
+sub _set_pbas_channel_and_label : PRIVATE {
+
+    my ( $self, $pbas, $labelname ) = @_;
+
+    foreach my $pba ( @{ $pbas || [] } ) {
+
+	# Set up the default objects.
+	my $default_label = Bio::MAGE::BioMaterial::Compound->new(
+	    identifier => $AE_LABELCOMPOUND_PREFIX . $labelname,
+	    name       => $labelname,
+	);
+	my $default_channel = Bio::MAGE::BioAssay::Channel->new(
+	    identifier => $AE_CHANNEL_PREFIX . $labelname,
+	    name       => $labelname,
+	    labels     => [ $default_label ],
+	);
+
+	# Fix the labels. We systematically replace all "unknown"
+	# labels with the default label.
+	if ( my $hyb = $pba->getBioAssayCreation() ) {
+
+	    BMM:
+	    foreach my $bmm ( @{ $hyb->getSourceBioMaterialMeasurements() || [] } ) {
+		my $le = $bmm->getBioMaterial();
+
+		# Make sure we're dealing with the right kind of object.
+		next BMM unless $le->can('getLabels');
+
+		my @newlabels;
+		if ( my $labels = $le->getLabels() ) {
+		    while ( my $l = shift @{ $labels } ) {
+			if (   lc($l->getIdentifier())
+			    eq lc($AE_LABELCOMPOUND_PREFIX . 'unknown') ) {
+			    push @newlabels, $default_label;
+			}
+			else {
+			    push @newlabels, $l;
+			}
+		    }
+		}
+		else {
+		    push @newlabels, $default_label;
+		}
+		$le->setLabels( \@newlabels );
+	    }
+	}
+
+	# Fix the channels. We systematically replace all "unknown"
+	# channels with the default channel.
+	my @newchannels;
+	if ( my $channels = $pba->getChannels() ) {
+	    while ( my $ch = shift @{ $channels } ) {
+		if (   lc($ch->getIdentifier())
+		    eq lc($AE_CHANNEL_PREFIX . 'unknown') ) {
+		    push @newchannels, $default_channel;
+		}
+		else {
+		    push @newchannels, $ch;
+		}
+	    }
+	}
+	else {
+	    push @newchannels, $default_channel;
+	}
+	$pba->setChannels( \@newchannels );
+    }
+
+    return;
+}
+
+sub _parse_affy_datafile : PRIVATE {
+
+    my ( $self, $file, $datarow ) = @_;
+
+    $file->isa('ArrayExpress::Datafile')
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    ref $datarow eq 'HASH'
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    my $badata = $file->get_mage_badata();
+
+    $file->set_format_type('Affymetrix');
+
+    require ArrayExpress::Datafile::Affymetrix;
+    my $factory = ArrayExpress::Datafile::Affymetrix->new();
+
+    # dye used
+    $datarow->{$EDF_DYE} ||= 'biotin';
+
+    if ( $file->get_name() =~ m/\.CEL$/i ) {
+
+	# Sort out the protocols (Unknown hyb protocol is the default)
+	if ( $datarow->{$EDF_HYB_PROTOCOL} ) {
+
+	    # Users can use the Affy protocol name here directly, but this
+	    # is ignored if the protocol is named like a known protocol
+	    # accession pattern.
+	    unless ( $datarow->{$EDF_HYB_PROTOCOL}
+		  =~ m!^${AFFY_PROTOCOL_PREFIX}Hybridization!
+		  || $datarow->{$EDF_HYB_PROTOCOL} =~ m!^P-\w+-\w+!i )
+                {
+                    $datarow->{$EDF_HYB_PROTOCOL}
+                        = "${AFFY_PROTOCOL_PREFIX}Hybridization-"
+			. $datarow->{$EDF_HYB_PROTOCOL};
+                }
+	}    # See below for EXP and fallback values
+
+	$datarow->{$EDF_SCAN_PROTOCOL} = "${AFFY_PROTOCOL_PREFIX}Scan";
+	$datarow->{$EDF_FEXT_PROTOCOL} = 'P-AFFY-6';
+
+	# Fallback value for hyb protocol
+	$datarow->{$EDF_HYB_PROTOCOL}
+	    ||= "${AFFY_PROTOCOL_PREFIX}Hybridization-Unknown";
+
+	# Sort out our EXP file. First, the Tab2MAGE version:
+	my $chip_type;
+	if ( my $expfile
+		 = $datarow->{ $EDF_FILE_PREFIX . 'exp' . $EDF_FILE_SUFFIX } ) {
+
+	    # EXP is still the first place we look for chip type.
+	    $chip_type = $self->_extract_exp_data(
+		$expfile,
+		$datarow,
+	    );
+	}
+
+	# MAGE-TAB object creation.
+	if ( $badata ) {
+
+	    my ( $mbas, $pbas ) = $self->_get_bioassays_from_mbadata( $badata );
+
+	    # Set the default label in cases where it's not already given.
+	    $self->_set_pbas_channel_and_label($pbas, 'biotin');
+
+	    # Next, EXP file for MAGE-TAB:
+	    my $expfile;
+	    foreach my $mba ( @$mbas ) {
+		if ( my $nvts = $mba->getPropertySets() ) {
+
+		    NVT:
+		    foreach my $nvt ( @$nvts ) {
+			if ( $nvt->getName() =~ m/\A EXP \z/ixms
+			 && $nvt->getValue() =~ m/\.EXP \z/ixms ) {
+			    $expfile = $nvt->getValue();
+			    last NVT;  # Only process the first EXP comment.
+			}
+		    }
+		}
+	    }
+	    if ( $expfile ) {
+		$chip_type = $self->_add_expdata_to_mbadata(
+		    $expfile,
+		    $badata,
+		);
+	    }
+	}
+	    
+	# Parse the CEL file, output the raw data
+	my $cel = $factory->make_parser( $file->get_path() );
+
+	if ( $self->get_use_binary_datafiles() ) {
+
+	    # Code the Affy files natively in MAGE-ML; just populate the
+	    # metadata.
+	    $cel->parse_header();
+
+	    # Make a note of the CEL version for later
+	    # introduction into the MAGE-ML
+	    my %typemap = (
+		3 => 'CELv3',
+		4 => 'CELv4',
+		1 => 'CEL_AGCC',
+	    );
+	    my $type = $typemap{ $cel->get_version() }
+		or croak( sprintf(
+		    "ERROR: Unrecognized CEL version: %s\n",
+		    $cel->get_version()
+		));
+
+	    # Copy the file to the target directory, note the type.
+	    $self->_mageml_file_as_binary( $file, $type );
+	}
+
+	else {
+
+	    # Parse the whole CEL file and export it as a matrix for
+	    # encoding in MAGE. This will use a lot of memory for larger
+	    # Affy chips.
+	    $cel->parse();
+
+	    my $target_file
+		= File::Spec->catfile( $self->get_output_directory(),
+				       $file->get_name() . ".proc" );
+	    $file->set_target_filename(
+		File::Spec->abs2rel(
+		    $target_file, $self->get_output_directory()
+		)
+	    );
+	    my $data_fh = IO::File->new( $target_file, '>' )
+		or croak(
+                    "Error: Unable to open output file $target_file: $!\n");
+	    $cel->export($data_fh);
+	}
+
+	# Sort out our fallback chip type if no EXP file available. We
+	# could go as far as parsing the CDF here, but that only works
+	# for some versions and is extra overhead.
+	my $stripped_cdfname;
+	if ( $stripped_cdfname
+		 = $datarow->{ $EDF_FILE_PREFIX . 'cdf' . $EDF_FILE_SUFFIX } ) {
+	    $stripped_cdfname =~ s/\.cdf//i;
+	}
+	$chip_type ||= $cel->get_chip_type()
+	    || $stripped_cdfname
+            || 'Unknown';
+
+	# Get the DesignElementDimension
+	$file->set_ded_type('Feature');
+
+	# If we're not fully parsing the CEL data, just use a standard DED
+	if ( $self->get_use_binary_datafiles() ) {
+	    $file->set_ded_identifier(
+		"Affymetrix:FeatureDimension:$chip_type");
+	}
+
+	# For full CEL parsing, derive a new DED and link to that.
+	else {
+	    my $ded = $cel->get_ded($chip_type);
+	    my $ded_identifier = $self->get_unique_de_dimension(
+		$ded,
+		$file->get_array_design_id(),
+		$file->get_ded_type(),
+	    );
+	    $file->set_ded_identifier($ded_identifier);
+	}
+
+	# Define the QuantitationTypeDimension
+	$file->set_column_headings( $cel->get_headings() );
+	$file->check_column_headings( $self->get_quantitation_types );
+
+	# Check here whether $file->get_mage_badata
+	# set, if so navigate to MBA and add a protocolapp,
+	# summarystats, software, hardware etc.
+	if ( $badata ) {
+
+	    # MAGE-TAB parsing here.
+	    $self->_add_celdata_to_mbadata( $cel, $badata );
+	}
+	else {
+
+	    # Tab2MAGE parsing here; dumps CEL data into $datarow for
+	    # later.
+	    $self->_extract_cel_data( $cel, $datarow );
+	}
+
+    }
+    elsif ( $file->get_name() =~ m/\.CHP$/i ) {
+
+	# Sort out our CDF file (note that this is backed up by
+	# $chp->get_chip_type, below).
+	my $cdffile
+	    = $datarow->{ $EDF_FILE_PREFIX . 'cdf' . $EDF_FILE_SUFFIX };
+
+	# Parse the CHP file, output the normalized data
+	my $chp = $factory->make_parser( $file->get_path() );
+
+	my $sighandler = $SIG{__DIE__};
+	delete $SIG{__DIE__};
+	local $EVAL_ERROR;
+	eval {
+	    $chp->parse();
+	};
+	$SIG{__DIE__} = $sighandler if $sighandler;
+
+	# Handle CHP parsing failure (CHPv8 will do this).
+	if ( $EVAL_ERROR ) {
+
+	    if ( $chp->get_version == 8 ) {
+		$file->set_ded_type('CompositeSequence');
+		$file->set_ded_identifier(
+		    "Affymetrix:CompositeSequenceDimension:"
+		     . $chp->get_chip_type()
+		 );
+
+		# Copy the file to the target directory, note the
+		# type.
+		$self->_mageml_file_as_binary(
+		    $file,
+		    "CHPv" . $chp->get_version,
+		);
+	    }
+	    else {
+		croak("Error: Cannot parse CHP: $EVAL_ERROR");
+	    }
+
+	}
+	else {
+
+	    # Successful parse; write out the data, set DED.
+	    $self->_output_chp_data(
+		$chp,
+		$file,
+		$cdffile,
+	    );
+	}
+
+	# Define the QuantitationTypeDimension
+	$file->set_column_headings( $chp->get_headings() );
+	$file->check_column_headings( $self->get_quantitation_types );
+
+	# Check here whether $file->get_mage_badata
+	# set, if so navigate to DBAD and add a protocolapp,
+	# summarystats.
+	if ( $badata ) {
+
+	    # MAGE-TAB.
+	    $self->_add_chpdata_to_dbadata( $chp, $badata );
+	}
+	else {
+
+	    # Tab2MAGE. Extract CHP data into $datarow.
+	    $self->_extract_chp_data( $chp, $datarow );
+	}
+    }
+    else {
+	croak(
+            sprintf(
+		qq{Error: unrecognized file type for file "%s" (Affymetrix must be .CEL or .CHP)\n},
+		$file->get_name(),
+	    ),
+	);
+    }
+
+    return;
+}
+
+sub _output_chp_data : PRIVATE {
+
+    my ($self, $chp, $file, $cdffile) = @_;
+
+    my $array_accession = $file->get_array_design_id()
+	or croak("Error: Datafile has no array accession.");
+
+    $cdffile ||= $chp->get_chip_type() . ".cdf";
+
+    # Parse the CDF, unless we have it already cached.
+    my $cdf;
+    unless ( $cdf = $self->get_cdf_cache(lc($cdffile)) ) {
+
+	my $factory = ArrayExpress::Datafile::Affymetrix->new();
+
+	# Check Affy library dir or source dir or working dir.
+	my ($actualcdf, $filepath) = find_cdf(
+	    $cdffile,
+	    $self->get_source_directory(),
+	);
+
+	unless ( $actualcdf ) {
+	    croak("Error: CDF file $cdffile not found\n");
+	}
+
+	$cdf = $factory->make_parser($filepath);
+	$cdf->parse();
+	$self->set_cdf_cache( lc($cdffile), $cdf );
+    }
+
+    # Output the stripped data.
+    my $target_file = File::Spec->catfile(
+	$self->get_output_directory(),
+	$file->get_name() . ".proc",
+    );
+
+    $file->set_target_filename(
+	File::Spec->abs2rel( $target_file, $self->get_output_directory() )
+    );
+
+    my $data_fh = IO::File->new( $target_file, '>' )
+	or croak("Error: Unable to open output file $target_file: $!\n");
+
+    $chp->export( $data_fh, $cdf );
+
+    # Set the DesignElementDimension.
+    my $ded = $chp->get_ded($cdf);
+    $file->set_ded_type('CompositeSequence');
+    my $ded_identifier = $self->get_unique_de_dimension(
+	$ded,
+	$array_accession,
+	$file->get_ded_type(),
+    );
+    $file->set_ded_identifier($ded_identifier);
+
+    return;
+}
+
+sub _parse_binary_nimblescan : PRIVATE {
+
+    my ( $self, $file ) = @_;
+
+    $file->isa('ArrayExpress::Datafile')
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    # Enter a dummy DED identifier (no longer used, code kept here in
+    # case this policy ever changes).
+
+#    my $ded_type = 'Reporter';
+#    $file->set_ded_type($ded_type);
+#    my $ded_identifier = $self->get_unique_de_dimension(
+#	[sprintf( "ebi.ac.uk:Nimblegen:Reporter:%s", $file->get_array_design_id() )],
+#	$file->get_array_design_id(),
+#	$ded_type
+#    );
+#    $file->set_ded_identifier($ded_identifier);
+
+    # Make sure we know which software is being used.
+    my $rc = $file->check_column_headings( $self->get_quantitation_types );
+    if ($rc) {
+        print { $self->get_error_fh() } ( "ERROR: " . $file->get_name() . ": $rc" );
+    }
+
+    # Copy the file to the target dir, label it with a
+    # dataExternal format tag (This tag will probably change FIXME).
+    my $format = (   $file->get_format_type() eq 'NimbleScanFeature'
+		  || $file->get_format_type() eq 'NimbleScanNorm')
+               ? 'NimbleScan'
+	       : $file->get_format_type();
+    $self->_mageml_file_as_binary( $file, $format );
+
+    return;
+}
+
+sub _mageml_file_as_binary : PRIVATE {
+
+    # Takes a Datafile object and a type string (CELv3, CELv4
+    # etc.). Copies the actual file across to the target MAGE-ML
+    # directory, stores the type for use later.
+
+    my ( $self, $file, $type ) = @_;
+
+    $file->isa('ArrayExpress::Datafile')
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    # For MAGE-TAB, rewrite the referenced filename back to its
+    # original form.
+    if ( my $data = $file->get_mage_badata() ) {
+	my $bdc = $data->getBioDataValues();
+	$bdc->setCube( $file->get_name() );
+    }
+
+    # Copy the untouched binary file across to the target directory
+    my $target_file
+        = File::Spec->rel2abs( $file->get_name(), $self->get_output_directory() );
+    $file->set_target_filename( $file->get_name() );
+    if ( $file->get_path ne $target_file ) {
+        copy( $file->get_path, $target_file )
+            or croak( "ERROR: Unable to copy binary data file "
+                . $file->get_path
+                . " into target directory: $!\n" );
+    }
+    else {
+        croak(    q{ERROR: Source and target filenames are identical}
+               . qq{ ("$target_file"). Copying may destroy the original file.}
+	        . q{ Please use alternative target directory.});
+    }
+
+    # Make a note of the file type (CELv3, CELv4, NimbleScan).
+    if ($type) {
+        $self->set_cel_types( $file->get_target_filename() => $type );
+    }
+    else {
+        croak("ERROR: No binary file type specified");
+    }
+
+    return;
+}
+
+sub _parse_text_datafile : PRIVATE {
+
+    my ( $self, $file, $datarow ) = @_;
+
+    $file->isa('ArrayExpress::Datafile')
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+    ref $datarow eq 'HASH'
+        or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+    # Default Array accession. This is changed if we encounter post-Affy
+    # data files (e.g. RMA data) to the name of the array. Used in
+    # constructing the CompositeSequence_refs.
+    my $arrayaccession = $file->get_array_design_id();
+
+    # This will hold a list of coordinate columns to be discarded.
+    my @unwanted_coord_columns;
+
+    # Figure out our line ending style (FIXME this should now be superfluous).
+    local $INPUT_RECORD_SEPARATOR = $file->get_linebreak_type();
+
+    # Figure out what kind of file we have, get column headings and indices
+    $file->parse_header();
+
+    unless ( scalar @{ $file->get_column_headings() } ) {
+        print STDERR (
+           "Error: No column headings for file " . $file->get_name() . "\n" );
+    }
+
+    # Used for AffyNorm; if not undef, will be used instead of the array
+    # accession to generate DesignElement ids.
+    my $chip_type;
+
+    FILEFORMAT:
+    {
+
+        # NimbleScan data files may be coded as binary.
+        if ( $self->get_use_binary_datafiles()
+            && ( $file->get_format_type =~ m/\A Nimble(Scan|gen)/xms ) ) {
+            $self->_parse_binary_nimblescan( $file );
+            return;
+        }
+
+	# For Illumina, check whether we have per-hyb data or not,
+	# prior to fixing anything.
+	elsif (   $file->get_format_type() =~ /^Illumina/
+	       && $file->is_illumina_fgem() ) {
+	    $file->rewrite_illumina_as_fgem();
+	}
+
+        # Next, scan through known file formats which need fixing
+        # (GenePix, etc.)
+	else {
+	    my $rc = $file->fix_known_text_format();
+	    last FILEFORMAT if $rc;
+	}
+
+        # Other minor tweaks. These first few are basically FGEM:
+        if ( $file->get_format_type eq 'FGEM_CS' ) {
+            $file->set_format_type('FGEM');
+        }
+        elsif ( $file->get_format_type eq 'GEO' ) {
+            $file->set_format_type('FGEM');
+
+	    # ID_REF is assumed to be CompSeq ID.
+	    my $cols = $file->get_column_headings();
+	    $cols->[ $file->get_index_columns()->[0] ]
+		= 'CompositeSequence Identifier';
+	    $file->set_column_headings($cols);
+        }
+        elsif ( $file->get_format_type eq 'AffyNorm' ) {
+            $file->set_format_type('FGEM');
+
+            # Now we know the dye
+            $datarow->{$EDF_DYE} ||= 'biotin';
+
+            # Use the name of the CDF file as our array name.
+            # FIXME to use strings from EXP, CEL, CHP where available.
+            if ( my $cdffile
+                = $datarow->{ $EDF_FILE_PREFIX . 'cdf' . $EDF_FILE_SUFFIX } ) {
+                $cdffile =~ s/\.cdf$//i;
+                $chip_type = $cdffile;
+            }
+
+	    # Fall back to unknown.
+	    $chip_type ||= 'Unknown';
+
+	    # Record the chip_type for these data matrix files.
+	    unless ( $file->get_dm_chip_type() ) {
+		$file->set_dm_chip_type($chip_type);
+	    }
+        }
+
+    }    # end FILEFORMAT
+
+    # Figure out which columns we want to keep, populate heading_qts,
+    # heading_hybs etc.
+    my $rc = $file->check_column_headings( $self->get_quantitation_types );
+    if ($rc) {
+        printf { $self->get_error_fh() } ( "ERROR: %s: %s", $file->get_name(), $rc );
+    }
+
+    # We've made this generic by now, or at least we should have (Affy
+    # would crash here, but we've already diverted it).
+    unless ( $file->get_format_type() eq 'Generic'
+        or $file->get_format_type() eq 'FGEM' ) {
+        croak(    "Error: file "
+                . $file->get_name()
+                . " not in recognized format: "
+                . ( $file->get_format_type() || 'Unknown' )
+                . "\n" );
+    }
+
+    # FGEM (combined) data is treated as a special case - column
+    # headings do not equate directly to QTs.
+    my @column_headings = @{ $file->get_column_headings };
+
+    # Next, select only our columns of interest (based on QT name),
+    # write the outcome to log file.
+    my @new_column_headings;
+    foreach my $heading (@column_headings) {
+        if ( $file->get_data_metrics()->{$heading} ) {
+            push( @new_column_headings, $heading );
+            print { $self->get_error_fh() }
+                ( $file->get_name() . ": Recognized column kept: $heading\n" );
+        }
+	elsif ( $file->is_ignored_qt($heading) ) {
+            print { $self->get_error_fh() }
+                ( $file->get_name() . ": Stripping unwanted column: $heading\n" );
+	}
+        elsif ( $self->get_allow_undef_qts() ) {
+            push( @new_column_headings, $heading );
+            print { $self->get_error_fh() }
+                ( $file->get_name() . ": Keeping unrecognized column: $heading\n" );
+        }
+        else {
+            print { $self->get_error_fh() }
+                ( $file->get_name() . ": Omitting unrecognized column: $heading\n" );
+        }
+    }
+
+    # Set up some target filenames ($file->get_name() will be modified by
+    # strip_and_sort).
+    my $temporary_file = File::Spec->catfile( $self->get_output_directory(),
+        $file->get_name() . ".temporary",
+    );
+    my $target_file = File::Spec->catfile( $self->get_output_directory(),
+        $file->get_name() . ".proc" );
+
+    # Strip the file of unwanted columns, sort on the indices (coord
+    # or identifier), and move those indices to the leftmost
+    # columns. The Datafile is relinked to the temp filehandle.
+    $file->strip_and_sort( $temporary_file, \@new_column_headings );
+
+    # Open output filehandle
+    my $output_fh = IO::File->new( $target_file, '>' )
+        or croak("Error opening output file $target_file: $!\n");
+
+    # Generate DesignElementDimension, while stripping the feature
+    # coordinates/identifiers from the data file. This also relinks
+    # the Datafile object to the output filehandle.
+    $self->_create_designelementdimension(
+	$file,
+        $output_fh,
+	( $chip_type || $arrayaccession ),
+    );
+
+    # End of DesignElementDimension processing
+    unlink($temporary_file)
+        or carp(
+        "Warning: Unable to delete temporary file $temporary_file: $!\n");
+
+    $file->set_target_filename(
+        File::Spec->abs2rel( $target_file, $self->get_output_directory() ) );
+
+    return;
+}
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../../index.html">
+                <img src="../../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: ExperimentChecker.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Parser - a module handling Datafile parsing
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Parser;
+ my $parser = ArrayExpress::Datafile::Parser->new;
+
+=head1 DESCRIPTION
+
+The ArrayExpress::Datafile module provides methods for
+parsing individual data files. This module provides a global handler
+for Datafile objects.
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+1;
+
diff --git a/lib/ArrayExpress/Datafile/QT_list.pm b/lib/ArrayExpress/Datafile/QT_list.pm
new file mode 100644
index 0000000..2e2f8df
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/QT_list.pm
@@ -0,0 +1,342 @@
+#!/usr/bin/env perl
+#
+# QT_list.pm
+#
+# $Id: QT_list.pm 1852 2007-12-13 10:14:27Z tfrayner $
+#
+# This file contains QuantitationType information parsed out from our
+# QuantitationType survey spreadsheet, and from the relevant database
+# tables in MIAMExpress. This information has been primarily gathered
+# and curated by Ele Holloway.
+#
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../index.html">
+                <img src="../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: QT_list.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::QT_list.pm - a module handling QuantitationType
+information.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::QT_list qw(get_QTs writeQTs)
+ my $QT_hash = get_QTs();
+ write_QTs($QT_hash);
+
+=head1 DESCRIPTION
+
+This is a module providing QuantitationType support information and
+subroutines for the Tab2MAGE and experiment checker package.
+
+=head1 QUANTITATION TYPES
+
+Known QuantitationTypes are listed in the QT_list.pm module included
+with this distribution. This file includes a simple tab-delimited text
+section with subsections specific to a given software/manufacturer
+type. Each subsection begins with the tag "C<E<gt>E<gt>E<gt>>". There are
+several columns, but only the first is required:
+
+=over 1
+
+=begin html
+
+<table class="example">
+<tr><th>>>>QT software 1</th></tr>
+<tr><td>QT1 name</td><td>QT1 datatype</td><td>QT1 scale</td><td>QT1 subclass</td><td>QT1 is_background</td><td>QT1 target (confidence indicators)</td><td>QT1 channel</td><td>QT1 description</td></tr>
+<tr><td>QT2 name</td><td>QT2 datatype</td><td>QT2 scale</td><td>QT2 subclass</td><td>QT2 is_background</td><td>QT2 target (confidence indicators)</td><td>QT2 channel</td><td>QT2 description</td></tr>
+<tr><td>QT3 name</td><td>QT3 datatype</td><td>QT3 scale</td><td>QT3 subclass</td><td>QT3 is_background</td><td>QT3 target (confidence indicators)</td><td>QT3 channel</td><td>QT3 description</td></tr>
+<tr><td>QT4 name...</td><td> </td><td> </td><td> </td><td> </td><td> </td><td> </td><td> </td></tr>
+<tr><th>>>>QT software 2</th></tr>
+<tr><td>QT5 name</td><td>QT5 datatype</td><td>QT5 scale</td><td>QT5 subclass</td><td>QT5 is_background</td><td>QT5 target (confidence indicators)</td><td>QT5 channel</td><td>QT5 description</td></tr>
+<tr><td>QT6 name...</td><td> </td><td> </td><td> </td><td> </td><td> </td><td> </td><td> </td></tr>
+</table>
+
+=end html
+
+=begin man
+
+ >>>QT source 1 (software name)[manufacturer 1]
+ QT1 name  QT1 datatype  QT1 scale  QT1 subclass  QT1 is_background  QT1 conf. indicator tgt.  QT1 channel  QT1 description
+ QT2 name  QT2 datatype  QT2 scale  QT2 subclass  QT2 is_background  QT2 conf. indicator tgt.  QT2 channel  QT2 description
+ QT3 name  QT3 datatype  QT3 scale  QT3 subclass  QT3 is_background  QT3 conf. indicator tgt.  QT3 channel  QT3 description
+ QT4 name...
+ >>>QT source 2 (software name)[manufacturer 2]
+ QT5 name  QT5 datatype  QT5 scale  QT5 subclass  QT5 is_background  QT5 conf. indicator tgt.  QT5 channel  QT5 description
+ QT6 name...
+
+=end man
+
+=back
+
+where QT1, QT2 and so on are the quantitation types. Note that for the
+resulting MAGE identifiers to have the correct namespace, the ">>>QT
+software" strings should contain the namespace in square brackets, for
+example:
+
+ >>>Feature Extraction Software[Agilent Technologies]
+
+Note also that to add to a pre-existing set of quantitation types from
+a given software, this initial string B<must> match that included in the
+Tab2MAGE package. Currently included software types are as follows:
+
+ AIDA[Raytest]
+ Affymetrix[Affymetrix]
+ AppliedBiosystems
+ ArrayGauge[FUJIFILM]
+ ArrayVision[Imaging Research]
+ BZScan[TAGC ERM206]
+ BlueFuse[BlueGnome]
+ ChipSkipper[EMBL]
+ CodeLink[Motorola Life Sciences]
+ CodeLink Expression Analysis[GE Healthcare]
+ Feature Extraction Software[Agilent Technologies]
+ GEMTools[Incyte Genomics]
+ GLEAMS[NuTec Sciences]
+ GenePix[Axon Instruments]
+ GeneTAC
+ ImaGene[BioDiscovery]
+ NimbleScan[NimbleGen Systems]
+ QuantArray[PerkinElmer]
+ ScanAlyze[Stanford University]
+ ScanArray Express[PerkinElmer]
+ SpotFinder[TIGR]
+ UCSF Spot
+ Uni of Toronto in-house analysis software
+ Tab2MAGE_FGEM[ebi.ac.uk]
+
+Each file may contain QT information from any number of software
+vendors. The contents of each column are described below:
+
+=head2 QT file columns
+
+=over 2
+
+=item name
+
+The name of the QuantitationType. This corresponds to the exact string found in the data files.
+
+=item datatype
+
+The DataType OntologyEntry to be used (MGED Ontology). Default: float
+
+=item scale
+
+The Scale OntologyEntry to be used (MGED Ontology). Default:
+linear_scale
+
+=item subclass
+
+The QuantitationType subclass to use (e.g., MeasuredSignal, Error
+etc.). Default: SpecializedQuantitationType
+
+=item is_background
+
+Boolean flag (0 or 1) indicating whether the QT is background. Default: 0
+
+=item confidence indicator target
+
+Only used for ConfidenceIndicator QTs (Error, PValue, and
+ExpectedValue). Indicates the target QT name to which the
+ConfidenceIndicator applies. Defaults to undefined.
+
+=item channel
+
+The channel to be associated with the QT. Note that QTs can only have
+one channel; this is a current limitation of the MAGE object
+model. QTs linked to more than one channel in the real world
+(e.g. ratios) should omit the channel value here.
+
+=item description
+
+A plain-text description of the QT.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments. Particular credit
+goes to Ele Holloway, who was responsible for curating the lists of
+QuantitationTypes included with this script.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::QT_list;
+
+use strict;
+use warnings;
+
+use Carp;
+use IO::File;
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw($RE_LINE_BREAK);
+
+use base 'Exporter';
+our @EXPORT_OK = qw(
+    get_QTs
+    write_QTs
+);
+
+sub get_QTs {
+
+    # Accepts single filenames or arrayref list; also an optional
+    # "include_known" QTs argument.
+    my ( $extra_file_or_files, $include_known ) = @_;
+
+    # We now track whether a QT is user-defined or not.
+    my %QT_file = ( old => [], new => [] );
+
+    # Get a list of files containing QT info.  Start with the default
+    # - the QTs listed in the QT_list.txt file in the same dir as this
+    # module. The default QTs can then be overwritten as desired by
+    # the user.
+    my $module_path      = File::Spec->rel2abs(__FILE__);
+    my @module_dir_array = File::Spec->splitpath($module_path);
+    my $package_default  = $ENV{PAR_TEMP}   # Running under PAR
+                         ? File::Spec->catfile( $ENV{PAR_TEMP},
+						qw( inc lib ArrayExpress Datafile QT_list.txt ) )
+                         : File::Spec->catpath(
+			     @module_dir_array[ 0, 1 ], 'QT_list.txt' );
+    my $default = $CONFIG->get_DEFAULT_QT_FILENAME() || $package_default;
+
+    if ( $include_known || !$extra_file_or_files ) {
+        push( @{ $QT_file{old} }, $default );
+    }
+
+    if ($extra_file_or_files) {
+        if ( ref($extra_file_or_files) eq 'ARRAY' ) {
+            foreach my $file (@$extra_file_or_files) {
+                push( @{ $QT_file{new} }, $file );
+            }
+        }
+        else {
+            push( @{ $QT_file{new} }, $extra_file_or_files );
+        }
+    }
+
+    # Add any files supplied to the routine.
+    my %QT_filehandle = ( old => [], new => [] );
+    foreach my $type ( keys %QT_file ) {
+        foreach my $file ( @{ $QT_file{$type} } ) {
+            my $filehandle = IO::File->new( $file, '<' )
+                or
+                croak("Error: QuantitationType file $file not found: $!\n");
+            push( @{ $QT_filehandle{$type} }, $filehandle );
+        }
+    }
+
+    # Loop through the filehandles.
+    my %QT_hash;
+    foreach my $type ( keys %QT_filehandle ) {
+        foreach my $filehandle ( @{ $QT_filehandle{$type} } ) {
+            my $software;
+
+            QTFILE_LINE:
+            while ( my $line = <$filehandle> ) {
+
+                # skip comment or blank lines
+                next QTFILE_LINE if ( $line =~ /^\#/ or $line =~ /^\s*$/ );
+
+		# This should support both unix or DOS line breaks.
+                $line =~ s/$RE_LINE_BREAK//g;
+
+                # Header for a software/manufacturer name. Remove
+                # leading and trailing whitespace
+                if ( $line =~ m/^>>>\s*(.*?)\s*$/ ) {
+                    $software = $1;
+                    next QTFILE_LINE;
+
+                }
+
+                # Everything else; the meat of the QTs
+                else {
+
+                    # Strip whitespace in a moderately efficient manner
+                    $line =~ s/^ *//;
+                    $line =~ s/ *$//;
+                    $line =~ s/ *\t */\t/g;
+
+                    my @line_array = split /\t/, $line, -1;
+
+                    $QT_hash{$software}{ $line_array[0] } = {
+                        datatype      => $line_array[1] || q{},
+                        scale         => $line_array[2] || q{},
+                        subclass      => $line_array[3] || q{},
+                        is_background => $line_array[4] || q{},
+                        confmap       => $line_array[5] || q{},
+                        channel       => $line_array[6] || q{},
+                        description   => $line_array[7] || q{},
+                        is_user_defined => ( $type eq 'new' ) || undef,
+                    };
+                }
+            }
+        }
+    }
+    return ( \%QT_hash );
+}
+
+sub write_QTs {
+
+    my ( $new_QTs, $fh ) = @_;
+
+    print $fh <<"HEADER";
+# Name\tDataType\tScale\tMAGE Subclass\tisBackground\tConfidenceIndicator target QT\tChannel\tDescription
+HEADER
+
+    foreach my $software ( sort keys %$new_QTs ) {
+
+        print $fh (">>>$software\n");
+
+        my @lines;
+        while ( my ( $name, $slots ) = each %{ $new_QTs->{$software} } ) {
+            push(
+                @lines,
+                join( "\t",
+                    $name,                   $slots->{datatype},
+                    $slots->{scale},         $slots->{subclass},
+                    $slots->{is_background}, $slots->{confmap},
+                    $slots->{channel},       $slots->{description},
+                )
+            );
+        }
+
+        print $fh ( join( "\n", sort @lines ), "\n" );
+    }
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/QT_list.txt b/lib/ArrayExpress/Datafile/QT_list.txt
new file mode 100644
index 0000000..0420249
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/QT_list.txt
@@ -0,0 +1,1087 @@
+#
+# $Id: QT_list.txt 2084 2008-06-20 16:41:44Z farne $
+#
+# -----------------------------------------------------------------------------------------------------							
+#							
+# This first section includes a set of "custom" QTs							
+# which are commonly used but not defined elsewhere.							
+#							
+# Name	DataType	Scale	MAGE Subclass	isBackground	ConfidenceIndicator target QT	Channel	Description
+>>>Tab2MAGE_FGEM[ebi.ac.uk]							
+RMA	float	linear_scale	DerivedSignal				
+Normalized	float	linear_scale	DerivedSignal				
+Ratio	float	linear_scale	Ratio				
+# -----------------------------------------------------------------------------------------------------							
+#							
+# Below is the curated list of QTs as prepared and maintained by the ArrayExpress curators.							
+# This will be updated periodically with new releases of this package.							
+#							
+# NOTE TO SELF - this part of the file will be automatically generated by Datafiles::write_QTs.							
+# Attempt no changes here unless you are sure they won't be overwritten.							
+#							
+# Name	DataType	Scale	MAGE Subclass	isBackground	ConfidenceIndicator target QT	Channel	Description
+>>>AIDA[Raytest]							
+Area [pixel]	float	linear_scale	SpecializedQuantitationType				Spot area in pixels.
+Bkg SD Cy3	float	linear_scale	Error	1	Bkg [QL] Cy3	Cy3	Standard deviation of background channel Cy3.
+Bkg SD Cy5	float	linear_scale	Error	1	Bkg [QL] Cy5	Cy5	Standard deviation of background channel Cy5.
+Bkg [QL] Cy3	float	linear_scale	SpecializedQuantitationType	1		Cy3	Standard deviation of background channel Cy3.
+Bkg [QL] Cy5	float	linear_scale	SpecializedQuantitationType	1		Cy5	Standard deviation of background channel Cy5.
+Homogeneity [%] Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Percentage of dot deviation from homogenous intensity distribution channel Cy3.
+Homogeneity [%] Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Percentage of dot deviation from homogenous intensity distribution channel Cy5.
+Integral SD Cy3	float	linear_scale	Error		Integral [QL] Cy3	Cy3	Standard deviation of integrated raw intensity channel Cy3.
+Integral SD Cy5	float	linear_scale	Error		Integral [QL] Cy5	Cy5	Standard deviation of integrated raw intensity channel Cy5.
+Integral [QL] Cy3	float	linear_scale	MeasuredSignal			Cy3	Integrated raw intensity of spot Channel Cy3.
+Integral [QL] Cy5	float	linear_scale	MeasuredSignal			Cy5	Integrated raw intensity of spot Channel Cy5.
+Mean [QL] Cy3	float	linear_scale	MeasuredSignal			Cy3	Mean (Integral/Area) Channel Cy3.
+Mean [QL] Cy5	float	linear_scale	MeasuredSignal			Cy5	Mean (Integral/Area) Channel Cy5.
+Median [QL] Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Median of the raw pixel intensity of spot Channel Cy3.
+Median [QL] Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Median of the raw pixel intensity of spot Channel Cy5.
+Quality Cy3	boolean	unscaled	SpecializedQuantitationType			Cy3	Quality flag Channel Cy3.
+Quality Cy5	boolean	unscaled	SpecializedQuantitationType			Cy5	Quality flag Channel Cy5.
+Saturated [pixel] Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Measure of saturation pixels channel Cy3.
+Saturated [pixel] Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Measure of saturation pixels channel Cy5.
+>>>Affymetrix[Affymetrix]							
+Probe Set Name	string_datatype	unscaled	SpecializedQuantitationType				The name of the probe set.
+Detection	string_datatype	unscaled	PresentAbsent				A measurement indicating if the transcript was detected (Present), not detected (Absent) or marginally detected (Marginal). Can be one of "Absent", "Marginal", "Present" or "No Call".
+Detection p-value	float	linear_scale	PValue		Detection		The p-value calculated for a given detection call.
+Signal	float	linear_scale	DerivedSignal				A measure of the relative abundance of a transcript.
+Number of Pairs	integer	linear_scale	SpecializedQuantitationType				The number of probe pairs in the probe set.
+Number of Pairs Used	integer	linear_scale	SpecializedQuantitationType				The number of probe pairs in the probe set used in the Detection call.
+CELIntensity	float	linear_scale	MeasuredSignal				The intensity value of the probe. Represents the 75th percentile among the DAT file intensity values of the pixels in the feature.
+CELIntensityStdev	float	linear_scale	SpecializedQuantitationType				The standard deviation of the intensity of the probe.
+CELMask	boolean	linear_scale	SpecializedQuantitationType				A flag indicating that the probe was masked by the user.
+CELOutlier	boolean	linear_scale	SpecializedQuantitationType				A flag indicating that the software called this probe an outlier.
+CELPixels	integer	linear_scale	SpecializedQuantitationType				The number of pixels used to calculate the intensity.
+CELX	integer	linear_scale	SpecializedQuantitationType				The x coordinate of the probe.
+CELY	integer	linear_scale	SpecializedQuantitationType				The y coordinate of the probe.
+CHPAbsCall	string_datatype	unscaled	PresentAbsent				The call in an absolute analysis that indicates if the transcript was present (P), absent (A), or marginal (M).
+CHPAllele	string_datatype	unscaled	SpecializedQuantitationType				The allele call in a SNP analysis: AA, BB, AB, AB_A, AB_B, or "No call".
+CHPAllelePvalue	float	linear_scale	PValue		CHPAllele		The p-value calculated for a given allele call.
+CHPAllelePvalueAA	float	linear_scale	PValue		CHPAllele		The p-value calculated for a given AA allele call.
+CHPAllelePvalueAB	float	linear_scale	PValue		CHPAllele		The p-value calculated for a given AB allele call.
+CHPAllelePvalueBB	float	linear_scale	PValue		CHPAllele		The p-value calculated for a given BB allele call.
+CHPAllelePvalueNoCall	float	linear_scale	PValue		CHPAllele		The p-value calculated for a given NoCall allele call.
+CHPAlleleRAS1	float	linear_scale	SpecializedQuantitationType				
+CHPAlleleRAS2	float	linear_scale	SpecializedQuantitationType				
+CHPAvgDiff	float	linear_scale	DerivedSignal				Average intensity difference
+CHPAvgDiffChange	float	linear_scale	SpecializedQuantitationType				The change in average difference
+CHPBaselineAbsent	float	linear_scale	SpecializedQuantitationType				Flag indicating if the baseline call was absent.
+CHPChange	string_datatype	unscaled	PresentAbsent				A call indicating an Increase (I), Marginal Increase (MI), No Change (NC), Decrease (D), or Marginal Decrease (MD) in transcript level between a baseline array and an experiment array. May also have the value "No Call".
+CHPChangePvalue	float	linear_scale	PValue		CHPChange		Measures the probability that the expression levels of a probe set in two different arrays are the same or not. When the p-value is close to 0.5 they are likely to be the same. When the p-value is close to 0 the expression level in the experiment array is
+CHPCommonPairs	integer	linear_scale	SpecializedQuantitationType				The number of common probe pairs on two arrays (experiment versus baseline) after saturation across the probe set is determined.
+CHPDPos-DNegRatio	float	linear_scale	Ratio				The ratio of the change in positive to the change in negative pairs
+CHPDec	integer	linear_scale	SpecializedQuantitationType				The number of decreasing pairs
+CHPDecRatio	float	linear_scale	Ratio				The ratio of decreasing pairs to the total number of pairs.
+CHPDetection	string_datatype	unscaled	PresentAbsent				A measurement indicating if the transcript was detected (Present), not detected (Absent) or marginally detected (Marginal). Can be one of "A", "M", "P" or "No Call".
+CHPDetectionPvalue	float	linear_scale	PValue		CHPDetection		See Detection p-value
+CHPDiffCall	string_datatype	unscaled	PresentAbsent				The comparison change call
+CHPFoldChange	float	linear_scale	Ratio				The fold change
+CHPInc	integer	linear_scale	SpecializedQuantitationType				The number of increasing pairs.
+CHPIncDecRatio	float	linear_scale	Ratio				The ratio of increase to decrease pairs.
+CHPIncRatio	float	linear_scale	Ratio				The ratio of increasing pairs to the total number of pairs
+CHPLogAvg	float	log_base_10	DerivedSignal				Average log ratio
+CHPLogAvgRatioChange	float	log_base_10	SpecializedQuantitationType				The change in log average ratio
+CHPNegChange	integer	linear_scale	SpecializedQuantitationType				The change in negative pairs
+CHPNegative	integer	linear_scale	SpecializedQuantitationType				Number of negative probe pairs
+CHPPairs	integer	linear_scale	SpecializedQuantitationType				The number of probe pairs in the probe set.
+CHPPairsInAvg	integer	linear_scale	SpecializedQuantitationType				Number of probe pairs used in computing the average intensity difference
+CHPPairsUsed	integer	linear_scale	SpecializedQuantitationType				The number of probe pairs in the probe set used in the Detection call.
+CHPPosChange	float	linear_scale	SpecializedQuantitationType				The change in positive pairs.
+CHPPosFraction	float	linear_scale	Ratio				The fraction of positive pairs
+CHPPosNegRatio	float	linear_scale	Ratio				The ratio of the positive to the negative pairs
+CHPPositive	integer	linear_scale	SpecializedQuantitationType				Number of positive probe pairs
+CHPSignal	float	linear_scale	DerivedSignal				A measure of the relative abundance of a transcript.
+CHPSignalLogRatio	float	log_base_2	Ratio				The change in expression level for a transcript between a baseline and an experiment array. This change is expressed as the Log2 ratio.
+CHPSignalLogRatioHigh	float	log_base_2	Ratio				The upper limit of the Signal Log Ratio within a 95% confidence interval.
+CHPSignalLogRatioLow	float	log_base_2	Ratio				The lower limit of the Signal Log Ratio within a 95% confidence interval.
+CHPSortScore	float	linear_scale	SpecializedQuantitationType				The sort score.
+ProbeSetName	string_datatype	unscaled	SpecializedQuantitationType				The name of the probe set.
+Stat Pairs	integer	linear_scale	SpecializedQuantitationType				The number of probe pairs in the probe set.
+Stat Pairs Used	integer	linear_scale	SpecializedQuantitationType				The number of probe pairs in the probe set used in the Detection call.
+>>>AppliedBiosystems[Celera]							
+Assay_Normalized_Signal	float		DerivedSignal				
+CV	float		SpecializedQuantitationType				
+Flags	integer	unscaled	SpecializedQuantitationType				
+SDEV	float	linear_scale	MeasuredSignal				
+S_N	float		SpecializedQuantitationType				
+Signal	float	linear_scale	MeasuredSignal				
+>>>ArrayGauge[FUJIFILM]							
+Dens.(Total PSL)	float	linear_scale	MeasuredSignal	0			Total phospho stimulated luminescence.
+Norm Data	float	log_base_2	DerivedSignal				
+Overshining Status	boolean	unscaled	SpecializedQuantitationType				During quantification the analysis spots that display false positive intensity due to non-specific signal from neighboring clones are excluded.
+Total PSL	float	linear_scale	SpecializedQuantitationType	0			Total phospho stimulated luminescence.
+>>>ArrayVision[Imaging Research]							
+% At Ceiling - Bkgd_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Clipped pixels - A quality control measure that reports the proportion of pixels at the uppermost limit (ceiling) of the density scale. The presence of clipped pixels suggest a problem during image formation, and can affect the validity of density data. V
+% At Ceiling - Bkgd_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Clipped pixels - A quality control measure that reports the proportion of pixels at the uppermost limit (ceiling) of the density scale. The presence of clipped pixels suggest a problem during image formation, and can affect the validity of density data. V
+% At Ceiling_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Clipped pixels - A quality control measure that reports the proportion of pixels at the uppermost limit (ceiling) of the density scale. The presence of clipped pixels suggest a problem during image formation, and can affect the validity of density data.
+% At Ceiling_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Clipped pixels - A quality control measure that reports the proportion of pixels at the uppermost limit (ceiling) of the density scale. The presence of clipped pixels suggest a problem during image formation, and can affect the validity of density data.
+% At Floor - Bkgd_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Clipped pixels - A quality control measure that reports the proportion of pixels at the lowermost limit (floor) of the density scale. The presence of clipped pixels suggest a problem during image formation, and can affect the validity of density data. Val
+% At Floor - Bkgd_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Clipped pixels - A quality control measure that reports the proportion of pixels at the lowermost limit (floor) of the density scale. The presence of clipped pixels suggest a problem during image formation, and can affect the validity of density data. Val
+% At Floor_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Clipped pixels - A quality control measure that reports the proportion of pixels at the lowermost limit (floor) of the density scale. The presence of clipped pixels suggest a problem during image formation, and can affect the validity of density data.
+% At Floor_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Clipped pixels - A quality control measure that reports the proportion of pixels at the lowermost limit (floor) of the density scale. The presence of clipped pixels suggest a problem during image formation, and can affect the validity of density data.
+% Replaced (AR VOL)_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Percentage of pixels replaced with estimated values in the calculation of AR volume.
+% Replaced (AR VOL)_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Percentage of pixels replaced with estimated values in the calculation of AR volume.
+AR VOL - RFU_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Artifact-removed density value for each spot, multiplied by its area.
+AR VOL - RFU_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Artifact-removed density value for each spot, multiplied by its area.
+Area - 5m2_Cy3	integer	linear_scale	SpecializedQuantitationType			Cy3	Area of the spot, background and/or reference region.
+Area - 5m2_Cy5	integer	linear_scale	SpecializedQuantitationType			Cy5	Area of the spot, background and/or reference region.
+Area - mm2_Cy3	integer	linear_scale	SpecializedQuantitationType			Cy3	Area of the spot, background and/or reference region.
+Area - mm2_Cy5	integer	linear_scale	SpecializedQuantitationType			Cy5	Area of the spot, background and/or reference region.
+Area - �m2_Cy3		linear_scale	SpecializedQuantitationType			Cy3	Area of the spot, background and/or reference region.
+Area - �m2_Cy5		linear_scale	SpecializedQuantitationType			Cy5	Area of the spot, background and/or reference region.
+Bkgd_Cy3	float	linear_scale	MeasuredSignal	1		Cy3	Background density or volume; background volume value is adjusted to the size of spot, where applicable.
+Bkgd_Cy5	float	linear_scale	MeasuredSignal	1		Cy5	Background density or volume; background volume value is adjusted to the size of spot, where applicable.
+Flag_Cy3	boolean	linear_scale	PresentAbsent			Cy3	Select this measure to manually flag data in the data table. The value of this measure can be 0 or 1.
+Flag_Cy5	boolean	linear_scale	PresentAbsent			Cy5	Select this measure to manually flag data in the data table. The value of this measure can be 0 or 1.
+MAD - RFU / 5m2_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Median of absolute deviation - median of the absolute values of deviations from the median density (i.e., the absolute values of pixel densities  median density); it is a measure of the variation around the median density value of the spot.
+MAD - RFU / 5m2_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Median of absolute deviation - median of the absolute values of deviations from the median density (i.e., the absolute values of pixel densities  median density); it is a measure of the variation around the median density value of the spot.
+MAD - RFU / mm2_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Median of absolute deviation - median of the absolute values of deviations from the median density (i.e., the absolute values of pixel densities  median density); it is a measure of the variation around the median density value of the spot.
+MAD - RFU / mm2_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Median of absolute deviation - median of the absolute values of deviations from the median density (i.e., the absolute values of pixel densities  median density); it is a measure of the variation around the median density value of the spot.
+MAD - RFU / �m2_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Median of absolute deviation - median of the absolute values of deviations from the median density (i.e., the absolute values of pixel densities  median density); it is a measure of the variation around the median density value of the spot.
+MAD - RFU / �m2_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Median of absolute deviation - median of the absolute values of deviations from the median density (i.e., the absolute values of pixel densities  median density); it is a measure of the variation around the median density value of the spot.
+Median Dens - Levels_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Median Density value of the spot Levels. ['Gray levels' (Levels) are the raw pixel intensity values upon which all other density/intensity scales are based.]
+Median Dens - Levels_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Median Density value of the spot Levels. ['Gray levels' (Levels) are the raw pixel intensity values upon which all other density/intensity scales are based.]
+Median Dens - RFU / �m2_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	
+Median Dens - RFU / �m2_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	
+S/N_Cy3	float	linear_scale	Ratio	1		Cy3	Signal-to-noise ratio - spot density minus background density, divided by the SD of the background density.
+S/N_Cy5	float	linear_scale	Ratio	1		Cy5	Signal-to-noise ratio - spot density minus background density, divided by the SD of the background density.
+SD - Levels_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Standard deviation of the pixel Levels values.
+SD - Levels_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Standard deviation of the pixel Levels values.
+SD - RFU / 5m2_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Standard deviation of the pixel density values.
+SD - RFU / 5m2_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Standard deviation of the pixel density values.
+SD - RFU / mm2_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Standard deviation of the pixel density values.
+SD - RFU / mm2_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Standard deviation of the pixel density values.
+SD - RFU / �m2_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Standard deviation of the pixel density values.
+SD - RFU / �m2_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Standard deviation of the pixel density values.
+SPOTLABEL							
+Spot labels	integer	linear_scale	SpecializedQuantitationType				
+VOL - Levels x mm2_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	
+VOL - Levels x mm2_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	
+sARMDens_Cy3	float	linear_scale	MeasuredSignal			Cy3	Background corrected ARM Density value. ARM Density value of the spot, minus the background Density value.
+sARMDens_Cy5	float	linear_scale	MeasuredSignal			Cy5	Background corrected ARM Density value. ARM Density value of the spot, minus the background Density value.
+sARVOL_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Subtracted AR volume value (AR volume value of the spot minus the background volume value); the background value is adjusted to the size of the spot when applicable.
+sARVOL_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Subtracted AR volume value (AR volume value of the spot minus the background volume value); the background value is adjusted to the size of the spot when applicable.
+sMedianDens_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Background corrected Median Density value. Median Density value of the spot, minus the background Density value.
+sMedianDens_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Background corrected Median Density value. Median Density value of the spot, minus the background Density value.
+sVOL_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Background corrected Volume value. Volume value of the spot, minus the background Volume value. The background value is adjusted to the size of the spot when applicable. sVOL = DxA_data - ((DxA_bkgd * Area_data)/Area_bkgd)).
+sVOL_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Background corrected Volume value. Volume value of the spot, minus the background Volume value. The background value is adjusted to the size of the spot when applicable. sVOL = DxA_data - ((DxA_bkgd * Area_data)/Area_bkgd)).
+>>>BZScan[TAGC ERM206]							
+Diameter	float	linear_scale	SpecializedQuantitationType				Automatically computed spot diameter.
+Fit_correction	float	linear_scale	SpecializedQuantitationType	1			Background level by unit surface.
+Overshining_correction	float	linear_scale	SpecializedQuantitationType				Correction for overshining by unit radius.
+Q(fit,const)	float	linear_scale	SpecializedQuantitationType				Spot intensity based on user-provided diameter and theoretical fit to the data.
+Q(fit,var)	float	linear_scale	SpecializedQuantitationType				Spot intensity based on computed diameter and and theoretical fit to the data.
+Q(image,const)	float	linear_scale	MeasuredSignal				Spot intensity based on user-provided diameter and measured data.
+Q(image,var)	float	linear_scale	SpecializedQuantitationType				Spot intensity based on computed diameter and measured data.
+QM	float	linear_scale	PValue		Q(image,const)		Quality metric: distance between measured data and theoretical fit.
+SpotQuality	boolean	unscaled	PresentAbsent				Present/Absent (automatic guess).
+>>>BeadStudio[Illumina]							
+MIN_Signal	float	linear_scale	SpecializedQuantitationType				Minimum intensity of the Bead Type/target in the group.
+AVG_Signal	float	linear_scale	MeasuredSignal				Average intensity of the Bead Type/target in the group.
+MAX_Signal	float	linear_scale	SpecializedQuantitationType				Maximum intensity of the Bead Type/target in the group.
+NARRAYS	integer	linear_scale	SpecializedQuantitationType				Number of samples in the group.
+ARRAY_STDEV			SpecializedQuantitationType				Standard deviation associated with sample-to-sample variability within the group (undefined when the group contains a single sample).
+BEAD_STDEV	float	linear_scale	Error		AVG_Signal		Average standard deviation associated with bead-to-bead variability for the samples in the group.
+BEAD_STDERR	float	linear_scale	Error		AVG_Signal		Standard error of the mean of each probe.
+Avg_NBEADS	integer	linear_scale	SpecializedQuantitationType				Average number of beads per bead type representing probes for the gene.
+Detection	float		SpecializedQuantitationType				1-p-value computed from the background model characterizing the chance that the target sequence signal was distinguishable from the negative controls.
+Detection Pval	float		SpecializedQuantitationType				
+DiffScore	float		SpecializedQuantitationType				Statistical confidence that the gene's expression has changed with respect to a reference group.
+Concordance	float		SpecializedQuantitationType				Number of probes showing upregulated signal compared to the number of probes showing downregulated signal.
+>>>BlueFuse[BlueGnome]							
+AMPCH1	float	linear_scale	MeasuredSignal			Cy3	Total signal in Cy3 channel.
+AMPCH2	float	linear_scale	MeasuredSignal			Cy5	Total signal in Cy5 channel.
+AUTO EXCL	string_datatype	unscaled	SpecializedQuantitationType				Indication of whether a spot has been automatically excluded. Yes - it has been excluded, no - it has not been excluded.
+BLOCK	integer	linear_scale	SpecializedQuantitationType				The sequential number of the block on the array.
+CIRCULARITY	float	linear_scale	SpecializedQuantitationType				Estimate of the circularity of the spot, between 0 and 1. The nearer to 1, the more circular the spot.
+CONFIDENCE	float	linear_scale	SpecializedQuantitationType				The Confidence Estimate in the calculated ratio, between 0 and 1.
+FLAG	string_datatype	unscaled	SpecializedQuantitationType				The Confidence Flag calculated from the Confidence Estimate.
+GRID OFFSET	float	linear_scale	SpecializedQuantitationType				Distance in pixels of the spot from the ideal grid position.
+ID	string_datatype	unscaled	SpecializedQuantitationType				The ID of the spot, taken from ID field in GAL file.
+LOG10PRODUCT	float	log_base_10	SpecializedQuantitationType				Log base 10 of the product of total signal in Cy3 channel multiplied by total signal in Cy5 channel.
+LOG10RATIO CH1/CH2	float	log_base_10	SpecializedQuantitationType				Log base 10 of the ratio of total signal in Cy3 channel divided by total signal in Cy5 channel.
+LOG10RATIO CH2/CH1	float	log_base_10	SpecializedQuantitationType				Log base 10 of the ratio of total signal in Cy5 channel divided by total signal in Cy3 channel.
+LOG10SUM	float	log_base_10	SpecializedQuantitationType				Log base 10 of the sum of total signal in Cy3 channel plus total signal in Cy5 channel.
+LOG2PRODUCT	float	log_base_2	SpecializedQuantitationType				Log base 2 of the product of total signal in Cy3 channel multiplied by total signal in Cy5 channel.
+LOG2RATIO CH1/CH2	float	log_base_2	Ratio				Log base 2 of the ratio of total signal in Cy3 channel divided by total signal in Cy5 channel.
+LOG2RATIO CH2/CH1	float	log_base_2	SpecializedQuantitationType				Log base 2 of the ratio of total signal in Cy5 channel divided by total signal in Cy3 channel.
+LOG2SUM	float	log_base_2	SpecializedQuantitationType				Log base 2 of the sum of total signal in Cy3 channel plus total signal in Cy5 channel.
+MAN EXCL	string_datatype	unscaled	SpecializedQuantitationType				Indication of whether a spot has been manually excluded. Yes - it has been manually excluded, no - it has not been manually excluded.
+NAME	string_datatype	unscaled	SpecializedQuantitationType				The name of the spot, taken from Name field in GAL file.
+P ON CH1	float	linear_scale	SpecializedQuantitationType			Cy3	Estimate of whether there is a biological signal in Cy3 channel, between 0 and 1.
+P ON CH2	float	linear_scale	SpecializedQuantitationType			Cy5	Estimate of whether there is a biological signal in Cy5 channel, between 0 and 1.
+PELCOL	integer	linear_scale	SpecializedQuantitationType				Calculated horizontal position of the spot on image in pixels. 1 is the left column of pixels.
+PELROW	integer	linear_scale	SpecializedQuantitationType				Calculated vertical position of the spot on image in pixels. 1 is the top row of pixels.
+PRODUCT	float	linear_scale	SpecializedQuantitationType				Product of total signal in Cy3 channel multiplied by total signal in Cy5 channel.
+QUALITY			SpecializedQuantitationType				
+RADIUS	float	linear_scale	SpecializedQuantitationType				Estimate of the radius of the spot in pixels.
+RATIO CH1/CH2	float	linear_scale	SpecializedQuantitationType				Ratio of total signal in Cy3 channel divided by total signal channel in Cy5 channel.
+RATIO CH2/CH1	float	linear_scale	SpecializedQuantitationType				Ratio of total signal in Cy5 channel divided by total signal channel in Cy3 channel.
+SPOTNUM	integer	linear_scale	SpecializedQuantitationType				The sequential number of the spot on the array.
+SUM	float	linear_scale	SpecializedQuantitationType				Sum of total signal in Cy3 channel plus total signal in Cy5 channel.
+UNIFORMITY	float	linear_scale	SpecializedQuantitationType				Estimate of the uniformity of the spot, between 0 and 1. The nearer to 1 the more coin-shaped the spot, i.e. vertical sides and horizontal surface.
+>>>ChipSkipper[EMBL]							
+Comment	string_datatype	unscaled	SpecializedQuantitationType				Additional description of spot.
+Comp_Channel Cy3	float	linear_scale	DerivedSignal			Cy3	Normalised intensity of Cy3_Vol-BG.
+Comp_Channel Cy5	float	linear_scale	DerivedSignal			Cy5	Normalised intensity of Cy5_Vol-BG.
+Comp_Directional_Ratio-Cy5/Cy3	float	linear_scale	SpecializedQuantitationType				Compensated directional ratio: if Comp_Ratio > 1 then Directional = Ratio, if Ratio < 1 then Directional = -1/Ratio.
+Comp_Log2_Ratio	float	log_base_2	SpecializedQuantitationType				Compensated Log2 ratio: log2 (Comp_Ratio-Cy5/Cy3).
+Comp_Ratio-Cy5/Cy3	float	linear_scale	Ratio				Compensated ratio ( = Comp_Channel Cy5 / Comp_Channel Cy3 ).
+Cy3_Av-Vol/pixel	float	linear_scale	SpecializedQuantitationType			Cy3	Average intensity per pixel in color channel Cy3.
+Cy3_BG-Arith	float	linear_scale	SpecializedQuantitationType	1		Cy3	Arithmetic average for local background in color channel Cy3.
+Cy3_BG-Geom	float	linear_scale	SpecializedQuantitationType	1		Cy3	Geometric average for local background in color channel Cy3.
+Cy3_BG-Median	float	linear_scale	SpecializedQuantitationType	1		Cy3	Median average for local background in color channel Cy3.
+Cy3_BG_Tot	float	linear_scale	MeasuredSignal	1		Cy3	Total integrated background in color channel Cy3.
+Cy3_SNR1	float	linear_scale	Error		Cy3_Vol-BG	Cy3	Sdev of local background average in color channel Cy3.
+Cy3_SNR1_Score	float	linear_scale	Error		Cy3_Vol-BG	Cy3	Quality value based on intensity and background for color channel Cy3 (range: 0 = bad....1 = good).
+Cy3_Sat-pixel	float	linear_scale	SpecializedQuantitationType			Cy3	Number of saturated pixel in color channel Cy3.
+Cy3_Spot-area	float	linear_scale	SpecializedQuantitationType			Cy3	Area of spot in colour channel Cy3 in pixels.
+Cy3_Vol-BG	float	linear_scale	SpecializedQuantitationType	1		Cy3	Background reduced intensity of colour channel Cy3.
+Cy3_Volume	integer	linear_scale	MeasuredSignal			Cy3	Total integrated signal intensity in color channel Cy3.
+Cy5_Av-Vol/pixel	float	linear_scale	SpecializedQuantitationType			Cy5	Average intensity per pixel in color channel Cy5.
+Cy5_BG-Arith	float	linear_scale	SpecializedQuantitationType	1		Cy5	Arithmetic average for local background in color channel Cy5.
+Cy5_BG-Geom	float	linear_scale	SpecializedQuantitationType	1		Cy5	Geometric average for local background in color channel Cy5.
+Cy5_BG-Median	float	linear_scale	SpecializedQuantitationType	1		Cy5	Median average for local background in color channel Cy5.
+Cy5_BG_Tot	float	linear_scale	MeasuredSignal	1		Cy5	Total integrated background in color channel Cy5.
+Cy5_SNR1	float	linear_scale	Error		Cy5_Vol-BG	Cy5	Sdev of local background average in color channel Cy5.
+Cy5_SNR1_Score	float	linear_scale	Error		Cy5_Vol-BG	Cy5	Quality value based on intensity and background for color channel Cy5 (range: 0 = bad....1 = good).
+Cy5_Sat-pixel	float	linear_scale	SpecializedQuantitationType			Cy5	Number of saturated pixel in color channel Cy5.
+Cy5_Spot-area	float	linear_scale	SpecializedQuantitationType			Cy5	Area of spot in colour channel Cy5 in pixels.
+Cy5_Vol-BG	float	linear_scale	SpecializedQuantitationType	1		Cy5	Background reduced intensity of colour channel Cy5.
+Cy5_Volume	integer	linear_scale	MeasuredSignal			Cy5	Total integrated signal intensity in color channel Cy5.
+Directional_Ratio-Cy5/Cy3	float	linear_scale	SpecializedQuantitationType				Directional ratio: if Ratio > 1 then Directional = Ratio, if Ratio < 1 then Directional = -1/Ratio.
+Flags	string_datatype	unscaled	SpecializedQuantitationType				Quality Flags: SNR1,SNR2 = low signal quality in color channel Cy5, Cy3; GS = low geometric score; MB = manually flagged bad.
+GS_Flags	string_datatype	linear_scale	SpecializedQuantitationType				P = pass or M = marginal flag.
+Group name	string_datatype	unscaled	SpecializedQuantitationType				Group name for this spot.
+Image-x-Pos	integer	linear_scale	SpecializedQuantitationType				Image coordinate of spot.
+Image-y-Pos	integer	linear_scale	SpecializedQuantitationType				Image coordinate of spot.
+Line_Counter	integer	unscaled	SpecializedQuantitationType				Line counter
+Log2_Ratio-Cy5/Cy3	float	log_base_2	Ratio				Log2 ratio: log2 (Ratio-Cy5/Cy3)
+Median of ratios	float	linear_scale	SpecializedQuantitationType				Median of ratios (Cy5/Cy3) from all pixel in this spot.
+Median of ratios % SDev	float	linear_scale	SpecializedQuantitationType				Standard deviation of Median of ratios in percent.
+Ratio of medians	float	linear_scale	SpecializedQuantitationType				Ratio of Medians (Cy5/Cy3) from all pixel in this spot.
+Ratio of medians % SDev	float	linear_scale	SpecializedQuantitationType				Standard deviation of Ratio of medians in percent.
+Ratio-Cy5/Cy3	float	linear_scale	SpecializedQuantitationType				Direct ratio ( = Cy5_Vol-BG / Cy3_Vol-BG )
+Regulation	string_datatype	unscaled	SpecializedQuantitationType				If Flag = SNR1 then Regulation=>Down; if Flag = SNR2 then Regulation=>up; if Flag = SNR1 AND SNR2 then Regulation=>weak; else Regulation = Ratio-Cy5/Cy3.
+Rep_vol_BG2	float	linear_scale	SpecializedQuantitationType				Average of background reduced volume for onchip replicas (when calculated).
+Replicas	float	linear_scale	SpecializedQuantitationType				
+SNR*GS	float	linear_scale	SpecializedQuantitationType				Number of saturated pixel in both colors (logical or).
+SNR-tot	float	linear_scale	SpecializedQuantitationType				Squared average of Cy5_SNR and Cy3_SNR.
+SNR-tot_Score	float	linear_scale	SpecializedQuantitationType				Squared average of Cy5_%SNR and Cy3_%SNR.
+Tot_Sat-pixel	float	linear_scale	SpecializedQuantitationType				Number of saturated pixel in color channel Cy5 OR color channel Cy3.
+>>>CodeLink[Motorola Life Sciences]							
+Cy3 BG Mean	float	linear_scale	MeasuredSignal	1		Cy3	Background mean in Cy3 channel.
+Cy3 BG Median	integer	linear_scale	SpecializedQuantitationType	1		Cy3	Background median in Cy3 channel.
+Cy3 Mean	float	linear_scale	SpecializedQuantitationType			Cy3	Statistical mean (average) of the raw intensity signal (i.e. mean of all the pixels belonging to the feature region) in Cy3 channel.
+Cy3 Raw Intensity	float	linear_scale	MeasuredSignal			Cy3	The signal from the spot minus the median background in Cy3 channel.
+Cy5 BG Mean	float	linear_scale	MeasuredSignal	1		Cy5	Background mean in Cy5 channel.
+Cy5 BG Median	integer	linear_scale	SpecializedQuantitationType	1		Cy5	Background median in Cy5 channel.
+Cy5 Mean	float	linear_scale	SpecializedQuantitationType			Cy5	Statistical mean (average) of the raw intensity signal (i.e. mean of all the pixels belonging to the feature region) in Cy5 channel.
+Cy5 Raw Intensity	float	linear_scale	MeasuredSignal			Cy5	The signal from the spot minus the median background in Cy5 channel.
+Flag	boolean	unscaled	SpecializedQuantitationType				Flag indicates if the spot matched with quality measures (i.e shape, low signal, saturated). TRUE corresponds to features that have to be discarded according to the quality measures, while FALSE is the opposite.
+>>>Spot[CSIRO]							
+# These first are just feature coordinate/index columns, and should be discarded.							
+#indexs							The index of the spot.
+#grid_r							The row index of the grid.
+#grid_c							The column index of the grid.
+#spot_r							The row index of the spot within the its grid.
+#spot_c							The column index of the spot within the its grid.
+area	integer	linear_scale	SpecializedQuantitationType				The number of pixels in the segmented spot.
+Gmean	float	linear_scale	SpecializedQuantitationType	0		Cy3	The average pixel value within the spot: green band.
+Gmedian	float	linear_scale	MeasuredSignal	0		Cy3	The median pixel value within the spot: green band.
+GIQR	float			0		Cy3	The interquartile range of the logged pixel values within the spot: green band.
+Rmean	float	linear_scale	SpecializedQuantitationType	0		Cy5	The average pixel value within the spot: red band.
+Rmedian	float	linear_scale	MeasuredSignal	0		Cy5	The median pixel value within the spot: red band.
+RIQR	float			0		Cy5	The interquartile range of the logged pixel values within the spot: red band.
+bg.Gmean	float	linear_scale	SpecializedQuantitationType	1		Cy3	The average pixel value in the background region: green band.
+bg.Gmedian	float	linear_scale	MeasuredSignal	1		Cy3	The median pixel value in the background region: green band.
+bg.Gsd	float			1		Cy3	
+bg.GIQR	float			1		Cy3	The interquartile range of the logged pixel values in the background region: green band.
+bg.Rmean	float	linear_scale	SpecializedQuantitationType	1		Cy5	The average pixel value in the background region: red band.
+bg.Rmedian	float	linear_scale	MeasuredSignal	1		Cy5	The median pixel value in the background region: red band.
+bg.Rsd	float			1		Cy5	
+bg.RIQR	float			1		Cy5	The interquartile range of the logged pixel values in the background region: red band.
+valleyG			SpecializedQuantitationType			Cy3	Median of median image values in four neighbouring "valley" regions: green band. "Valley" regions are square regions between spots.
+valleyR			SpecializedQuantitationType			Cy5	Median of median image values in four neighbouring "valley" regions: red band.
+morphG	integer	linear_scale	SpecializedQuantitationType			Cy3	Value of a morphological opening of the green band image at the nominal spot centre. Openings and erosions (see below) use a rectangular structuring element with side lengths equal to twice the average horizontal and vertical distances between spots.
+morphG.erode			SpecializedQuantitationType			Cy3	Value of a morphological erosion of the green band image at the nominal spot centre.
+morphG.close.open	integer	linear_scale	SpecializedQuantitationType			Cy3	Value of a morphological closing followed by a morphological opening of the green band image at the nominal spot centre.
+morphR 	integer	linear_scale	SpecializedQuantitationType			Cy5	Value of a morphological opening of the red band image at the nominal spot centre.
+morphR.erode			SpecializedQuantitationType			Cy5	Value of a morphological erosion of the red band image at the nominal spot centre.
+morphR.close.open	integer	linear_scale	SpecializedQuantitationType			Cy5	Value of a morphological closing followed by a morphological opening of the red band image at the nominal spot centre.
+logratios	float	log_base_2	Ratio	0			The base-2 log of the background-adjusted ratio, R/G, using R/Gmedian for spot value and morphR/G for background.
+perimeter	integer	linear_scale	SpecializedQuantitationType				The perimeter of the spot as measured by the number of pixels in the spot which have a neighbouring pixel outside the spot. Each pixel is considered to have 8 neighbours in this process.
+circularity			SpecializedQuantitationType				A measure of circularity of the spot equal to area times four pi divided by perimeter squared. This is ideally one for circles and more than one for other shapes.
+badspot			SpecializedQuantitationType				A flag which is one if the spot area is greater than the product of the horizontal and vertical average spot separations, and zero otherwise.
+>>>CodeLink Expression Analysis[GE Healthcare]							
+Raw_intensity	float	linear_scale	SpecializedQuantitationType	0			Spot density, which is the difference between the spot mean and the local background median.
+Normalized_intensity	float	linear_scale	DerivedSignal	0			Normalized intensity value (the raw intensity divided by the normalization factor).
+Quality_flag	string_datatype	unscaled	SpecializedQuantitationType	0			Metrics the software uses to indicate the quality of a spot.
+Signal_strength	float	linear_scale	Ratio	0			Estimated signal above its local background noise level. The ratio is expressed as the spot mean divided by the local background standard deviation.
+Spot_mean	float	linear_scale	MeasuredSignal	0			Sum of all pixel values in the spot area divided by the number of pixels.
+Spot_median	integer	linear_scale	SpecializedQuantitationType	0			Median pixel intensity computed over the spot area.
+Spot_stdev	float	linear_scale	Error	0	Spot_mean		Standard deviation of the pixel intensity in the spot.
+Spot_area	integer	linear_scale	SpecializedQuantitationType	0			Number of pixels in the spot.
+Spot_diameter	float	linear_scale	SpecializedQuantitationType	0			Spot diameter (in pixels) that the software calculates during data extraction.
+Spot_noise_level	float	linear_scale	SpecializedQuantitationType	1			Local background noise level, given by the median signal of the pixels within the baackground ring.
+Bkgd_mean	float	linear_scale	MeasuredSignal	1			Estimate of the background mean that the software computes, using the pixel intensities in the background area surrounding the spot.
+Bkgd_median	integer	linear_scale	SpecializedQuantitationType	1			Estimate of the background median the software computes, using the pixel intensities inside the background area surrounding the spot.
+Bkgd_stdev	float	linear_scale	Error	1	Bkgd_mean		Estimate of the background standard deviation that the software computes, using the pixel intensities inside the background area surrounding the spot.
+Bkgd_area	float	linear_scale	SpecializedQuantitationType	1			Number of pixels that the software includes to compute the estimated local background mean, median, and standard deviation.
+>>>Feature Extraction Software[Agilent Technologies]							
+BGPixCorrelation	float	linear_scale	SpecializedQuantitationType				Ratio of estimated feature covariance in RedGreen space to product of feature Standard Deviation in Red Green space. The covariance of two features measures their tendency to vary together, i.e. to co-vary. In this case, it is a cumulative quantitation of
+BGSubSigCorrelation	float	linear_scale	SpecializedQuantitationType				Ratio of estimated background subtracted feature signal covariance in RG space to product of background subtracted feature Standard Deviation in RG space.
+DyeNormCorrelation	float	linear_scale	SpecializedQuantitationType				Dye normalized red and green pixel correlation.
+ErrorModel	integer	linear_scale	SpecializedQuantitationType				ErrorModel Indicates the error model that the user chose for feature extraction. A 0 indicates use of the Propogated Error Model. A 1 indicates use of the Universal Error Model.
+Failed	boolean	linear_scale	Failed				Set to one if the quantitation values of a feature are considered bad enough to thrown out in the higher level analysis. Features are thrown out if both channels are saturated or if either channel is a non-uniform outlier. A feature can be failed from out
+Green.BGPixSDev	float	linear_scale	Error		Green.Background	Cy3	
+Green.Background	float	linear_scale	MeasuredSignal	1		Cy3	
+Green.DerivedSignal	float	linear_scale	DerivedSignal			Cy3	
+Green.DyeNormSignal	float	linear_scale	SpecializedQuantitationType			Cy3	
+Green.MeasuredSignal	float	linear_scale	MeasuredSignal			Cy3	
+Green.PixSDev	float	linear_scale	Error		Green.MeasuredSignal	Cy3	
+Green.ProcessedSigError	float	linear_scale	Error		Green.DerivedSignal	Cy3	
+HighEndCorrDNCorrelation	float	linear_scale	SpecializedQuantitationType				
+HighEndDyeNormApplied	boolean	linear_scale	SpecializedQuantitationType				Has a value of 0 or 1.
+IsManualFlag	boolean	linear_scale	SpecializedQuantitationType				If a feature has been moved manually this flag will be set to 1.
+IsNormalization	boolean	linear_scale	SpecializedQuantitationType				A boolean flag which indicates if a feature is used to measure dye bias. A 1 incidates the value was used.
+IsUsedBGAdjust	float	linear_scale	SpecializedQuantitationType				A boolean used to flag features used for computation of global BG offset. A 1 indicates the feature was used.
+LogRatio	float	log_base_10	Ratio				log(REDsignal/GREENsignal) per feature. Signals in both channels are post dye normalization If SURROGATES are disabled, then this value will be set to: -2 if DyeNormRedSig <= 0.0 &DyeNormGreenSig > 0.0; 2 if DyeNormRedSig > 0.0 & DyeNormGreenSi
+LogRatioError	float	log_base_10	Error		LogRatio		LogRatioError = error of the log ratio calculated according to the error model chosen. If SURROGATES are disabled, then this value will be set to if DyeNormRedSig <= 0.0 OR DyeNormGreenSig <= 0.0
+PValueLogRatio	float	percent	PValue		LogRatio		Significance level of the Log Ratio computed for a feature
+PixCorrelation	float	linear_scale	SpecializedQuantitationType				Ratio of estimated feature covariance in RedGreen space to product of feature Standard Deviation in Red Green space. The covariance of two features measures their tendency to vary together, i.e. to co-vary. In this case, it is a cumulative quantitation of
+PositionX	float	linear_scale	SpecializedQuantitationType				Found X coordinate of the feature centroid currently reported in pixels.
+PositionY	float	linear_scale	SpecializedQuantitationType				Found Y coordinate of the feature centroid currently reported in pixels.
+Red.BGPixSDev	float	linear_scale	Error		Red.Background	Cy5	
+Red.Background	float	linear_scale	MeasuredSignal	1		Cy5	
+Red.DerivedSignal	float	linear_scale	DerivedSignal			Cy5	
+Red.DyeNormSignal	float	linear_scale	SpecializedQuantitationType			Cy5	
+Red.MeasuredSignal	float	linear_scale	MeasuredSignal			Cy5	
+Red.PixSDev	float	linear_scale	Error		Red.MeasuredSignal	Cy5	
+Red.ProcessedSigError	float	linear_scale	Error		Red.DerivedSignal	Cy5	
+SpotExtentX	float	linear_scale	SpecializedQuantitationType				The size of a spot in X in microns.
+SpotExtentY	float	linear_scale	SpecializedQuantitationType				The size of a spot in Y in microns.
+gBGMeanSignal	float	linear_scale	MeasuredSignal	1		Cy3	Mean local background signal (local to corresponding feature) computed per channel.
+gBGMedianSignal	float	linear_scale	SpecializedQuantitationType	1		Cy3	Median local background signal (local to corresponding feature) computed per channel.
+gBGNumPix	integer	linear_scale	SpecializedQuantitationType	1		Cy3	Total number of pixels used to compute Local BG statistics per spot; ie. Total number of BG inlier pixels. This number is computed independently in each channel.
+gBGPixSDev	float	linear_scale	Error	1	gBGMeanSignal	Cy3	Standard deviation of all inlier pixels per Local BG of each feature, computed independently in each channel.
+gBGSDUsed	float	linear_scale	Error	1	gBGUsed	Cy3	Standard deviation of background used in green channel.
+gBGSubSigError	float	linear_scale	Error		gBGSubSignal	Cy3	Propagated standard error as computed on net green background subtracted signal.
+gBGSubSignal	float	linear_scale	SpecializedQuantitationType			Cy3	The net green signal following the subtraction of the background from the raw mean green signal.
+gBGUsed	float	linear_scale	SpecializedQuantitationType	1		Cy3	Background value subtracted from the raw mean signal to generate the BG subtracted signal; this value is computed per channel. If global BG subtraction is used the column is identical for every feature in a given channel.
+gBackground	float	linear_scale	MeasuredSignal	1		Cy3	Mean local background signal (local to corresponding feature) computed per channel.
+gDerivedSignal	float	linear_scale	DerivedSignal			Cy3	The propagated feature signal, per channel, used for computation of log ratio.
+gDyeNormError	float	linear_scale	Error		gDyeNormSignal	Cy3	The standard error associated with the dye normalized signal.
+gDyeNormSignal	float	linear_scale	SpecializedQuantitationType			Cy3	The dye normalized signal in the indicated channel.
+gHighEndCorrDNError	float	linear_scale	Error		gHighEndCorrDNSig	Cy3	
+gHighEndCorrDNSig	float	linear_scale	SpecializedQuantitationType			Cy3	
+gIsBGNonUnifOL	boolean	linear_scale	SpecializedQuantitationType			Cy3	Boolean flag indicating if a background is a NonUniformity Outlier or not. A feature is non-uniform if the pixel noise of feature exceeds a threshold established for a "uniform" feature. A 1 indicates Feature is a non-uniformity outlier.
+gIsBGPopnOL	boolean	linear_scale	SpecializedQuantitationType			Cy3	Boolean flag indicating if a background is a Population Outlier or not. Probes with replicate features on a microarray are examined using population statistics. A feature is a population outlier if its signal is less than a lower threshold or exceeds an u
+gIsFeatNonUnifOL	boolean	linear_scale	SpecializedQuantitationType			Cy3	Boolean flag indicating if a feature is a NonUniformity Outlier or not. A feature is non-uniform if the pixel noise of feature exceeds a threshold established for a "uniform" feature. A 1 indicates Feature is a non-uniformity outlier.
+gIsFeatPopnOL	boolean	linear_scale	SpecializedQuantitationType			Cy3	Boolean flag indicating if a feature is a Population Outlier or not. Probes with replicate features on a microarray are examined using population statistics. A feature is a population outlier if its signal is less than a lower threshold or exceeds an uppe
+gIsFound	boolean	linear_scale	SpecializedQuantitationType			Cy3	A boolean used to flag found (strong) features. The flag is applied independently in each channel. A feature is considered found if the found spot centroid is within the bounds of the spot deviation limit with respect to corresponding nominal centroid. NO
+gIsInNegCtrlRange	boolean	linear_scale	SpecializedQuantitationType			Cy3	Boolean that identifies this spot as being in the range of background.
+gIsPosAndSignif	boolean	linear_scale	SpecializedQuantitationType			Cy3	Boolean flag indicating if the mean signal of a feature is greater than the corresponding background and if this difference is significant. Significance is established via a 2-sided t-test against the user-settable maximum p-value. A 1 indicates Feature i
+gIsSaturated	boolean	linear_scale	SpecializedQuantitationType			Cy3	Boolean flag indicating if a feature is saturated or not. A feature is saturated if 50% of the pixels in a feature are above the saturation threshold. A value of 1 means saturated.
+gIsWellAboveBG	boolean	linear_scale	SpecializedQuantitationType			Cy3	Boolean flag indicating if a feature is well above background or not. A feature must be gIsPosAndSignif and additionally the gBGSubSignal must be greater than 2.6*g(r)BG_SD. A 1 indicates a feature IsWellAboveBG.
+gMeanSignal	float	linear_scale	MeasuredSignal			Cy3	Raw mean signal of feature in green channel.
+gMeasuredSignal	float	linear_scale	MeasuredSignal			Cy3	Raw mean signal of feature in green channel.
+gMedianSignal	float	linear_scale	SpecializedQuantitationType			Cy3	Raw median signal of feature in green channel.
+gNetSignal	float	linear_scale	SpecializedQuantitationType			Cy3	The meanSignal - the scanner offset.
+gNumBGUsed	integer	linear_scale	SpecializedQuantitationType	1		Cy3	A count of the number of background pixels used in the green channel.
+gNumPix	integer	linear_scale	SpecializedQuantitationType			Cy3	Total number of pixels used to compute feature statistics; ie. total number of inlier pixels/per spot, computed independently in each channel. The number of inlier pixels are the same in both channels.
+gNumPixOLHi	integer	linear_scale	SpecializedQuantitationType			Cy3	Number of outlier pixels per feature with intensity > upper threshold set via the pixel outlier rejection method. The number is computed independently in each channel. These pixels are omitted from all subsequent calculations.
+gNumPixOLLo	integer	linear_scale	SpecializedQuantitationType			Cy3	Number of outlier pixels per feature with intensity < lower threshold set via the pixel outlier rejection method. The number is computed independently in each channel. NOTE: The pixel outlier method is the ONLY step that removes data in Feature Extract
+gNumSatPix	integer	linear_scale	SpecializedQuantitationType			Cy3	Total number of saturated pixels per feature, computed per channel.
+gPValFeatEqBG	float	linear_scale	SpecializedQuantitationType			Cy3	P-value from t-test of significance between green Mean signal and green background.
+gPixSDev	float	linear_scale	Error		gMeanSignal	Cy3	Standard deviation of all inlier pixels per feature; this is computed independently in each channel.
+gProcessedSigError	float	linear_scale	Error		gProcessedSignal	Cy3	Standard error of propagated feature signal, per channel.
+gProcessedSignal	float	linear_scale	DerivedSignal			Cy3	The propagated feature signal, per channel, used for computation of log ratio.
+gSpatialDetrendIsInFilteredSet	boolean	linear_scale	SpecializedQuantitationType			Cy3	Set to true for a given feature if it is part of the filtered set used to fit the surface measuring the spatial trend across the microarray. This feature is considered part of the locally weighted x% of the features.
+gSpatialDetrendSurfaceValue	float	linear_scale	SpecializedQuantitationType			Cy3	Value of the smoothed surface derived from the set of features that are part of SpatialDetrendIsInFilteredSet.
+gSurrogateUsed	float	linear_scale	SpecializedQuantitationType			Cy3	The green surrogate value used.
+rBGMeanSignal	float	linear_scale	MeasuredSignal	1		Cy5	Mean local background signal (local to corresponding feature) computed per channel.
+rBGMedianSignal	float	linear_scale	SpecializedQuantitationType	1		Cy5	Median local background signal (local to corresponding feature) computed per channel.
+rBGNumPix	integer	linear_scale	SpecializedQuantitationType	1		Cy5	Total number of pixels used to compute Local BG statistics per spot; ie. Total number of BG inlier pixels. This number is computed independently in each channel.
+rBGPixSDev	float	linear_scale	Error	1	rBGMeanSignal	Cy5	Standard deviation of all inlier pixels per Local BG of each feature, computed independently in each channel.
+rBGSDUsed	float	linear_scale	Error	1	rBGUsed	Cy5	Standard deviation of background used in red channel.
+rBGSubSigError	float	linear_scale	Error		rBGSubSignal	Cy5	Propagated standard error as computed on net red background subtracted signal.
+rBGSubSignal	float	linear_scale	SpecializedQuantitationType			Cy5	The net red signal following the subtraction of the background from the raw mean red signal.
+rBGUsed	float	linear_scale	SpecializedQuantitationType	1		Cy5	Background value subtracted from the raw mean signal to generate the BG subtracted signal; this value is computed per channel. If global BG subtraction is used the column is identical for every feature in a given channel.
+rBackground	float	linear_scale	MeasuredSignal	1		Cy5	Mean local background signal (local to corresponding feature) computed per channel.
+rDerivedSignal	float	linear_scale	DerivedSignal			Cy5	The propagated feature signal, per channel, used for computation of log ratio.
+rDyeNormError	float	linear_scale	Error		rDyeNormSignal	Cy5	The standard error associated with the dye normalized signal.
+rDyeNormSignal	float	linear_scale	SpecializedQuantitationType			Cy5	The dye normalized signal in the indicated channel.
+rHighEndCorrDNError	float	linear_scale	Error		rHighEndCorrDNSig	Cy5	
+rHighEndCorrDNSig	float	linear_scale	SpecializedQuantitationType			Cy5	
+rIsBGNonUnifOL	boolean	linear_scale	SpecializedQuantitationType			Cy5	Boolean flag indicating if a background is a NonUniformity Outlier or not. A feature is non-uniform if the pixel noise of feature exceeds a threshold established for a "uniform" feature. A 1 indicates Feature is a non-uniformity outlier.
+rIsBGPopnOL	boolean	linear_scale	SpecializedQuantitationType			Cy5	Boolean flag indicating if a background is a Population Outlier or not. Probes with replicate features on a microarray are examined using population statistics. A feature is a population outlier if its signal is less than a lower threshold or exceeds an u
+rIsFeatNonUnifOL	boolean	linear_scale	SpecializedQuantitationType			Cy5	Boolean flag indicating if a feature is a NonUniformity Outlier or not. A feature is non-uniform if the pixel noise of feature exceeds a threshold established for a "uniform" feature. A 1 indicates Feature is a non-uniformity outlier.
+rIsFeatPopnOL	boolean	linear_scale	SpecializedQuantitationType			Cy5	Boolean flag indicating if a feature is a Population Outlier or not. Probes with replicate features on a microarray are examined using population statistics. A feature is a population outlier if its signal is less than a lower threshold or exceeds an uppe
+rIsFound	boolean	linear_scale	SpecializedQuantitationType			Cy5	A boolean used to flag found (strong) features. The flag is applied independently in each channel. A feature is considered found if the found spot centroid is within the bounds of the spot deviation limit with respect to corresponding nominal centroid. NO
+rIsInNegCtrlRange	boolean	linear_scale	SpecializedQuantitationType			Cy5	Boolean that identifies this spot as being in the range of background.
+rIsPosAndSignif	boolean	linear_scale	SpecializedQuantitationType			Cy5	Boolean flag indicating if the mean signal of a feature is greater than the corresponding background and if this difference is significant. Significance is established via a 2-sided t-test against the user-settable maximum p-value. A 1 indicates Feature i
+rIsSaturated	boolean	linear_scale	SpecializedQuantitationType			Cy5	Boolean flag indicating if a feature is saturated or not. A feature is saturated if 50% of the pixels in a feature are above the saturation threshold. A value of 1 means saturated.
+rIsWellAboveBG	boolean	linear_scale	SpecializedQuantitationType			Cy5	Boolean flag indicating if a feature is well above background or not. A feature must be gIsPosAndSignif and additionally the gBGSubSignal must be greater than 2.6*g(r)BG_SD. A 1 indicates a feature IsWellAboveBG.
+rMeanSignal	float	linear_scale	MeasuredSignal			Cy5	Raw mean signal of feature in red channel.
+rMeasuredSignal	float	linear_scale	MeasuredSignal			Cy5	Raw mean signal of feature in red channel.
+rMedianSignal	float	linear_scale	SpecializedQuantitationType			Cy5	Raw median signal of feature in red channel.
+rNetSignal	float	linear_scale	SpecializedQuantitationType			Cy5	The meanSignal - the scanner offset.
+rNumBGUsed	integer	linear_scale	SpecializedQuantitationType	1		Cy5	A count of the number of background pixels used in the red channel.
+rNumPix	integer	linear_scale	SpecializedQuantitationType			Cy5	Total number of pixels used to compute feature statistics; ie. total number of inlier pixels/per spot, computed independently in each channel. The number of inlier pixels are the same in both channels.
+rNumPixOLHi	integer	linear_scale	SpecializedQuantitationType			Cy5	Number of outlier pixels per feature with intensity > upper threshold set via the pixel outlier rejection method. The number is computed independently in each channel. These pixels are omitted from all subsequent calculations.
+rNumPixOLLo	integer	linear_scale	SpecializedQuantitationType			Cy5	Number of outlier pixels per feature with intensity < lower threshold set via the pixel outlier rejection method. The number is computed independently in each channel. NOTE: The pixel outlier method is the ONLY step that removes data in Feature Extract
+rNumSatPix	integer	linear_scale	SpecializedQuantitationType			Cy5	Total number of saturated pixels per feature, computed per channel.
+rPValFeatEqBG	float	linear_scale	SpecializedQuantitationType			Cy5	P-value from t-test of significance between red Mean signal and red background.
+rPixSDev	float	linear_scale	Error		rMeanSignal	Cy5	Standard deviation of all inlier pixels per feature; this is computed independently in each channel.
+rProcessedSigError	float	linear_scale	Error		rProcessedSignal	Cy5	Standard error of propagated feature signal, per channel.
+rProcessedSignal	float	linear_scale	DerivedSignal			Cy5	The propagated feature signal, per channel, used for computation of log ratio.
+rSpatialDetrendIsInFilteredSet	boolean	linear_scale	SpecializedQuantitationType			Cy5	Set to true for a given feature if it is part of the filtered set used to fit the surface measuring the spatial trend across the microarray. This feature is considered part of the locally weighted x% of the features.
+rSpatialDetrendSurfaceValue	float	linear_scale	SpecializedQuantitationType			Cy5	Value of the smoothed surface derived from the set of features that are part of SpatialDetrendIsInFilteredSet.
+rSurrogateUsed	float	linear_scale	SpecializedQuantitationType			Cy5	The red surrogate value used.
+xDev	float	log_base_10	SpecializedQuantitationType				Used to compute the Log Ratio PValue.
+>>>GEMTools[Incyte Genomics]							
+BalancedDiffExpr	float	linear_scale	SpecializedQuantitationType				Balanced fold change.
+CloneID	string_datatype	unscaled	SpecializedQuantitationType				Incyte identifier for individual clone spotted on the array.
+CloneSource	string_datatype	unscaled	SpecializedQuantitationType				All arrayed elements were either clones derived from Incyte cDNA libraries, or sequences generated specifically to be used as hybridization and/or signal balancing controls.
+DiffExpr	float	linear_scale	SpecializedQuantitationType				Unbalanced fold change.
+GEMID	string_datatype	unscaled	SpecializedQuantitationType				Unique identifier.
+GeneID	string_datatype	unscaled	SpecializedQuantitationType				Incyte unique identifier for a sequence contig.
+GeneName	string_datatype	unscaled	SpecializedQuantitationType				Genbank annotation of Incyte sequence contig.
+IncyteCloneID	string_datatype	unscaled	SpecializedQuantitationType				Incyte identifier for individual clone spotted on the array.
+Location	integer	linear_scale	SpecializedQuantitationType				An Incyte code that can be used to determine the location of the spot on the array.
+P1Area%	integer	linear_scale	SpecializedQuantitationType			Cy5	Area of spot coverage.
+P1Description	string_datatype	unscaled	SpecializedQuantitationType			Cy5	Identifier or code name for Cy5 labeled probe.
+P1S/B	float	linear_scale	SpecializedQuantitationType	1		Cy5	Element signal to background in the Cy5 channel.
+P1Signal	integer	linear_scale	MeasuredSignal			Cy5	Signal intensity in the Cy5 channel.
+P2Area%	integer	linear_scale	SpecializedQuantitationType			Cy3	Area of spot coverage.
+P2BalancedSignal	integer	linear_scale	SpecializedQuantitationType				Balanced Cy3 signal (balanced by total average signal intensity in both channels).
+P2Description	string_datatype	unscaled	SpecializedQuantitationType			Cy3	Identifier or code name for Cy3 labeled probe.
+P2S/B	float	linear_scale	SpecializedQuantitationType	1		Cy3	Element signal to background in the Cy3 channel.
+P2Signal	integer	linear_scale	MeasuredSignal			Cy3	Signal intensity in the Cy3 channel.
+PCRStatus	string_datatype	unscaled	SpecializedQuantitationType				An indicator of the quality of the spotted cDNA.
+Probe1	string_datatype	unscaled	SpecializedQuantitationType			Cy5	Unique identifier for Cy5 labeled probe.
+Probe2	string_datatype	unscaled	SpecializedQuantitationType			Cy3	Unique identifier for Cy3 labeled probe.
+>>>GLEAMS[NuTec Sciences]							
+BG.Cy3	float	linear_scale	MeasuredSignal	1		Cy3	Median pixel intensity computed over the local background region for Cy3.
+BG.Cy5	float	linear_scale	MeasuredSignal	1		Cy5	Median pixel intensity computed over the local background region for Cy5.
+Mean.Sig.Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Pixel intensity averaged over the local signal region for Cy3.
+Mean.Sig.Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Pixel intensity averaged over the local signal region for Cy5.
+Med.Sig.Cy3	integer	linear_scale	MeasuredSignal			Cy3	Median pixel intensity computed over the local signal region for Cy3.
+Med.Sig.Cy5	integer	linear_scale	MeasuredSignal			Cy5	Median pixel intensity computed over the local signal region for Cy5.
+SD.Cy3	float	linear_scale	Error		Mean.Sig.Cy3	Cy3	Standard deviation of pixel intensities over the local signal region for Cy3.
+SD.Cy5	float	linear_scale	Error		Mean.Sig.Cy5	Cy5	Standard deviation of pixel intensities over the local signal region for Cy5.
+Sig.Area	integer	linear_scale	SpecializedQuantitationType				Number of pixels in the local signal region.
+X	integer	linear_scale	SpecializedQuantitationType				Location of the center of the spot on the array on the X-axis.
+Y	integer	linear_scale	SpecializedQuantitationType				Location of the center of the spot on the array on the Y-axis.
+>>>GenePix[Axon Instruments]							
+% > B595+1SD	integer	linear_scale	SpecializedQuantitationType			Cy3	The percentage of feature pixels with intensities more than one standard deviation above the background pixel intensity, at wavelength 595 nm.
+% > B595+2SD	integer	linear_scale	SpecializedQuantitationType			Cy3	The percentage of feature pixels with intensities more than two standard deviations above the background pixel intensity, at wavelength 595 nm.
+% > B685+1SD	integer	linear_scale	SpecializedQuantitationType			Cy5	The percentage of feature pixels with intensities more than one standard deviation above the background pixel intensity, at wavelength 685 nm.
+% > B685+2SD	integer	linear_scale	SpecializedQuantitationType			Cy5	The percentage of feature pixels with intensities more than two standard deviations above the background pixel intensity, at wavelength 685 nm.
+B595	integer	linear_scale	SpecializedQuantitationType	1		Cy3	The actual background value used for the feature in GenePix Pro calculations (as opposed to B595 Median, for example, which is the local median background.) This column is required because GenePix Pro 5.0 has global and negative control background subtrac
+B595 CV	integer	linear_scale	SpecializedQuantitationType	1		Cy3	The coefficient of variation of local background pixel intensity.
+B595 Mean	integer	linear_scale	MeasuredSignal	1		Cy3	The mean feature background intensity at wavelength 595 nm.
+B595 Median	integer	linear_scale	SpecializedQuantitationType	1		Cy3	The median feature background intensity at wavelength 595 nm.
+B595 SD	integer	linear_scale	Error	1	B595 Median	Cy3	The standard deviation of the feature background intensity at wavelength 595 nm.
+B685	integer	linear_scale	SpecializedQuantitationType	1		Cy5	The actual background value used for the feature in GenePix Pro calculations (as opposed to B685 Median, for example, which is the local median background.) This column is required because GenePix Pro 5.0 has global and negative control background subtrac
+B685 CV	integer	linear_scale	SpecializedQuantitationType	1		Cy5	The coefficient of variation of local background pixel intensity.
+B685 Mean	integer	linear_scale	MeasuredSignal	1		Cy5	The mean feature background intensity at wavelength 685 nm.
+B685 Median	integer	linear_scale	SpecializedQuantitationType	1		Cy5	The median feature background intensity at wavelength 685 nm.
+B685 SD	integer	linear_scale	Error	1	B685 Median	Cy5	The standard deviation of the feature background intensity at wavelength 685 nm.
+F595 % Sat.	integer	linear_scale	SpecializedQuantitationType			Cy3	The percentage of feature pixels at wavelength 595 nm that are saturated.
+F595 CV	integer	linear_scale	SpecializedQuantitationType			Cy3	The coefficient of variation of feature pixel intensity.
+F595 Mean	integer	linear_scale	MeasuredSignal			Cy3	Mean feature pixel intensity at wavelength 595 nm.
+F595 Mean - B595	integer	linear_scale	SpecializedQuantitationType			Cy3	The mean feature pixel intensity at wavelength 595 nm with the median background subtracted.
+F595 Median	integer	linear_scale	SpecializedQuantitationType			Cy3	Median feature pixel intensity at wavelength 595 nm.
+F595 Median - B595	integer	linear_scale	SpecializedQuantitationType			Cy3	The median feature pixel intensity at wavelength 595 nm with the median background subtracted.
+F595 SD	integer	linear_scale	Error		F595 Median	Cy3	The standard deviation of the feature intensity at wavelength 595 nm.
+F595 Total Intensity	integer	linear_scale	SpecializedQuantitationType			Cy3	The sum of feature pixel intensities at 595 nm.
+F685 % Sat.	integer	linear_scale	SpecializedQuantitationType			Cy5	The percentage of feature pixels at wavelength 685 nm that are saturated.
+F685 CV	integer	linear_scale	SpecializedQuantitationType			Cy5	The coefficient of variation of feature pixel intensity.
+F685 Mean	integer	linear_scale	MeasuredSignal			Cy5	Mean feature pixel intensity at wavelength 685 nm.
+F685 Mean - B685	integer	linear_scale	SpecializedQuantitationType			Cy5	The mean feature pixel intensity at wavelength 685 nm with the median background subtracted.
+F685 Median	integer	linear_scale	SpecializedQuantitationType			Cy5	Median feature pixel intensity at wavelength 685 nm.
+F685 Median - B685	integer	linear_scale	SpecializedQuantitationType			Cy5	The median feature pixel intensity at wavelength 685 nm with the median background subtracted.
+F685 SD	integer	linear_scale	Error		F685 Median	Cy5	The standard deviation of the feature intensity at wavelength 685 nm.
+F685 Total Intensity	integer	linear_scale	SpecializedQuantitationType			Cy5	The sum of feature pixel intensities at 685 nm.
+Log Ratio (685/595)	float	log_base_2	Ratio				Log (base 2) transform of the ratio of the medians.
+Mean of Ratios (685/595)	float	linear_scale	SpecializedQuantitationType				The geometric mean of the pixel-by-pixel ratios of pixel intensities, with the median background subtracted.
+Median of Ratios (685/595)	float	linear_scale	SpecializedQuantitationType				The median of pixel-by-pixel ratios of pixel intensities, with the median background subtracted.
+Ratio of Means (685/595)	float	linear_scale	SpecializedQuantitationType				The ratio of the arithmetic mean intensities of each feature for each wavelength, with the median background subtracted.
+Ratio of Medians (685/595)	float	linear_scale	SpecializedQuantitationType				The ratio of the median intensities of each feature for each wavelength, with the median background subtracted.
+Ratios SD (685/595)	float	linear_scale	Error		Median of Ratios (685/595)		The geometric standard deviation of the pixel intensity ratios.
+Rgn R2 (685/595)	float	linear_scale	SpecializedQuantitationType				The coefficient of determination for the current regression value.
+Rgn Ratio (685/595)	float	linear_scale	SpecializedQuantitationType				The regression ratio of every pixel in a 2-feature-diameter circle around the center of the feature.
+SNR 595	float	linear_scale	SpecializedQuantitationType	1		Cy5	The signal-to-noise ratio at 595 nm, defined by (F595 Mean - B595 Mean) / (B595 SD)
+SNR 685	float	linear_scale	SpecializedQuantitationType	1		Cy5	The signal-to-noise ratio at 685 nm, defined by (F685 Mean - B685 Mean) / (B685 SD)
+Sum of Means (685/595)	integer	linear_scale	SpecializedQuantitationType				The sum of the arithmetic mean intensities for each wavelength, with the median background subtracted.
+Sum of Medians (685/595)	integer	linear_scale	SpecializedQuantitationType				The sum of the median intensities for each wavelength, with the median background subtracted.
+% > B633+1SD	integer	linear_scale	SpecializedQuantitationType			Cy5	The percentage of feature pixels with intensities more than one standard deviation above the background pixel intensity, at wavelength 633 nm.
+% > B633+2SD	integer	linear_scale	SpecializedQuantitationType			Cy5	The percentage of feature pixels with intensities more than two standard deviations above the background pixel intensity, at wavelength 633 nm.
+B633	integer	linear_scale	SpecializedQuantitationType	1		Cy5	The actual background value used for the feature in GenePix Pro calculations (as opposed to B633 Median, for example, which is the local median background.) This column is required because GenePix Pro 5.0 has global and negative control background subtrac
+B633 CV	integer	linear_scale	SpecializedQuantitationType	1		Cy5	The coefficient of variation of local background pixel intensity.
+B633 Mean	integer	linear_scale	MeasuredSignal	1		Cy5	The mean feature background intensity at wavelength 633 nm.
+B633 Median	integer	linear_scale	SpecializedQuantitationType	1		Cy5	The median feature background intensity at wavelength 633 nm.
+B633 SD	integer	linear_scale	Error	1	B633 Median	Cy5	The standard deviation of the feature background intensity at wavelength 633 nm.
+F633 % Sat.	integer	linear_scale	SpecializedQuantitationType			Cy5	The percentage of feature pixels at wavelength 633 nm that are saturated.
+F633 CV	integer	linear_scale	SpecializedQuantitationType			Cy5	The coefficient of variation of feature pixel intensity.
+F633 Mean	integer	linear_scale	MeasuredSignal			Cy5	Mean feature pixel intensity at wavelength 633 nm.
+F633 Mean - B633	integer	linear_scale	SpecializedQuantitationType			Cy5	The mean feature pixel intensity at wavelength 633 nm with the median background subtracted.
+F633 Median	integer	linear_scale	SpecializedQuantitationType			Cy5	Median feature pixel intensity at wavelength 633 nm.
+F633 Median - B633	integer	linear_scale	SpecializedQuantitationType			Cy5	The median feature pixel intensity at wavelength 633 nm with the median background subtracted.
+F633 SD	integer	linear_scale	Error		F633 Median	Cy5	The standard deviation of the feature intensity at wavelength 633 nm.
+F633 Total Intensity	integer	linear_scale	SpecializedQuantitationType			Cy5	The sum of feature pixel intensities at 633 nm.
+Log Ratio (633/532)	float	log_base_2	Ratio				Log (base 2) transform of the ratio of the medians.
+Mean of Ratios (633/532)	float	linear_scale	SpecializedQuantitationType				The geometric mean of the pixel-by-pixel ratios of pixel intensities, with the median background subtracted.
+Median of Ratios (633/532)	float	linear_scale	SpecializedQuantitationType				The median of pixel-by-pixel ratios of pixel intensities, with the median background subtracted.
+Ratio of Means (633/532)	float	linear_scale	SpecializedQuantitationType				The ratio of the arithmetic mean intensities of each feature for each wavelength, with the median background subtracted.
+Ratio of Medians (633/532)	float	linear_scale	SpecializedQuantitationType				The ratio of the median intensities of each feature for each wavelength, with the median background subtracted.
+Ratios SD (633/532)	float	linear_scale	Error		Median of Ratios (633/532)		The geometric standard deviation of the pixel intensity ratios.
+Rgn R2 (633/532)	float	linear_scale	SpecializedQuantitationType				The coefficient of determination for the current regression value.
+Rgn Ratio (633/532)	float	linear_scale	SpecializedQuantitationType				The regression ratio of every pixel in a 2-feature-diameter circle around the center of the feature.
+SNR 633	float	linear_scale	SpecializedQuantitationType	1		Cy5	The signal-to-noise ratio at 633 nm, defined by (F633 Mean - B633 Mean) / (B633 SD)
+Sum of Means (633/532)	integer	linear_scale	SpecializedQuantitationType				The sum of the arithmetic mean intensities for each wavelength, with the median background subtracted.
+Sum of Medians (633/532)	integer	linear_scale	SpecializedQuantitationType				The sum of the median intensities for each wavelength, with the median background subtracted.
+% > B532+1SD	integer	linear_scale	SpecializedQuantitationType			Cy3	The percentage of feature pixels with intensities more than one standard deviation above the background pixel intensity, at wavelength 532 nm.
+% > B532+1SD2	integer	linear_scale	SpecializedQuantitationType			Cy3	
+% > B532+2SD	integer	linear_scale	SpecializedQuantitationType			Cy3	The percentage of feature pixels with intensities more than two standard deviations above the background pixel intensity, at wavelength 532 nm.
+% > B532+2SD2	integer	linear_scale	SpecializedQuantitationType			Cy3	
+% > B563+1SD	integer	linear_scale	SpecializedQuantitationType			Cy3	The percentage of feature pixels with intensities more than one standard deviation above the background pixel intensity, at wavelength 563 nm.
+% > B563+2SD	integer	linear_scale	SpecializedQuantitationType			Cy3	The percentage of feature pixels with intensities more than two standard deviations above the background pixel intensity, at wavelength 563 nm.
+% > B635+1SD	integer	linear_scale	SpecializedQuantitationType			Cy5	The percentage of feature pixels with intensities more than one standard deviation above the background pixel intensity, at wavelength 635 nm.
+% > B635+1SD2	integer	linear_scale	SpecializedQuantitationType			Cy5	
+% > B635+2SD	integer	linear_scale	SpecializedQuantitationType			Cy5	The percentage of feature pixels with intensities more than two standard deviations above the background pixel intensity, at wavelength 635 nm.
+% > B635+2SD2	integer	linear_scale	SpecializedQuantitationType			Cy5	
+Autoflag	boolean	linear_scale	SpecializedQuantitationType				Reports whether or not a feature has been flagged from the Flag Features dialog box. It applies to good and bad flags only.
+B Pixels	integer	linear_scale	SpecializedQuantitationType				The total number of background pixels.
+B532	integer	linear_scale	SpecializedQuantitationType	1		Cy3	The actual background value used for the feature in GenePix Pro calculations (as opposed to B532 Median, for example, which is the local median background.) This column is required because GenePix Pro 5.0 has global and negative control background subtrac
+B532 CV	integer	linear_scale	SpecializedQuantitationType	1		Cy3	The coefficient of variation of local background pixel intensity.
+B532 Mean	integer	linear_scale	MeasuredSignal	1		Cy3	The mean feature background intensity at wavelength 532 nm.
+B532 Median	integer	linear_scale	SpecializedQuantitationType	1		Cy3	The median feature background intensity at wavelength 532 nm.
+B532 SD	integer	linear_scale	Error	1	B532 Median	Cy3	The standard deviation of the feature background intensity at wavelength 532 nm.
+B532 SD2	float	linear_scale	Error	1	B532 Median	Cy3	
+B563 Mean	integer	linear_scale	MeasuredSignal	1		Cy3	The mean feature background intensity at wavelength 563 nm.
+B563 Median	integer	linear_scale	SpecializedQuantitationType	1		Cy3	The median feature background intensity at wavelength 563 nm.
+B563 SD	integer	linear_scale	Error	1	B563 Median	Cy3	The standard deviation of the feature background intensity at wavelength 563 nm.
+B635	integer	linear_scale	SpecializedQuantitationType	1		Cy5	The actual background value used for the feature in GenePix Pro calculations (as opposed to B635 Median, for example, which is the local median background.) This column is required because GenePix Pro 5.0 has global and negative control background subtrac
+B635 CV	integer	linear_scale	SpecializedQuantitationType	1		Cy5	The coefficient of variation of local background pixel intensity.
+B635 Mean	integer	linear_scale	MeasuredSignal	1		Cy5	The mean feature background intensity at wavelength 635 nm.
+B635 Median	integer	linear_scale	SpecializedQuantitationType	1		Cy5	The median feature background intensity at wavelength 635 nm.
+B635 SD	integer	linear_scale	Error	1	B635 Median	Cy5	The standard deviation of the feature background intensity at wavelength 635 nm.
+B635 SD2	float	linear_scale	Error	1	B635 Median	Cy5	
+Circularity	integer	linear_scale	SpecializedQuantitationType				A measure of circularity from 0 to 100, using a metric based on the variance of the distance of each boundary pixel to the centroid of the feature: 100 is most circular, 0 is most non-circular. Circular features always have a circularity of 100, square fe
+Dia.	integer	linear_scale	SpecializedQuantitationType				The diameter in um of the feature-indicator.
+F Pixels	integer	linear_scale	SpecializedQuantitationType				The total number of feature pixels.
+F532 % Sat.	integer	linear_scale	SpecializedQuantitationType			Cy3	The percentage of feature pixels at wavelength 532 nm that are saturated.
+F532 CV	integer	linear_scale	SpecializedQuantitationType			Cy3	The coefficient of variation of feature pixel intensity.
+F532 Mean	integer	linear_scale	MeasuredSignal			Cy3	Mean feature pixel intensity at wavelength 532 nm.
+F532 Mean - B532	integer	linear_scale	SpecializedQuantitationType			Cy3	The mean feature pixel intensity at wavelength 532 nm with the median background subtracted.
+F532 Median	integer	linear_scale	SpecializedQuantitationType			Cy3	Median feature pixel intensity at wavelength 532 nm.
+F532 Median - B532	integer	linear_scale	SpecializedQuantitationType			Cy3	The median feature pixel intensity at wavelength 532 nm with the median background subtracted.
+F532 SD	integer	linear_scale	Error		F532 Median	Cy3	The standard deviation of the feature intensity at wavelength 532 nm.
+F532 Total Intensity	integer	linear_scale	SpecializedQuantitationType			Cy3	The sum of feature pixel intensities at 532 nm.
+F563 % Sat.	integer	linear_scale	SpecializedQuantitationType			Cy3	The percentage of feature pixels at wavelength 563 nm that are saturated.
+F563 Mean	integer	linear_scale	MeasuredSignal			Cy3	Mean feature pixel intensity at wavelength 563 nm.
+F563 Mean - B563	integer	linear_scale	SpecializedQuantitationType			Cy3	The mean feature pixel intensity at wavelength 563 nm with the median background subtracted.
+F563 Median	integer	linear_scale	SpecializedQuantitationType			Cy3	Median feature pixel intensity at wavelength 563 nm.
+F563 Median - B563	integer	linear_scale	SpecializedQuantitationType			Cy3	The median feature pixel intensity at wavelength 563 nm with the median background subtracted.
+F563 SD	integer	linear_scale	Error		F563 Median	Cy3	The standard deviation of the feature intensity at wavelength 563 nm.
+F635 % Sat.	integer	linear_scale	SpecializedQuantitationType			Cy5	The percentage of feature pixels at wavelength 635 nm that are saturated.
+F635 CV	integer	linear_scale	SpecializedQuantitationType			Cy5	The coefficient of variation of feature pixel intensity.
+F635 Mean	integer	linear_scale	MeasuredSignal			Cy5	Mean feature pixel intensity at wavelength 635 nm.
+F635 Mean - B635	integer	linear_scale	SpecializedQuantitationType			Cy5	The mean feature pixel intensity at wavelength 635 nm with the median background subtracted.
+F635 Median	integer	linear_scale	SpecializedQuantitationType			Cy5	Median feature pixel intensity at wavelength 635 nm.
+F635 Median - B635	integer	linear_scale	SpecializedQuantitationType			Cy5	The median feature pixel intensity at wavelength 635 nm with the median background subtracted.
+F635 SD	integer	linear_scale	Error		F635 Median	Cy5	The standard deviation of the feature intensity at wavelength 635 nm.
+F635 Total Intensity	integer	linear_scale	SpecializedQuantitationType			Cy5	The sum of feature pixel intensities at 635 nm.
+Flags	integer	linear_scale	SpecializedQuantitationType				The type of flag associated with a feature: -100 = user-flagged null spot; -50 = software-flagged null spot; 0 = spot valid.
+Log Ratio	float	log_base_2	Ratio				Log (base 2) transform of the ratio of the medians.
+Log Ratio (532/635)	float	log_base_2	Ratio				log (base 2) transform of the ratio of the medians.
+Log Ratio (635/532)	float	log_base_2	Ratio				Log (base 2) transform of the ratio of the medians.
+Mean of Ratios	float	linear_scale	SpecializedQuantitationType				The geometric mean of the pixel-by-pixel ratios of pixel intensities, with the median background subtracted.
+Mean of Ratios (532/635)	float	linear_scale	SpecializedQuantitationType				The geometric mean of the pixel-by-pixel ratios of pixel intensities, with the median background subtracted.
+Mean of Ratios (635/532)	float	linear_scale	SpecializedQuantitationType				The geometric mean of the pixel-by-pixel ratios of pixel intensities, with the median background subtracted.
+Median of Ratios	float	linear_scale	SpecializedQuantitationType				The median of pixel-by-pixel ratios of pixel intensities, with the median background subtracted.
+Median of Ratios (532/635)	float	linear_scale	SpecializedQuantitationType				The median of pixel-by-pixel ratios of pixel intensities, with the median background subtracted.
+Median of Ratios (635/532)	float	linear_scale	SpecializedQuantitationType				The median of pixel-by-pixel ratios of pixel intensities, with the median background subtracted.
+Normalize	integer	unscaled	PresentAbsent				The normalization status of the feature (included/not included).
+Ratio of Means	float	linear_scale	SpecializedQuantitationType				The ratio of the arithmetic mean intensities of each feature for each wavelength, with the median background subtracted.
+Ratio of Means (532/635)	float	linear_scale	SpecializedQuantitationType				The ratio of the arithmetic mean intensities of each feature for each wavelength, with the median background subtracted.
+Ratio of Means (635/532)	float	linear_scale	SpecializedQuantitationType				The ratio of the arithmetic mean intensities of each feature for each wavelength, with the median background subtracted.
+Ratio of Medians	float	linear_scale	SpecializedQuantitationType				The ratio of the median intensities of each feature for each wavelength, with the median background subtracted.
+Ratio of Medians (532/635)	float	linear_scale	SpecializedQuantitationType				The ratio of the median intensities of each feature for each wavelength, with the median background subtracted.
+Ratio of Medians (635/532)	float	linear_scale	SpecializedQuantitationType				The ratio of the median intensities of each feature for each wavelength, with the median background subtracted.
+Ratios SD	float	linear_scale	Error		Median of Ratios		The geometric standard deviation of the pixel intensity ratios.
+Ratios SD (532/635)	float	linear_scale	Error		Median of Ratios (532/635)		The geometric standard deviation of the pixel intensity ratios.
+Ratios SD (635/532)	float	linear_scale	Error		Median of Ratios (635/532)		The geometric standard deviation of the pixel intensity ratios.
+Rgn R  (532/635)	float	linear_scale	SpecializedQuantitationType				
+Rgn R2	float	linear_scale	SpecializedQuantitationType				The coefficient of determination for the current regression value.
+Rgn R2 (532/635)	float	linear_scale	SpecializedQuantitationType				The coefficient of determination for the current regression value.
+Rgn R2 (635/532)	float	linear_scale	SpecializedQuantitationType				The coefficient of determination for the current regression value.
+Rgn Ratio	float	linear_scale	SpecializedQuantitationType				The regression ratio of every pixel in a 2-feature-diameter circle around the center of the feature.
+Rgn Ratio (532/635)	float	linear_scale	SpecializedQuantitationType				The regression ratio of every pixel in a 2-feature-diameter circle around the center of the feature.
+Rgn Ratio (635/532)	float	linear_scale	SpecializedQuantitationType				The regression ratio of every pixel in a 2-feature-diameter circle around the center of the feature.
+Rgn R�	float	linear_scale	SpecializedQuantitationType				
+Rgn R� (635/532)	float	linear_scale	SpecializedQuantitationType				
+Rgn R� (532/635)	float	linear_scale	SpecializedQuantitationType				
+SNR 532	float	linear_scale	SpecializedQuantitationType	1		Cy3	The signal-to-noise ratio at 532 nm, defined by (F532 Mean - B532 Mean) / (B532 SD)
+SNR 635	float	linear_scale	SpecializedQuantitationType	1		Cy5	The signal-to-noise ratio at 635 nm, defined by (F635 Mean - B635 Mean) / (B635 SD)
+Sum of Means	integer	linear_scale	SpecializedQuantitationType				The sum of the arithmetic mean intensities for each wavelength, with the median background subtracted.
+Sum of Means (532/635)	integer	linear_scale	SpecializedQuantitationType				The sum of the arithmetic mean intensities for each wavelength, with the median background subtracted.
+Sum of Means (635/532)	integer	linear_scale	SpecializedQuantitationType				The sum of the arithmetic mean intensities for each wavelength, with the median background subtracted.
+Sum of Medians	integer	linear_scale	SpecializedQuantitationType				The sum of the median intensities for each wavelength, with the median background subtracted.
+Sum of Medians (532/635)	integer	linear_scale	SpecializedQuantitationType				The sum of the median intensities for each wavelength, with the median background subtracted.
+Sum of Medians (635/532)	integer	linear_scale	SpecializedQuantitationType				The sum of the median intensities for each wavelength, with the median background subtracted.
+X	integer	linear_scale	SpecializedQuantitationType				The X-coordinate in um of the center of the feature-indicator associated with the feature, where (0,0) is the top left of the image.
+Y	integer	linear_scale	SpecializedQuantitationType				The Y-coordinate in um of the center of the feature-indicator associated with the feature, where (0,0) is the top left of the image.
+>>>GeneTAC							
+Background med Cy3	float	linear_scale	MeasuredSignal	1		Cy3	
+Background med Cy5	float	linear_scale	MeasuredSignal	1		Cy5	
+Background med ratio	float	linear_scale	Ratio	1			
+Background st dev Cy3	float	linear_scale	Error	1	Background med Cy3	Cy3	
+Background st dev Cy5	float	linear_scale	Error	1	Background med Cy5	Cy5	
+Signal med Cy3	float	linear_scale	MeasuredSignal			Cy3	
+Signal med Cy5	float	linear_scale	MeasuredSignal			Cy5	
+Signal med ratio	float	linear_scale	Ratio				
+Signal st dev Cy3	float	linear_scale	Error		Signal med Cy3	Cy3	
+Signal st dev Cy5	float	linear_scale	Error		Signal med Cy5	Cy5	
+>>>ImaGene[BioDiscovery]							
+Area To Perimeter_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	This quality measure defines the spot's circularity. Area of a spot is divided by a square of spot perimeter and multiplied by 4π. As a result, this measure ranges from 0 (highly non-circular shape) to 1 (a perfect circle).
+Area To Perimeter_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	This quality measure defines the spot's circularity. Area of a spot is divided by a square of spot perimeter and multiplied by 4π. As a result, this measure ranges from 0 (highly non-circular shape) to 1 (a perfect circle).
+Background Area_Cy3	integer	linear_scale	SpecializedQuantitationType	1		Cy3	Number of pixels in the local background region.
+Background Area_Cy5	integer	linear_scale	SpecializedQuantitationType	1		Cy5	Number of pixels in the local background region.
+Background Contamination Present_Cy3	boolean	linear_scale	SpecializedQuantitationType			Cy3	
+Background Contamination Present_Cy5	boolean	linear_scale	SpecializedQuantitationType			Cy5	
+Background Mean	float	linear_scale	MeasuredSignal	1			
+Background Mean_Cy3	float	linear_scale	MeasuredSignal	1		Cy3	Pixel intensity averaged over the local background region.
+Background Mean_Cy5	float	linear_scale	MeasuredSignal	1		Cy5	Pixel intensity averaged over the local background region.
+Background Median_Cy3	float	linear_scale	SpecializedQuantitationType	1		Cy3	Median pixel intensity computed over the local background region.
+Background Median_Cy5	float	linear_scale	SpecializedQuantitationType	1		Cy5	Median pixel intensity computed over the local background region.
+Background Mode_Cy3	float	linear_scale	SpecializedQuantitationType	1		Cy3	Mode pixel intensity computed over the local background region.
+Background Mode_Cy5	float	linear_scale	SpecializedQuantitationType	1		Cy5	Mode pixel intensity computed over the local background region.
+Background Stdev	float	linear_scale	Error	1	Background Mean		
+Background Stdev_Cy3	float	linear_scale	Error	1	Background Mean_Cy3	Cy3	Standard deviation of pixel intensities over the local background region.
+Background Stdev_Cy5	float	linear_scale	Error	1	Background Mean_Cy5	Cy5	Standard deviation of pixel intensities over the local background region.
+Background Total_Cy3	integer	linear_scale	SpecializedQuantitationType	1		Cy3	Total pixel intensity summed over the local background region.
+Background Total_Cy5	integer	linear_scale	SpecializedQuantitationType	1		Cy5	Total pixel intensity summed over the local background region.
+Background contamination present_Cy3	boolean	linear_scale	SpecializedQuantitationType			Cy3	0 if the spot passed background contamination test, 1 if it did not.
+Background contamination present_Cy5	boolean	linear_scale	SpecializedQuantitationType			Cy5	0 if the spot passed background contamination test, 1 if it did not.
+CM Offset-X_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	X offset (in pixels) of the spot's center of the mass from the expected position in the grid.
+CM Offset-X_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	X offset (in pixels) of the spot's center of the mass from the expected position in the grid.
+CM Offset-Y_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Y offset (in pixels) of the spot's center of the mass from the expected position in the grid.
+CM Offset-Y_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Y offset (in pixels) of the spot's center of the mass from the expected position in the grid.
+CM Offset_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Offset (in pixels) of the spot's center of the mass from the expected position in the grid.
+CM Offset_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Offset (in pixels) of the spot's center of the mass from the expected position in the grid.
+CM-X_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	X coordinate of the center of the mass of spot's signal region.
+CM-X_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	X coordinate of the center of the mass of spot's signal region.
+CM-Y_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Y coordinate of the center of the mass of spot's signal region.
+CM-Y_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Y coordinate of the center of the mass of spot's signal region.
+Control_Cy3	string_datatype	unscaled	SpecializedQuantitationType			Cy3	Name of a control type for current spot (no name means the spot is not a control spot).
+Control_Cy5	string_datatype	unscaled	SpecializedQuantitationType			Cy5	Name of a control type for current spot (no name means the spot is not a control spot).
+Diameter	integer	linear_scale	SpecializedQuantitationType				
+Diameter_Cy3	integer	linear_scale	SpecializedQuantitationType			Cy3	Diameter (in pixels) of grid circle corresponding to the spot.
+Diameter_Cy5	integer	linear_scale	SpecializedQuantitationType			Cy5	Diameter (in pixels) of grid circle corresponding to the spot.
+Empty spot_Cy3	boolean	linear_scale	SpecializedQuantitationType			Cy3	0 if the spot passed offset test, 1 if it did not.
+Empty spot_Cy5	boolean	linear_scale	SpecializedQuantitationType			Cy5	0 if the spot passed offset test, 1 if it did not.
+Expected X_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	X coordinate of expected position of the circle in the grid. Expected position in the grid is computed fitting least square lines to circle centers in every row and column.
+Expected X_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	X coordinate of expected position of the circle in the grid. Expected position in the grid is computed fitting least square lines to circle centers in every row and column.
+Expected Y_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Y coordinate of expected position of the circle in the grid. Expected position in the grid is computed fitting least square lines to circle centers in every row and column.
+Expected Y_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Y coordinate of expected position of the circle in the grid. Expected position in the grid is computed fitting least square lines to circle centers in every row and column.
+Failed Control_Cy3	boolean	linear_scale	SpecializedQuantitationType			Cy3	0 if the control passed all tests, 1 if at least one of the tests failed.
+Failed Control_Cy5	boolean	linear_scale	SpecializedQuantitationType			Cy5	0 if the control passed all tests, 1 if at least one of the tests failed.
+Field	string_datatype	unscaled	SpecializedQuantitationType				Name of a field where the spot is located.
+Flag	boolean	unscaled	PresentAbsent				
+Flag_Cy3	integer	linear_scale	SpecializedQuantitationType			Cy3	Numeric code for the spot (0 - no flag, flag codes 1,...,7).
+Flag_Cy5	integer	linear_scale	SpecializedQuantitationType			Cy5	Numeric code for the spot (0 - no flag, flag codes 1,...,7).
+Gene ID	string_datatype	unscaled	SpecializedQuantitationType				Gene ID information for the spot.
+Ignored % failed_Cy3	boolean	linear_scale	SpecializedQuantitationType			Cy3	0 if the spot passed ignored percentage test, 1 if it did not.
+Ignored % failed_Cy5	boolean	linear_scale	SpecializedQuantitationType			Cy5	0 if the spot passed ignored percentage test, 1 if it did not.
+Ignored Area_Cy3	integer	linear_scale	SpecializedQuantitationType			Cy3	Area of ignored regions directly neighboring ("touching") the signal area is computed.
+Ignored Area_Cy5	integer	linear_scale	SpecializedQuantitationType			Cy5	Area of ignored regions directly neighboring ("touching") the signal area is computed.
+Ignored Median_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Median pixel intensity computed over the local ignored region.
+Ignored Median_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Median pixel intensity computed over the local ignored region.
+Max Diam_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Diameter of the circle, the spot's signal region can be inscribed in.
+Max Diam_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Diameter of the circle, the spot's signal region can be inscribed in.
+Min Diam_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Diameter of the circle inscribed into the spot's signal region.
+Min Diam_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Diameter of the circle inscribed into the spot's signal region.
+Negative spot_Cy3	boolean	linear_scale	SpecializedQuantitationType			Cy3	1 if the spot was qualified as negative, 0 if it was not.
+Negative spot_Cy5	boolean	linear_scale	SpecializedQuantitationType			Cy5	1 if the spot was qualified as negative, 0 if it was not.
+Offset X_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	X Offset (in pixels) of the center of the grid circle from the expected position in the grid.
+Offset X_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	X Offset (in pixels) of the center of the grid circle from the expected position in the grid.
+Offset Y_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Y Offset (in pixels) of the center of the grid circle from the expected position in the grid.
+Offset Y_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Y Offset (in pixels) of the center of the grid circle from the expected position in the grid.
+Offset failed_Cy3	boolean	linear_scale	SpecializedQuantitationType			Cy3	0 if the spot passed offset test, 1 if it did not.
+Offset failed_Cy5	boolean	linear_scale	SpecializedQuantitationType			Cy5	0 if the spot passed offset test, 1 if it did not.
+Open Perimeter Failed_Cy3	boolean	linear_scale	SpecializedQuantitationType			Cy3	
+Open Perimeter Failed_Cy5	boolean	linear_scale	SpecializedQuantitationType			Cy5	
+Open Perimeter_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Computes the proportion of signal perimeter that touches the border of rectangular snip around the spot.
+Open Perimeter_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Computes the proportion of signal perimeter that touches the border of rectangular snip around the spot.
+Open perimeter failed_Cy3	boolean	linear_scale	SpecializedQuantitationType			Cy3	0 if the spot open perimeter test, 1 if it did not.
+Open perimeter failed_Cy5	boolean	linear_scale	SpecializedQuantitationType			Cy5	0 if the spot open perimeter test, 1 if it did not.
+Perim-To-Area_Cy3	boolean	linear_scale	SpecializedQuantitationType			Cy3	
+Perim-To-Area_Cy5	boolean	linear_scale	SpecializedQuantitationType			Cy5	
+Perim-to-area failed_Cy3	boolean	linear_scale	SpecializedQuantitationType			Cy3	0 if the spot passed perimeter-to-area test, 1 if it did not.
+Perim-to-area failed_Cy5	boolean	linear_scale	SpecializedQuantitationType			Cy5	0 if the spot passed perimeter-to-area test, 1 if it did not.
+Position Offset_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	
+Position Offset_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	
+Position offset_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Offset (in pixels) of the center of the grid circle from the expected position in the grid.
+Position offset_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Offset (in pixels) of the center of the grid circle from the expected position in the grid.
+Saturated spot_Cy3	boolean	linear_scale	SpecializedQuantitationType			Cy3	1 if the spot was qualified as saturated, 0 if it was not.
+Saturated spot_Cy5	boolean	linear_scale	SpecializedQuantitationType			Cy5	1 if the spot was qualified as saturated, 0 if it was not.
+Selected spot_Cy3	boolean	linear_scale	SpecializedQuantitationType			Cy3	1 if the spot was selected in the software view at time of save, 0 if not.
+Selected spot_Cy5	boolean	linear_scale	SpecializedQuantitationType			Cy5	1 if the spot was selected in the software view at time of save, 0 if not.
+Shape Regularity	float	linear_scale	SpecializedQuantitationType				
+Shape Regularity Failed_Cy3	boolean	linear_scale	SpecializedQuantitationType			Cy3	
+Shape Regularity Failed_Cy5	boolean	linear_scale	SpecializedQuantitationType			Cy5	
+Shape Regularity_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	The signal area of a spot is inscribed into a circle, then the number of non-signal pixels that fall within this circle is computed and divided by area of circle. This ratio is subtracted from 1 to give the Shape Regularity.
+Shape Regularity_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	The signal area of a spot is inscribed into a circle, then the number of non-signal pixels that fall within this circle is computed and divided by area of circle. This ratio is subtracted from 1 to give the Shape Regularity.
+Shape regularity failed_Cy3	boolean	linear_scale	SpecializedQuantitationType			Cy3	0 if the spot passed shape regularity test, 1 if it did not.
+Shape regularity failed_Cy5	boolean	linear_scale	SpecializedQuantitationType			Cy5	0 if the spot passed shape regularity test, 1 if it did not.
+Signal Area_Cy3	integer	linear_scale	SpecializedQuantitationType			Cy3	Number of pixels in the local signal region.
+Signal Area_Cy5	integer	linear_scale	SpecializedQuantitationType			Cy5	Number of pixels in the local signal region.
+Signal Contamination Present_Cy3	boolean	linear_scale	SpecializedQuantitationType			Cy3	
+Signal Contamination Present_Cy5	boolean	linear_scale	SpecializedQuantitationType			Cy5	
+Signal Mean	float	linear_scale	MeasuredSignal				
+Signal Mean_Cy3	float	linear_scale	MeasuredSignal			Cy3	Pixel intensity averaged over the local signal region.
+Signal Mean_Cy5	float	linear_scale	MeasuredSignal			Cy5	Pixel intensity averaged over the local signal region.
+Signal Median_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Median pixel intensity computed over the local signal region.
+Signal Median_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Median pixel intensity computed over the local signal region.
+Signal Mode_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Mode pixel intensity computed over the local signal region (mode corresponds to the pick location in intensity distribution).
+Signal Mode_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Mode pixel intensity computed over the local signal region (mode corresponds to the pick location in intensity distribution).
+Signal Stdev	float	linear_scale	Error		Signal Mean		
+Signal Stdev_Cy3	float	linear_scale	Error		Signal Mean_Cy3	Cy3	Standard deviation of pixel intensities over the local signal region.
+Signal Stdev_Cy5	float	linear_scale	Error		Signal Mean_Cy5	Cy5	Standard deviation of pixel intensities over the local signal region.
+Signal Total_Cy3	integer	linear_scale	SpecializedQuantitationType			Cy3	Total pixel intensity summed over the local signal region.
+Signal Total_Cy5	integer	linear_scale	SpecializedQuantitationType			Cy5	Total pixel intensity summed over the local signal region.
+Signal contamination present_Cy3	boolean	linear_scale	SpecializedQuantitationType			Cy3	0 if the spot passed signal contamination test, 1 if it did not.
+Signal contamination present_Cy5	boolean	linear_scale	SpecializedQuantitationType			Cy5	0 if the spot passed signal contamination test, 1 if it did not.
+Spot Area_Cy3	integer	linear_scale	SpecializedQuantitationType			Cy3	Signal Area plus Ignored Area.
+Spot Area_Cy5	integer	linear_scale	SpecializedQuantitationType			Cy5	Signal Area plus Ignored Area.
+XCoord	float	linear_scale	SpecializedQuantitationType				
+XCoord_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	X coordinate (in pixels) of grid circle corresponding to the spot.
+XCoord_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	X coordinate (in pixels) of grid circle corresponding to the spot.
+YCoord	float	linear_scale	SpecializedQuantitationType				
+YCoord_Cy3	float	linear_scale	SpecializedQuantitationType			Cy3	Y coordinate (in pixels) of grid circle corresponding to the spot.
+YCoord_Cy5	float	linear_scale	SpecializedQuantitationType			Cy5	Y coordinate (in pixels) of grid circle corresponding to the spot.
+>>>NimbleScan[NimbleGen Systems]							
+GENE_EXPR_OPTION	string_datatype	unscaled	SpecializedQuantitationType				The CONTAINER name from the design file, if analysis was done by container or WHOLE_ARRAY if all replicate probe sets were combined into a single set. The default analysis is normally by container. CONTAINER names are generally named FORWARD/REVERSE/BLOCK
+IMAGE_ID_CY3	string_datatype	unscaled	SpecializedQuantitationType			Cy3	The name of the image the data was extracted from, minus the .tif extension. For NimbleGen data sets, this will be the array identifier plus any additional information, like wavelength used to scan the array, or photomultiplier tube setting. The array ID
+IMAGE_ID_CY5	string_datatype	unscaled	SpecializedQuantitationType			Cy5	The name of the image the data was extracted from, minus the .tif extension. For NimbleGen data sets, this will be the array identifier plus any additional information, like wavelength used to scan the array, or photomultiplier tube setting. The array ID
+IMAGE_ID	string_datatype	unscaled	SpecializedQuantitationType				The name of the image the data was extracted from, minus the .tif extension. For NimbleGen data sets, this will be the array identifier plus any additional information, like wavelength used to scan the array, or photomultiplier tube setting. The array ID
+MATCH_INDEX	integer	linear_scale	SpecializedQuantitationType				Integer number that ties probe pairs together. Using the combination of MATCH_INDEX and MISMATCH you can retrieve and distinguish the members of the probe pair. Required for expression arrays with mismatches. Must be unique for each probe pair of a given
+MM_CY3	float	linear_scale	SpecializedQuantitationType			Cy3	The mismatch signal intensity for the probe pair. Will range from 0 to 65536. Will be zero for perfect match only designs.
+MM_CY5	float	linear_scale	SpecializedQuantitationType			Cy5	The mismatch signal intensity for the probe pair. Will range from 0 to 65536. Will be zero for perfect match only designs.
+PM_CY3	float	linear_scale	MeasuredSignal			Cy3	The perfect match signal intensity for the probe pair. Will range from 0 to 65536.
+PM_CY5	float	linear_scale	MeasuredSignal			Cy5	The perfect match signal intensity for the probe pair. Will range from 0 to 65536.
+MM	float	linear_scale	SpecializedQuantitationType				The mismatch signal intensity for the probe pair. Will range from 0 to 65536. Will be zero for perfect match only designs.
+PM	float	linear_scale	MeasuredSignal				The perfect match signal intensity for the probe pair. Will range from 0 to 65536.
+POSITION	integer	linear_scale	SpecializedQuantitationType				Position of the PROBE_SEQUENCE in the sequence/region of interest, starting from the left/5-prime end.
+PROBE_ID	string_datatype	unscaled	SpecializedQuantitationType				The NimbleGen probe identifier. Used to identify a probe sequence within a design. Limited to 50 characters. For expression designs, a PROBE_ID is a 17 character string that looks like HSAP00P0001724033. The first four letters (HSAP) are the species code.
+SEQ_ID	string_datatype	unscaled	SpecializedQuantitationType				The NimbleGen sequence identifier. Used to group the probe pairs together for determining gene expression summary values. Required - must be unique for each sequence/region of interest. Limited to 50 characters.
+SEQ_URL	string_datatype	unscaled	SpecializedQuantitationType				When populated, URL to sequence information for the SEQ_ID.
+>>>QuantArray[PerkinElmer]							
+Cy3 Area	integer	linear_scale	SpecializedQuantitationType			Cy3	The area of the spot given as the number of pixels (in sq. microns). Used to determine whether the spot is close to the expected size or is an artifact on the slide.
+Cy3 Area Filter	boolean	linear_scale	SpecializedQuantitationType			Cy3	
+Cy3 Background	float	linear_scale	MeasuredSignal	1		Cy3	Mean pixel Cy3 background intensity in Channel Cy3.
+Cy3 Background Std Dev	float	linear_scale	Error	1	Cy3 Background	Cy3	Standard deviation of the raw Cy3 background pixel intensity.
+Cy3 Background Subtr				1		Cy3	
+Cy3 Bkg. Uniformity	float	linear_scale	SpecializedQuantitationType	1		Cy3	The uniformity of the pixels used in the background intensity calculation.
+Cy3 Bkg. Uniformity Filter	boolean	linear_scale	SpecializedQuantitationType			Cy3	
+Cy3 Circularity	float	linear_scale	SpecializedQuantitationType			Cy3	Reports the degree to which a spot is close to a perfect circle. This measurement is used to determine the print quality of the microarray.
+Cy3 Circularity Filter	boolean	linear_scale	SpecializedQuantitationType			Cy3	
+Cy3 Confidence	integer	linear_scale	SpecializedQuantitationType			Cy3	Unused QuantArray feature, will always = 1.
+Cy3 Diameter	float	linear_scale	SpecializedQuantitationType			Cy3	Average diameter of the spot (in microns). Used to determine whether the spot is close to the expected size or is perhaps an artifact on the slide.
+Cy3 Diameter Filter	boolean	linear_scale	SpecializedQuantitationType			Cy3	
+Cy3 Footprint	float	linear_scale	SpecializedQuantitationType			Cy3	The distance between the expected position of a spot and its actual measured position (in microns).
+Cy3 Footprint Filter	boolean	linear_scale	SpecializedQuantitationType			Cy3	
+Cy3 Intensity	float	linear_scale	MeasuredSignal			Cy3	Mean pixel Cy3 foreground intensity in Channel Cy3.
+Cy3 Intensity Std Dev	float	linear_scale	Error		Cy3 Intensity	Cy3	Standard deviation of the raw Cy3 foreground pixel intensity.
+Cy3 Normalized & BackSubtr				1		Cy3	
+Cy3 Normalized & BackSubtr Ratio				1		Cy3	
+Cy3 Percent	float	linear_scale	SpecializedQuantitationType			Cy3	The portion of the overall spot fluorescence contributed by channel Cy3 (in percent).
+Cy3 Ratio	float	linear_scale	SpecializedQuantitationType			Cy3	The portion of the overall spot fluorescence contributed by channel Cy3 (normalized so that channel Cy3 contribution equals 1).
+Cy3 Replicate Filter	boolean	linear_scale	SpecializedQuantitationType			Cy3	
+Cy3 Signal Noise Ratio	float	linear_scale	Ratio	1		Cy3	The ratio of spot intensity to the standard deviation of the local background of all spots in the microarray. Spots with a low Signal/Noise Ratio can be quickly identified for later reviews.
+Cy3 Signal Noise Ratio Filter	float	linear_scale	SpecializedQuantitationType			Cy3	
+Cy3 Spot Uniformity	float	linear_scale	SpecializedQuantitationType			Cy3	The uniformity of the pixels used in the spot foreground intensity calculation.This helps in determining the quality of the spot and aids in selecting the correct quantification output method.
+Cy3 Spot Uniformity Filter	boolean	linear_scale	SpecializedQuantitationType			Cy3	
+Cy5 Area	integer	linear_scale	SpecializedQuantitationType			Cy5	The area of the spot given as the number of pixels (in sq. microns). Used to determine whether the spot is close to the expected size or is an artifact on the slide.
+Cy5 Area Filter	boolean	linear_scale	SpecializedQuantitationType			Cy5	
+Cy5 Background	float	linear_scale	MeasuredSignal	1		Cy5	Mean pixel Cy5 background intensity in Channel Cy5.
+Cy5 Background Std Dev	float	linear_scale	Error	1	Cy5 Background	Cy5	Standard deviation of the raw Cy5 background pixel intensity.
+Cy5 Background Subtr				1		Cy5	
+Cy5 Bkg. Uniformity	float	linear_scale	SpecializedQuantitationType	1		Cy5	The uniformity of the pixels used in the background intensity calculation.
+Cy5 Bkg. Uniformity Filter	boolean	linear_scale	SpecializedQuantitationType			Cy5	
+Cy5 Circularity	float	linear_scale	SpecializedQuantitationType			Cy5	Reports the degree to which a spot is close to a perfect circle. This measurement is used to determine the print quality of the microarray.
+Cy5 Circularity Filter	boolean	linear_scale	SpecializedQuantitationType			Cy5	
+Cy5 Confidence	integer	linear_scale	SpecializedQuantitationType			Cy5	Unused QuantArray feature, will always = 1.
+Cy5 Diameter	float	linear_scale	SpecializedQuantitationType			Cy5	Average diameter of the spot (in microns). Used to determine whether the spot is close to the expected size or is perhaps an artifact on the slide.
+Cy5 Diameter Filter	boolean	linear_scale	SpecializedQuantitationType			Cy5	
+Cy5 Footprint	float	linear_scale	SpecializedQuantitationType			Cy5	The distance between the expected position of a spot and its actual measured position (in microns).
+Cy5 Footprint Filter	boolean	linear_scale	SpecializedQuantitationType			Cy5	
+Cy5 Intensity	float	linear_scale	MeasuredSignal			Cy5	Mean pixel Cy5 foreground intensity in Channel Cy5.
+Cy5 Intensity Std Dev	float	linear_scale	Error		Cy5 Intensity	Cy5	Standard deviation of the raw Cy5 foreground pixel intensity.
+Cy5 Normalized & BackSubtr				1		Cy5	
+Cy5 Normalized & BackSubtr Ratio				1		Cy5	
+Cy5 Percent	float	linear_scale	SpecializedQuantitationType			Cy5	The portion of the overall spot fluorescence contributed by channel Cy5 (in percent).
+Cy5 Ratio	float	linear_scale	SpecializedQuantitationType			Cy5	The portion of the overall spot fluorescence contributed by channel Cy5 (normalized so that channel Cy5 contribution equals 1).
+Cy5 Replicate Filter	boolean	linear_scale	SpecializedQuantitationType			Cy5	
+Cy5 Signal Noise Ratio	float	linear_scale	Ratio	1		Cy5	The ratio of spot intensity to the standard deviation of the local background of all spots in the microarray. Spots with a low Signal/Noise Ratio can be quickly identified for later reviews.
+Cy5 Signal Noise Ratio Filter	float	linear_scale	SpecializedQuantitationType			Cy5	
+Cy5 Spot Uniformity	float	linear_scale	SpecializedQuantitationType			Cy5	The uniformity of the pixels used in the spot foreground intensity calculation.This helps in determining the quality of the spot and aids in selecting the correct quantification output method.
+Cy5 Spot Uniformity Filter	boolean	linear_scale	SpecializedQuantitationType			Cy5	
+Ignore Filter	integer	linear_scale	SpecializedQuantitationType				Whether the feature quantitation is used in down-stream analysis (flagging feature as valid): 0 = feature invalid (null data); 1 = feature valid.
+Number	integer	linear_scale	SpecializedQuantitationType				Unique integer for each spot.
+X Location	integer	linear_scale	SpecializedQuantitationType				X pixel coordinate of spot center.
+Y Location	integer	linear_scale	SpecializedQuantitationType				Y pixel coordinate of spot center.
+log_normalised	float	log_base_2	Ratio				Ratio = log2 Cy5 - log2 Cy3, for dyeswap ratio *-1
+normalised log(cy3/cy5) ratio	float	log	Ratio	0			
+normalised log(cy5/cy3) ratio	float	log	Ratio	0			
+Cy3 Background Mean	float	linear_scale	MeasuredSignal	1		Cy3	Mean pixel Cy3 background intensity in Channel Cy3.
+Cy3 Background Median	integer	linear_scale	SpecializedQuantitationType	1		Cy3	Median pixel Cy3 background intensity in Channel Cy3.
+Cy3 Background Mode	float	linear_scale	SpecializedQuantitationType	1		Cy3	Mode of pixel Cy3 background intensity in Channel Cy3.
+Cy3 Background Total	integer	linear_scale	SpecializedQuantitationType	1		Cy3	Total pixel Cy3 background intensity in Channel Cy3.
+Cy3 Flag	integer	unscaled	SpecializedQuantitationType			Cy3	
+Cy3 Signal Mean	float	linear_scale	MeasuredSignal			Cy3	Mean pixel Cy3 foreground intensity in Channel Cy3.
+Cy3 Signal Median	integer	linear_scale	SpecializedQuantitationType			Cy3	Median pixel Cy3 foreground intensity in Channel Cy3.
+Cy3 Signal Mode	float	linear_scale	SpecializedQuantitationType			Cy3	Mode of pixel Cy3 foreground intensity in Channel Cy3.
+Cy3 Signal Total	integer	linear_scale	SpecializedQuantitationType			Cy3	Total pixel Cy3 foreground intensity in Channel Cy3.
+Cy5 Background Mean	float	linear_scale	MeasuredSignal	1		Cy5	Mean pixel Cy5 background intensity in Channel Cy5.
+Cy5 Background Median	integer	linear_scale	SpecializedQuantitationType	1		Cy5	Median pixel Cy5 background intensity in Channel Cy5.
+Cy5 Background Mode	float	linear_scale	SpecializedQuantitationType	1		Cy5	Mode of pixel Cy5 background intensity in Channel Cy5.
+Cy5 Background Total	integer	linear_scale	SpecializedQuantitationType	1		Cy5	Total pixel Cy5 background intensity in Channel Cy5.
+Cy5 Flag	integer	unscaled	SpecializedQuantitationType			Cy5	
+Cy5 Signal Mean	float	linear_scale	MeasuredSignal			Cy5	Mean pixel Cy5 foreground intensity in Channel Cy5.
+Cy5 Signal Median	integer	linear_scale	SpecializedQuantitationType			Cy5	Median pixel Cy5 foreground intensity in Channel Cy5.
+Cy5 Signal Mode	float	linear_scale	SpecializedQuantitationType			Cy5	Mode of pixel Cy5 foreground intensity in Channel Cy5.
+Cy5 Signal Total	integer	linear_scale	SpecializedQuantitationType			Cy5	Total pixel Cy5 foreground intensity in Channel Cy5.
+>>>ScanAlyze[Stanford University]							
+BGPIX	integer	linear_scale	SpecializedQuantitationType	1			Number of background pixels.
+BOT	integer	linear_scale	SpecializedQuantitationType				Box bottom: int(((centerX + radius) - Xoffset) / pixelSize).
+CH1AB	float	linear_scale	SpecializedQuantitationType	1		Cy3	Mean intensities of background pixels of Cy3.
+CH1B	float	linear_scale	MeasuredSignal	1		Cy3	Median intensities of background pixels of Cy3.
+CH1EDGEA	float	linear_scale	SpecializedQuantitationType			Cy3	Mean magnitude of the horizontal & vertical Sobel edge vectors contained within each spot, for Cy3.
+CH1GTB1	float	linear_scale	SpecializedQuantitationType			Cy3	Fraction of pixels in the spot greater than background (CH1B) of Cy3.
+CH1GTB2	float	linear_scale	SpecializedQuantitationType			Cy3	Fraction of pixels in the spot greater than 1.5x the background (CH1B) for Cy3.
+CH1I	float	linear_scale	MeasuredSignal			Cy3	Uncorrected Cy3 mean pixel intensity.
+CH1KSD	float	linear_scale	SpecializedQuantitationType			Cy3	Value of Komogorov-Smirnov statistic that assesses the likelihood that the spot pixel intensity is drawn from the background distribution, for Cy3.
+CH1KSP	float	linear_scale	SpecializedQuantitationType			Cy3	Probability value of Komogorov-Smirnov statistic that assesses the likelihood that the spot pixel intensity is drawn from the background distribution, for Cy3.
+CH2AB	float	linear_scale	SpecializedQuantitationType	1		Cy5	Mean intensities of background pixels of Cy5.
+CH2B	float	linear_scale	MeasuredSignal	1		Cy5	Median intensities of background pixels of Cy5.
+CH2EDGEA	float	linear_scale	SpecializedQuantitationType			Cy5	Mean magnitude of the horizontal & vertical Sobel edge vectors contained within each spot, for Cy5.
+CH2GTB1	float	linear_scale	SpecializedQuantitationType			Cy5	Fraction of pixels in the spot greater than background (CH2B) of Cy5.
+CH2GTB2	float	linear_scale	SpecializedQuantitationType			Cy5	Fraction of pixels in the spot greater than 1.5x the background (CH2B) for Cy5.
+CH2I	float	linear_scale	MeasuredSignal			Cy5	Uncorrected Cy5 mean pixel intensity.
+CH2KSD	float	linear_scale	SpecializedQuantitationType			Cy5	Value of Komogorov-Smirnov statistic that assesses the likelihood that the spot pixel intensity is drawn from the background distribution, for Cy5.
+CH2KSP	float	linear_scale	SpecializedQuantitationType			Cy5	Probability value of Komogorov-Smirnov statistic that assesses the likelihood that the spot pixel intensity is drawn from the background distribution, for Cy5.
+COL	integer	linear_scale	SpecializedQuantitationType				The column within the grid that the spot is contained.
+CORR	float	linear_scale	SpecializedQuantitationType				The correlation between channel1 (Cy3) & Channel 2 (Cy5) pixels within the spot, and is a useful quality control parameter. Generally, high values imply better fit & good spot quality.
+EDGE	integer	unscaled	SpecializedQuantitationType				
+FLAG	integer	unscaled	Failed				User defined spot flag (default 0).
+GRID	integer	linear_scale	SpecializedQuantitationType				The grid in which the spot is contained.
+LEFT	integer	linear_scale	SpecializedQuantitationType				Box left: int(((centerY - radius) - yoffset) / pixelSize).
+LFRAT	float	linear_scale	SpecializedQuantitationType				When single pixel intensities on channel 1 (Cy3) are plotted against those in channel 2 (Cy5) they fall on a straight line with a slope equal to the ratio. The least-squares fit of a line to the points, minimizing the sum of the squares shortest distance
+MRAT	float	linear_scale	SpecializedQuantitationType				Contains median of Ch2PI-CH2B/Ch1PI-CH1B where Ch1PI & Ch2PI represent single pixel intensities.
+RAT2	float	linear_scale	Ratio				Ratio of the arithmetic mean intensities of each spot for each wavelength, with the median background subtracted. Channel 2/Channel 1 ratio, (CH2I - CH2B)/(CH1 - CH1B) or Red/Green ratio.
+REGR	float	linear_scale	SpecializedQuantitationType				When single pixel intensities on channel 1 (Cy3) are plotted against those in channel 2 (Cy5) they fall on a straight line with a slope equal to the ratio. This represents the linear regression of Cy5 on Cy3.
+RIGHT	integer	linear_scale	SpecializedQuantitationType				Box right: int(((centerY + radius) - yoffset) / pixelSize).
+SPIX	integer	linear_scale	SpecializedQuantitationType				Count of the number of pixels in the spot.
+SPOT	integer	linear_scale	SpecializedQuantitationType				Unique index of spot in file - counting starts with grid1, moves along row1 from column1 until the last column, then advances to the next row; after all rows in grid1 are assigned an index, counting proceeds to grid2, etc...
+TOP	integer	linear_scale	SpecializedQuantitationType				Box top: int(((centerX - radius) - Xoffset) / pixelSize).
+>>>ScanArray Express[PerkinElmer]							
+% > B543+1SD	float	linear_scale	SpecializedQuantitationType			Cy3	The percentage of feature pixels with intensities more than one standard deviation above the background pixel intensity, in the 543nm channel.
+% > B543+2SD	float	linear_scale	SpecializedQuantitationType			Cy3	The percentage of feature pixels with intensities more than two standard deviations above the background pixel intensity, in the 543nm channel.
+% > B633+1SD	float	linear_scale	SpecializedQuantitationType			Cy5	The percentage of feature pixels with intensities more than one standard deviation above the background pixel intensity, in the 633nm channel.
+% > B633+2SD	float	linear_scale	SpecializedQuantitationType			Cy5	The percentage of feature pixels with intensities more than two standard deviations above the background pixel intensity, in the 633nm channel.
+B Pixels	integer	linear_scale	SpecializedQuantitationType				The total number of background pixels.
+B543 Mean	float	linear_scale	MeasuredSignal	1		Cy3	Mean pixel background intensity in 543nm channel.
+B543 Median	float	linear_scale	SpecializedQuantitationType	1		Cy3	Median pixel background intensity in 543nm channel.
+B543 SD	float	linear_scale	Error	1	B543 Median	Cy3	Standard deviation of the raw 543nm background pixel intensity.
+B633 Mean	float	linear_scale	MeasuredSignal	1		Cy5	Mean pixel background intensity in 633nm channel.
+B633 Median	float	linear_scale	SpecializedQuantitationType	1		Cy5	Median pixel background intensity in 633nm channel.
+B633 SD	float	linear_scale	Error	1	B633 Median	Cy5	Standard deviation of the raw 633nm background pixel intensity.
+Cy3 % > B + 1 SD	float	linear_scale	SpecializedQuantitationType			Cy3	The percentage of feature pixels with intensities more than one standard deviation above the background pixel intensity, in the Cy3 channel.
+Cy3 % > B + 2 SD	float	linear_scale	SpecializedQuantitationType			Cy3	The percentage of feature pixels with intensities more than two standard deviations above the background pixel intensity, in the Cy3 channel.
+Cy3 B Mean	float	linear_scale	MeasuredSignal	1		Cy3	Mean pixel Cy3 background intensity in Channel Cy3.
+Cy3 B Median	float	linear_scale	SpecializedQuantitationType	1		Cy3	Median pixel Cy3 background intensity in Channel Cy3.
+Cy3 B SD	float	linear_scale	Error	1	Cy3 B Median	Cy3	Standard deviation of the raw Cy3 background pixel intensity.
+Cy3 F % Sat.	float	linear_scale	SpecializedQuantitationType			Cy3	The percentage of feature pixels in the Cy3 channel that are saturated.
+Cy3 Log Ratio							
+Cy3 Mean	float	linear_scale	MeasuredSignal			Cy3	Mean pixel Cy3 foreground intensity in Channel Cy3.
+Cy3 Mean - B	float	linear_scale	SpecializedQuantitationType			Cy3	The mean feature pixel intensity in the Cy3 channel with the mean background subtracted.
+Cy3 Mean of Ratios							
+Cy3 Median	float	linear_scale	SpecializedQuantitationType			Cy3	Median pixel Cy3 foreground intensity in Channel Cy3.
+Cy3 Median - B	float	linear_scale	SpecializedQuantitationType			Cy3	The median feature pixel intensity in the Cy3 channel with the median background subtracted.
+Cy3 Median of Ratios							
+Cy3 N (Mean-B)	float	linear_scale	DerivedSignal			Cy3	Mean pixel Cy3 foreground intensity in Channel Cy3 with the mean background subtracted, globally normalized.
+Cy3 N (Median-B)	float	linear_scale	SpecializedQuantitationType			Cy3	Median pixel Cy3 foreground intensity in Channel Cy3 with the median background subtracted, globally normalized.
+Cy3 N Log Ratio							
+Cy3 N Mean	float	linear_scale	DerivedSignal			Cy3	Globally normalized mean pixel Cy3 foreground intensity in Channel Cy3.
+Cy3 N Mean of Ratios							
+Cy3 N Median	float	linear_scale	SpecializedQuantitationType			Cy3	Globally normalized median pixel Cy3 foreground intensity in Channel Cy3.
+Cy3 N Median of Ratios							
+Cy3 N Ratio of Means							
+Cy3 N Ratio of Medians							
+Cy3 N Rgn Ratio							
+Cy3 Ratio of Means							
+Cy3 Ratio of Medians							
+Cy3 Ratios SD							
+Cy3 Rgn Ratio							
+Cy3 Rgn R�							
+Cy3 SD	float	linear_scale	Error		Cy3 Median	Cy3	Standard deviation of the raw Cy3 foreground pixel intensity.
+Cy3 SignalNoiseRatio	float	linear_scale	Ratio			Cy3	The ratio of spot Cy3 intensity to the standard deviation of the local Cy3 background of all spots in the microarray. Spots with a low Signal/Noise Ratio can be quickly identified for later reviews.
+Cy5 % > B + 1 SD	float	linear_scale	SpecializedQuantitationType			Cy5	The percentage of feature pixels with intensities more than one standard deviation above the background pixel intensity, in the Cy5 channel.
+Cy5 % > B + 2 SD	float	linear_scale	SpecializedQuantitationType			Cy5	The percentage of feature pixels with intensities more than two standard deviations above the background pixel intensity, in the Cy5 channel.
+Cy5 B Mean	float	linear_scale	MeasuredSignal	1		Cy5	Mean pixel Cy5 background intensity in Channel Cy5.
+Cy5 B Median	float	linear_scale	SpecializedQuantitationType	1		Cy5	Median pixel Cy5 background intensity in Channel Cy5.
+Cy5 B SD	float	linear_scale	Error	1	Cy5 B Median	Cy5	Standard deviation of the raw Cy5 background pixel intensity.
+Cy5 F % Sat.	float	linear_scale	SpecializedQuantitationType			Cy5	The percentage of feature pixels in the Cy5 channel that are saturated.
+Cy5 Log Ratio							
+Cy5 Mean	float	linear_scale	MeasuredSignal			Cy5	Mean pixel Cy5 foreground intensity in Channel Cy5.
+Cy5 Mean - B	float	linear_scale	SpecializedQuantitationType			Cy5	The mean feature pixel intensity in the Cy5 channel with the mean background subtracted.
+Cy5 Mean of Ratios							
+Cy5 Median	float	linear_scale	SpecializedQuantitationType			Cy5	Median pixel Cy5 foreground intensity in Channel Cy5.
+Cy5 Median - B	float	linear_scale	SpecializedQuantitationType			Cy5	The median feature pixel intensity in the Cy5 channel with the median background subtracted.
+Cy5 Median of Ratios							
+Cy5 N (Mean-B)	float	linear_scale	DerivedSignal			Cy5	Mean pixel Cy5 foreground intensity in Channel Cy5 with the mean background subtracted, globally normalized.
+Cy5 N (Median-B)	float	linear_scale	SpecializedQuantitationType			Cy5	Median pixel Cy5 foreground intensity in Channel Cy5 with the median background subtracted, globally normalized.
+Cy5 N Log Ratio							
+Cy5 N Mean	float	linear_scale	DerivedSignal			Cy5	Globally normalized mean pixel Cy5 foreground intensity in Channel Cy5.
+Cy5 N Mean of Ratios							
+Cy5 N Median	float	linear_scale	SpecializedQuantitationType			Cy5	Globally normalized median pixel Cy5 foreground intensity in Channel Cy5.
+Cy5 N Median of Ratios							
+Cy5 N Ratio of Means							
+Cy5 N Ratio of Medians							
+Cy5 N Rgn Ratio							
+Cy5 Ratio of Means							
+Cy5 Ratio of Medians							
+Cy5 Ratios SD							
+Cy5 Rgn Ratio							
+Cy5 Rgn R�							
+Cy5 SD	float	linear_scale	Error		Cy5 Median	Cy5	Standard deviation of the raw Cy5 foreground pixel intensity.
+Cy5 SignalNoiseRatio	float	linear_scale	Ratio			Cy5	The ratio of spot Cy5 intensity to the standard deviation of the local Cy5 background of all spots in the microarray. Spots with a low Signal/Noise Ratio can be quickly identified for later reviews.
+Dia.	integer	linear_scale	SpecializedQuantitationType				The diameter in um of the feature-indicator.
+Diameter	integer	linear_scale	SpecializedQuantitationType				The diameter in um of the feature-indicator.
+F Pixels	integer	linear_scale	SpecializedQuantitationType				The total number of feature pixels.
+F543 % Sat.	float	linear_scale	SpecializedQuantitationType			Cy3	The percentage of feature pixels in the 543nm channel that are saturated.
+F543 Mean	float	linear_scale	MeasuredSignal			Cy3	Mean pixel foreground intensity in 543nm channel.
+F543 Mean - B543	float	linear_scale	SpecializedQuantitationType			Cy3	The mean feature pixel intensity in the 543nm channel with the mean background subtracted.
+F543 Median	float	linear_scale	SpecializedQuantitationType			Cy3	Median pixel foreground intensity in 543nm channel.
+F543 Median - B543	float	linear_scale	SpecializedQuantitationType			Cy3	The median feature pixel intensity in the 543nm channel with the median background subtracted.
+F543 SD	float	linear_scale	Error		F543 Median	Cy3	Standard deviation of the raw 543nm foreground pixel intensity.
+F633 % Sat.	float	linear_scale	SpecializedQuantitationType			Cy5	The percentage of feature pixels in the 633nm channel that are saturated.
+F633 Mean	float	linear_scale	MeasuredSignal			Cy5	Mean pixel foreground intensity in 633nm channel.
+F633 Mean - B633	float	linear_scale	SpecializedQuantitationType			Cy5	The mean feature pixel intensity in the 633nm channel with the mean background subtracted.
+F633 Median	float	linear_scale	SpecializedQuantitationType			Cy5	Median pixel foreground intensity in 633nm channel.
+F633 Median - B633	float	linear_scale	SpecializedQuantitationType			Cy5	The median feature pixel intensity in the 633nm channel with the median background subtracted.
+F633 SD	float	linear_scale	Error		F633 Median	Cy5	Standard deviation of the raw 633nm foreground pixel intensity.
+Flags	integer	unscaled	SpecializedQuantitationType				
+Footprint	integer	linear_scale	SpecializedQuantitationType				
+ID	string_datatype	unscaled	SpecializedQuantitationType				
+Index	integer	linear_scale	SpecializedQuantitationType				
+Log Ratio	float	log_base_2	Ratio				
+Log Ratio (633/543)	float	log_base_2	Ratio				
+Mean of Ratios	float	linear_scale	SpecializedQuantitationType				The mean of pixel-by-pixel ratios of pixel intensities.
+Mean of Ratios (633/543)	float	linear_scale	SpecializedQuantitationType				The mean of pixel-by-pixel ratios of pixel intensities.
+Median of Ratios	float	linear_scale	SpecializedQuantitationType				The median of pixel-by-pixel ratios of pixel intensities.
+Median of Ratios (633/543)	float	linear_scale	SpecializedQuantitationType				The median of pixel-by-pixel ratios of pixel intensities.
+N Log Ratio	float	linear_scale	SpecializedQuantitationType				
+N Mean of Ratios	float	linear_scale	SpecializedQuantitationType				
+N Median of Ratios	float	linear_scale	SpecializedQuantitationType				
+N Ratio of Means	float	linear_scale	SpecializedQuantitationType				
+N Ratio of Medians	float	linear_scale	SpecializedQuantitationType				
+N Rgn Ratio	float	linear_scale	SpecializedQuantitationType				
+Name	string_datatype	unscaled	SpecializedQuantitationType				
+Normalize	integer	unscaled	PresentAbsent				The normalization status of the feature (included/not included).
+Ratio of Means	float	linear_scale	SpecializedQuantitationType				The ratio of the mean intensities of each feature for each wavelength.
+Ratio of Means (633/543)	float	linear_scale	SpecializedQuantitationType				The ratio of the mean intensities of each feature for each wavelength.
+Ratio of Medians	float	linear_scale	SpecializedQuantitationType				The ratio of the median intensities of each feature for each wavelength.
+Ratio of Medians (633/543)	float	linear_scale	SpecializedQuantitationType				The ratio of the median intensities of each feature for each wavelength.
+Ratios SD	float	linear_scale	SpecializedQuantitationType		Median of Ratios		
+Ratios SD (633/543)	float	linear_scale	SpecializedQuantitationType		Median of Ratios (633/543)		
+Rgn R2	float	linear_scale	SpecializedQuantitationType				
+Rgn R2 (633/543)	float	linear_scale	SpecializedQuantitationType				
+Rgn Ratio	float	linear_scale	SpecializedQuantitationType				
+Rgn Ratio (633/543)	float	linear_scale	SpecializedQuantitationType				
+Rgn R� (633/543)	float	linear_scale	SpecializedQuantitationType				
+Sum of Means	float	linear_scale	SpecializedQuantitationType				The sum of the mean intensities for each wavelength.
+Sum of Medians	float	linear_scale	SpecializedQuantitationType				The sum of the median intensities for each wavelength.
+X	integer	linear_scale	SpecializedQuantitationType				X coordinate of spot center.
+Y	integer	linear_scale	SpecializedQuantitationType				Y coordinate of spot center.
+>>>SpotFinder[TIGR]							
+BGA	integer	linear_scale	MeasuredSignal	1		Cy3	Spot background in Cy3 channel.
+BGB	integer	linear_scale	MeasuredSignal	1		Cy5	Spot background in Cy5 channel.
+BkgA	integer	linear_scale	MeasuredSignal	1		Cy3	Spot background in Cy3 channel.
+BkgB	integer	linear_scale	MeasuredSignal	1		Cy5	Spot background in Cy5 channel.
+Flag	string_datatype	unscaled	SpecializedQuantitationType				Flag values are generated based on next conditions: A - the number of non-saturated pixels in spot is 0; B- number of non-saturated pixels in spot is between 30 and 50; C- number of non saturated pixels in spot is more then 50; S- fully or partially satur
+Flag A	string_datatype	unscaled	SpecializedQuantitationType			Cy3	Spot flag in channel A. This flag is set by QC filter.
+Flag B	string_datatype	unscaled	SpecializedQuantitationType			Cy5	Spot flag in channel B. This flag is set by QC filter.
+FlagA	string_datatype	unscaled	SpecializedQuantitationType			Cy3	Spot flag in channel A. This flag is set by QC filter.
+FlagB	string_datatype	unscaled	SpecializedQuantitationType			Cy5	Spot flag in channel B. This flag is set by QC filter.
+IA	integer	linear_scale	MeasuredSignal			Cy3	Spot intensity in Cy3 channel corrected for background.
+IB	integer	linear_scale	MeasuredSignal			Cy5	Spot intensity in Cy5 channel corrected for background.
+MNA	integer	linear_scale	SpecializedQuantitationType			Cy3	Mean intensity value in Cy3 channel.
+MNB	integer	linear_scale	SpecializedQuantitationType			Cy5	Mean intensity value in Cy5 channel.
+MeanR	float	linear_scale	Ratio				Spot mean ratio
+MedA	integer	linear_scale	SpecializedQuantitationType			Cy3	Median intensity in Cy3 channel.
+MedB	integer	linear_scale	SpecializedQuantitationType			Cy5	Median intensity in Cy5 channel.
+MedBkgA	integer	linear_scale	SpecializedQuantitationType			Cy3	Median background intensity in Cy3 channel.
+MedBkgB	integer	linear_scale	SpecializedQuantitationType			Cy5	Median background intensity in Cy5 channel.
+MedianR	float	linear_scale	Ratio				Spot median ratio.
+ModeR	float	linear_scale	Ratio				Spot mode ratio.
+PValueA	float	linear_scale	SpecializedQuantitationType			Cy3	P-value in Cy3 channel.
+PValueB	float	linear_scale	SpecializedQuantitationType			Cy5	P-value in Cy5 channel.
+QC	float	linear_scale	SpecializedQuantitationType				Spot total QC score. This is a mean of QC scores in Cy3 and Cy5 channels.
+QCA	float	linear_scale	SpecializedQuantitationType			Cy3	Spot QC score in channel A. This is a geometric mean of shape and S/N QC scores in channel A. S/N QC score is calculated as percentage of pixels in a spot with values higher than 2*median(local BKG). Spot shape QC score is defined as ratio of spot area to
+QCB	float	linear_scale	SpecializedQuantitationType			Cy5	Spot QC score in channel B. This is a geometric mean of shape and S/N QC scores in channel B. S/N QC score is calculated as percentage of pixels in a spot with values higher than 2*median(local BKG). Spot shape QC score is defined as ratio of spot area to
+QCscore	float	linear_scale	SpecializedQuantitationType				
+SA	integer	linear_scale	SpecializedQuantitationType				Spot total area in pixels.
+SC	integer	unscaled	SpecializedQuantitationType				
+SDA	float	linear_scale	Error		IA	Cy3	Standard deviation for spot pixels in Cy3 channel.
+SDB	float	linear_scale	Error		IB	Cy5	Standard deviation for spot pixels in Cy5 channel.
+SDBkgA	float	linear_scale	Error	1	BkgA	Cy3	Standard deviation of the background value in Cy3 channel.
+SDBkgB	float	linear_scale	Error	1	BkgB	Cy5	Standard deviation of the background value in Cy5 channel.
+SF	float	linear_scale	SpecializedQuantitationType				
+SR	integer	unscaled	SpecializedQuantitationType				
+Sat	float	linear_scale	SpecializedQuantitationType				Spot saturation factor. This measure shows the percentage of non-saturated pixels in the spot used for integration.
+UID	integer	linear_scale	SpecializedQuantitationType				Unique identifier for this spot.
+>>>UCSF Spot							
+Dapi	float	linear_scale	MeasuredSignal			DAPI	
+DapiBack	float	linear_scale	MeasuredSignal	1		DAPI	
+DapiFore	float	linear_scale	SpecializedQuantitationType			DAPI	
+Flag	integer	linear_scale	Failed				
+Log2MedRat	float	log_base_2	Ratio				
+Log2Rat	float	log_base_2	Ratio				
+Log2Slope	float	log_base_2	SpecializedQuantitationType				
+MeanLog2Rat	float	log_base_2	SpecializedQuantitationType				
+MeanRat	float	linear_scale	SpecializedQuantitationType				
+MedianRatio	float		Ratio				
+MinF-B	float		SpecializedQuantitationType				
+OriginDist	float		SpecializedQuantitationType				
+RawRat	float	linear_scale	Ratio				
+Ref	float	linear_scale	MeasuredSignal				
+RefBack	float	linear_scale	MeasuredSignal	1			
+RefBackSD		linear_scale	Error	1	RefBack		
+RefFore	float	linear_scale	SpecializedQuantitationType				
+RefForeSD		linear_scale	Error		RefFore		
+RefIntercept	float		SpecializedQuantitationType				
+RefZstat			PValue				
+Slope	float	linear_scale	SpecializedQuantitationType				
+SpotCorr	float		DerivedSignal				
+Test	float	linear_scale	MeasuredSignal	1			
+TestBack	float	linear_scale	MeasuredSignal	1			
+TestBackSD		linear_scale	Error	1	TestBack		
+TestFore	float	linear_scale	SpecializedQuantitationType				
+TestForeSD		linear_scale	Error		TestFore		
+TestIntercept	float		SpecializedQuantitationType				
+TestZstat			PValue				
+nback	integer	linear_scale	SpecializedQuantitationType	1			
+nfore	integer	linear_scale	SpecializedQuantitationType				
+>>>Uni of Toronto in-house analysis software							
+B532 Location	float	linear_scale	MeasuredSignal	1		Cy3	
+B635 Location	float	linear_scale	MeasuredSignal	1		Cy5	
+F532 Location	float	linear_scale	MeasuredSignal			Cy3	
+F635 Location	float	linear_scale	MeasuredSignal			Cy5	
+Intensity	float	log_base_10	SpecializedQuantitationType				
+Raw Log Ratio	float	log_base_10	Ratio	1			
diff --git a/lib/ArrayExpress/MAGETAB.pm b/lib/ArrayExpress/MAGETAB.pm
new file mode 100644
index 0000000..42bba6b
--- /dev/null
+++ b/lib/ArrayExpress/MAGETAB.pm
@@ -0,0 +1,911 @@
+#!/usr/bin/env perl
+#
+# Core module for ArrayExpress MAGE-TAB to MAGE-ML 
+# implementation.
+#
+# Tim Rayner, 2007, EMBL-EBI Microarray Informatics Team
+#
+# $Id: MAGETAB.pm 2072 2008-06-04 15:08:35Z tfrayner $
+
+package ArrayExpress::MAGETAB;
+
+use strict;
+use warnings;
+
+use Class::Std;
+use Carp;
+use File::Spec;
+use File::Path;
+use File::Basename;
+use Text::CSV_XS;
+
+require ArrayExpress::MAGETAB::IDF;
+require ArrayExpress::MAGETAB::SDRF;
+require ArrayExpress::MAGETAB::DataMatrix;
+require ArrayExpress::Datafile::Parser;
+
+use ArrayExpress::Curator::MAGE qw(write_mage unique_identifier postprocess_mage);
+use ArrayExpress::Curator::Common qw(check_linebreaks);
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+use base qw(ArrayExpress::Curator::Logger);
+
+my %magetab_doc   : ATTR( :name<magetab_doc>,                       :default<undef> );
+my %idf           : ATTR( :name<idf>,                               :default<undef> );
+my %sdrfs         : ATTR( :get<sdrfs>,         :set<sdrfs>,         :default<[]>    );
+my %data_matrices : ATTR( :get<data_matrices>, :set<data_matrices>, :default<[]>    );
+my %output_file   : ATTR( :name<output_file>,                       :default<[]>    );
+my %mage          : ATTR( :get<mage>,          :set<mage>,          :default<undef> );
+my %namespace     : ATTR( :name<namespace>,    :default<q{MAGETabulator}> );
+my %authority     : ATTR( :name<authority>,    :default<q{ebi.ac.uk}> );
+my %expt_accession    : ATTR( :name<expt_accession>,             :default<undef> );
+
+# Allow API users to pass in a prot accn service of their own.
+my %prot_accn_service : ATTR( :name<protocol_accession_service>, :default<undef> );
+my %prot_accn_prefix  : ATTR( :name<protocol_accession_prefix>,  :default<undef> );
+
+my %ded_filehandle    : ATTR( :get<ded_filehandle>,   :set<ded_filehandle>,   :default<undef> );
+my %ded_count         : ATTR( :get<ded_count>,        :set<ded_count>,        :default<undef> );
+my %native_filetypes  : ATTR( :get<native_filetypes>, :set<native_filetypes>, :default<undef> );
+my %bags              : ATTR( :name<bags>,    :default<{}> );
+my %clobber           : ATTR( :name<clobber>, :default<0>  );
+my %target_directory     : ATTR( :name<target_directory>,   :init_arg<target_directory>, );
+my %source_directory     : ATTR( :get<source_directory>,    :init_arg<source_directory>,    :default<undef> );
+my %is_standalone        : ATTR( :get<is_standalone>,       :init_arg<is_standalone>,       :default<0> );
+my %qt_filename          : ATTR( :get<qt_filename>,         :init_arg<qt_filename>,         :default<undef> );
+my %keep_all_qts         : ATTR( :get<keep_all_qts>,        :init_arg<keep_all_qts>,        :default<undef> );
+my %keep_protocol_accns  : ATTR( :get<keep_protocol_accns>, :init_arg<keep_protocol_accns>, :default<undef> );
+my %include_default_qts  : ATTR( :get<include_default_qts>, :init_arg<include_default_qts>, :default<undef> );
+my %reporter_prefix      : ATTR( :get<reporter_prefix>,     :init_arg<reporter_prefix>,     :default<undef> );
+my %compseq_prefix       : ATTR( :get<compseq_prefix>,      :init_arg<compseq_prefix>,      :default<undef> );
+my %use_plain_text       : ATTR( :get<use_plain_text>,      :init_arg<use_plain_text>,      :default<undef> );
+my %skip_datafiles       : ATTR( :get<skip_datafiles>,      :init_arg<skip_datafiles>,      :default<undef> );
+my %in_relaxed_mode      : ATTR( :name<in_relaxed_mode>,    :default<0> );
+my %ignore_size_limits   : ATTR( :name<ignore_size_limits>, :default<undef> );
+
+# Make this visible to users of the module.
+our $VERSION = 1.1;
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    croak("Error: no experiment accession set.")
+	unless defined( $expt_accession{$id} );
+
+    croak("Error: no target directory set.")
+	unless defined( $target_directory{$id} );
+
+    croak("Error: no IDF/MAGE-TAB doc filename set.")
+	unless (defined( $idf{$id} ) || defined( $magetab_doc{$id} ) );
+
+    # Create the target directory, if not already present:
+    mkpath( $target_directory{$id}, undef, oct(777) );
+
+    # If protocol accession service is defined, accession prefix must
+    # be also. Ignore all this if we're not going to be using it
+    # anyway (e.g. during validation).
+    unless ( $self->get_keep_protocol_accns() ) {
+        if ( (    $CONFIG->get_AUTOSUBS_DSN()
+	       || $self->get_protocol_accession_service() )
+	     && ! defined( $self->get_protocol_accession_prefix() ) ) {
+	    croak("Error: protocol accession service is active"
+	        . " but no accession prefix set.");
+	}
+    }
+
+    $self->set_progname( 'MAGETAB' );
+    $self->set_version( $VERSION );
+
+    $self->initialize_logfiles();
+
+    return;
+}
+
+sub preprocess_magetab_doc {
+
+    # Class method used to parse linebreaks and split a combined
+    # MAGE-TAB (IDF+SDRF) document into its IDF and SDRF components.
+    my ( $class, $doc ) = @_;
+
+    # Check linebreaks here.
+    my ( $eols, $eol_char ) = check_linebreaks( $doc );
+    unless ( $eol_char ) {
+	croak(
+	    sprintf(
+		"Error: Cannot correctly parse linebreaks in file %s"
+		    . " (%s unix, %s dos, %s mac)\n",
+		$doc,
+		$eols->{unix},
+		$eols->{dos},
+		$eols->{mac},
+	    )
+	);
+    }
+
+    if (    ( $eol_char eq "\015" )
+         && ( $Text::CSV_XS::VERSION < 0.27 ) ) {
+
+	# Mac linebreaks not supported by older versions of Text::CSV_XS.
+	croak("Error: Mac linebreaks not supported by this version"
+	    . " of Text::CSV_XS. Please upgrade to version 0.27 or higher.\n");
+    }
+
+    # Split the file here.
+    my $idf_fh  = IO::File->new_tmpfile();
+    my $sdrf_fh = IO::File->new_tmpfile();
+    my ($is_idf, $is_sdrf);
+
+    open( my $fh, '<', $doc )
+	or croak(qq{Error: Unable to open MAGETAB document "$doc": $!});
+
+    LINE:
+    while ( my $line = <$fh> ) {
+	if ( $line =~ /\A \"? \[IDF\] /ixms ) {
+	    $is_idf  = 1;
+	    next LINE;
+	}
+	if ( $line =~ /\A \"? \[SDRF\] /ixms ) {
+	    $is_sdrf = 1;
+	    next LINE;
+	}
+
+	if ( $is_idf && ! $is_sdrf ) {
+	    print $idf_fh $line;
+	}
+	elsif ( $is_idf && $is_sdrf ) {
+	    print $sdrf_fh $line;
+	}
+    }
+
+    close( $fh ) or croak(qq{Error: Unable to close MAGETAB document "$doc": $!});
+    
+    unless ( $is_idf && $is_sdrf ) {
+	croak("Error: Document must contain an [IDF] "
+	    . "section and an [SDRF] section, in that order.");
+    }
+    seek($idf_fh,  0, 0) or die("Error rewinding temporary IDF file: $!");
+    seek($sdrf_fh, 0, 0) or die("Error rewinding temporary SDRF file: $!");
+
+    return ($idf_fh, $sdrf_fh, $eol_char);
+}
+
+sub parse_magetab_doc {
+
+    my ( $self ) = @_;
+
+    unless ( $self->get_magetab_doc() ) {
+	croak("Error: MAGETAB::parse_magetab_doc called without setting filename.");
+    }
+
+    $self->logprint('magetab',
+		    sprintf("Processing MAGE-TAB doc: %s\n\n", $self->get_magetab_doc()));
+
+    # Split the file into IDF and SDRF, and determine the eol_char.
+    my ( $idf_fh, $sdrf_fh, $eol_char )
+	= __PACKAGE__->preprocess_magetab_doc( $self->get_magetab_doc() );
+
+    # Parse the IDF component.
+    my $idf_parser = ArrayExpress::MAGETAB::IDF->new({
+	idf_filehandle             => $idf_fh,
+	eol_char                   => $eol_char,
+	namespace                  => $self->get_namespace(),
+	authority                  => $self->get_authority(),
+	expt_accession             => $self->get_expt_accession(),
+	in_relaxed_mode            => $self->get_in_relaxed_mode(),
+	source_directory           => $self->get_source_directory(),
+    });
+    my $experiment = $self->parse_idf( $idf_parser );
+
+    # Parse the SDRF component.
+    my $sdrf_parser = ArrayExpress::MAGETAB::SDRF->new({
+	sdrf_filehandle            => $sdrf_fh,
+	eol_char                   => $eol_char,
+	namespace                  => $self->get_namespace(),
+	authority                  => $self->get_authority(),
+	expt_accession             => $self->get_expt_accession(),
+	bags                       => $idf_parser->get_bags(),
+	source_directory           => $self->get_source_directory(),
+	in_relaxed_mode            => $self->get_in_relaxed_mode(),
+	skip_datafiles             => $self->get_skip_datafiles(),
+    });
+
+    my $datafiles = $self->parse_sdrf( $sdrf_parser );
+    $self->add_all_mage_objects( $experiment, $sdrf_parser );
+
+    return wantarray
+	   ? ( $datafiles, $self->get_bags(), $self->get_mage() )
+	   : $self->get_mage();
+}
+
+sub parse {
+
+    my ( $self ) = @_;
+
+    unless ( $self->get_idf() ) {
+	croak("Error: MAGETAB::parse called without setting IDF filename.");
+    }
+
+    $self->logprint('magetab',
+		    sprintf("Processing IDF: %s\n\n", $self->get_idf()));
+
+    my $idf_parser = ArrayExpress::MAGETAB::IDF->new({
+	idf                        => $self->get_idf(),
+	namespace                  => $self->get_namespace(),
+	authority                  => $self->get_authority(),
+	expt_accession             => $self->get_expt_accession(),
+	in_relaxed_mode            => $self->get_in_relaxed_mode(),
+	source_directory           => $self->get_source_directory(),
+    });
+
+    my $experiment = $self->parse_idf( $idf_parser );
+
+    my $sdrfs = $self->get_sdrfs();
+    my $mage  = $self->get_mage();
+
+    # FIXME parse the SDRFS etc. here
+    foreach my $sdrf_file (@$sdrfs) {
+
+	# FIXME stitch the SDRFs together here.
+
+    }
+
+    my $datafiles = [];
+    if ( scalar @$sdrfs ) {
+
+	# FIXME only one SDRF supported for now:
+	my $sdrf_file = $sdrfs->[0];  # FIXME delete this line when ready.
+
+	$self->logprint('magetab',
+			sprintf("Processing SDRF: %s\n\n", $sdrf_file));
+
+	my $sdrf_parser = ArrayExpress::MAGETAB::SDRF->new({
+	    sdrf                       => $sdrf_file,
+	    namespace                  => $self->get_namespace(),
+	    authority                  => $self->get_authority(),
+	    expt_accession             => $self->get_expt_accession(),
+	    bags                       => $idf_parser->get_bags(),
+	    source_directory           => $self->get_source_directory(),
+	    in_relaxed_mode            => $self->get_in_relaxed_mode(),
+	    skip_datafiles             => $self->get_skip_datafiles(),
+	});
+
+	$datafiles = $self->parse_sdrf( $sdrf_parser );
+	$self->add_all_mage_objects( $experiment, $sdrf_parser );
+    }
+
+    return wantarray
+	   ? ( $datafiles, $self->get_bags(), $self->get_mage() )
+	   : $self->get_mage();
+}
+
+sub write_mageml {
+
+    my ( $self ) = @_;
+
+    my $output_file;
+    unless ( $output_file = $self->get_output_file() ) {
+	croak("Error: called write_mageml without setting output_file.");
+    }
+
+    if ( my $target = $self->get_target_directory() ) {
+	$output_file = File::Spec->catfile( $target, basename($output_file) );
+    }
+
+    my $mage;
+    unless ( $mage = $self->get_mage() ) {
+	$mage = $self->parse();
+    }
+
+    # Construct the MAGE structure
+    my $mage_identifier = sprintf(
+	"MAGE:%s:%s v%s:%s",
+	$self->get_authority(),
+	$self->get_namespace(),
+	$VERSION,
+	$self->get_expt_accession(),
+    );
+    $mage->identifier( $mage_identifier );
+
+    # Process any external DesignElementDimension filehandles.
+    my ( $ded_fh, $dummy_id );
+    if ( $self->get_ded_count() ) {
+
+	$ded_fh = $self->get_ded_filehandle();
+
+	# Create a placeholder for the insertion of DEDs
+	$dummy_id = 'DUMMY';
+	my $dummy_FD = Bio::MAGE::BioAssayData::FeatureDimension->new(
+	    identifier => $dummy_id,
+	    name       => $dummy_id,
+	);
+	$mage->add_objects( [$dummy_FD] );
+    }
+    else {
+	print STDOUT ("No DesignElementDimension found.\n");
+    }
+
+    # Write out everything but the DED
+    warn ("Writing out initial MAGE-ML...\n");
+
+    my $mage_version = 1.1;
+    my $data_format  = 'tab delimited';
+    my $mage_fh = IO::File->new_tmpfile;
+    write_mage( $mage, $mage_fh, $data_format, $mage_version );
+
+    # Rewind the temporary filehandle
+    seek( $mage_fh, 0, 0 );
+
+    # Fix the DED using classical text processing (lower memory overhead)
+    print STDOUT ("Inserting DesignElementDimension where found...\n");
+    my $output_fh = IO::File->new( $output_file, '>' )
+	or die(
+	    sprintf(
+		"Error: cannot open output file %s: $!\n",
+		$output_file,
+	    ));
+
+    postprocess_mage(
+	$ded_fh,
+	$mage_fh,
+	$output_fh,
+	$dummy_id,
+	$self->get_native_filetypes(),
+    );
+
+    return;
+}
+
+sub get_id_prefix : RESTRICTED {  # FIXME consider ways to combine
+                                  # this with the same TabFile method.
+
+    my ( $self ) = @_;
+
+    return sprintf( "%s:%s", $self->get_authority(), $self->get_namespace() );
+}
+
+###################
+# Private methods #
+###################
+
+sub initialize_logfiles : PRIVATE {
+
+    my ( $self ) = @_;
+
+    # Default to using the IDF filename; only one of these should be
+    # set anyway.
+    my $sourcefile = $self->get_idf() || $self->get_magetab_doc();
+
+    # Sort out our log files.
+    my $logfile_string = File::Spec->catfile(
+	$self->get_target_directory(),
+	basename($sourcefile),
+    );
+    $logfile_string =~ s/\.\w{3,4}$//;     # strip off the extension
+    my ( $vol, $dir, $name ) = File::Spec->splitpath($logfile_string);
+    $self->localize_logfiles({
+	directory => $dir,
+	volume    => $vol,
+    });
+
+    return;
+}
+
+sub parse_idf : PRIVATE {
+
+    my ( $self, $idf_parser ) = @_;
+
+    # Add the protocol accession reassignment service if we've got
+    # one.
+    if ( my $service = $self->generate_prot_accn_service() ) {
+	$idf_parser->set_protocol_accession_service( $service );
+    }
+
+    my ( $mage, $experiment, $sdrfs ) = $idf_parser->parse();
+
+    $self->set_sdrfs($sdrfs || []);
+    $self->set_mage($mage);
+
+    return $experiment;
+}
+
+sub parse_sdrf : PRIVATE {
+
+    my ( $self, $sdrf_parser ) = @_;
+
+    my @datafiles = @{ $sdrf_parser->parse() };
+
+    $self->set_bags( $sdrf_parser->get_bags() );
+
+    # Process the datafiles here.
+    if ( $self->get_skip_datafiles() ) {
+	$self->logprint('magetab', "Skipping Data files...\n\n");
+
+	# E.g. CEL, CHP; recognised solely by filename extension.
+	$self->set_native_filetypes(
+	    $sdrf_parser->get_native_filetypes(),
+	);
+    }
+    else {
+	$self->logprint('magetab', "Processing Data files...\n\n");
+
+	$self->encode_datafiles( \@datafiles );
+    }
+
+    return \@datafiles;
+}
+
+sub add_all_mage_objects : PRIVATE {
+
+    my ( $self, $experiment, $sdrf_parser ) = @_;
+
+    my $mage = $self->get_mage();
+
+    foreach my $bag ( values %{ $self->get_bags() } ) {
+	$mage->add_objects( $bag->() );
+    }
+
+    $experiment->setBioAssays(    $sdrf_parser->get_all_bioassays()    );
+    $experiment->setBioAssayData( $sdrf_parser->get_all_bioassaydata() );
+
+    $self->set_mage($mage);
+
+    return;
+}
+
+sub encode_datafiles : PRIVATE {
+
+    my ( $self, $datafiles ) = @_;
+
+    my $parser = ArrayExpress::Datafile::Parser->new({
+	namespace            => $self->get_id_prefix() . ":" . $self->get_expt_accession(),
+	include_known_qts    => $self->get_include_default_qts(),
+	quantitation_types   => $self->get_qt_filename(),
+	use_binary_datafiles => ! $self->get_use_plain_text(),
+	is_standalone        => $self->get_is_standalone(),
+	output_directory     => $self->get_target_directory(),
+	source_directory     => $self->get_source_directory(),
+	error_fh             => $self->log_fh('magetab'),
+	allow_undef_qts      => $self->get_keep_all_qts(),
+	protocol_bag         => $self->get_bags->{protocol},
+	parameter_bag        => $self->get_bags->{parameter},
+	reporter_prefix      => $self->get_reporter_prefix(),
+	compseq_prefix       => $self->get_compseq_prefix(),
+	ignore_size_limits   => $self->get_ignore_size_limits(),
+	clobber              => $self->get_clobber(),
+    });
+
+    foreach my $file ( @{ $datafiles } ) {
+
+	# Parse the file, generate the DED and extract QTD
+	# info. Output the stripped data file.
+	$parser->parse( $file );
+
+	# If the parser has reset clobber, note it here.
+	$self->set_clobber( $parser->get_clobber() );
+
+	my $data = $file->get_mage_badata();
+
+	# DesignElementDimension.
+	if ( $file->get_ded_type() ) {
+
+	    my $dim_class = sprintf("Bio::MAGE::BioAssayData::%sDimension",
+				    $file->get_ded_type());
+
+	    my $ded;
+	    eval {
+		$ded = $dim_class->new(
+		    identifier => $file->get_ded_identifier(),
+		);
+	    } or croak(
+		sprintf("Error: Unrecognized DED type: %s\n",
+			$file->get_ded_type())
+	    );
+	    $data->setDesignElementDimension($ded) if $data;
+	}
+
+	# Process data matrices and per-hyb data a little differently.
+	if (   $file->get_data_type() eq $CONFIG->get_RAW_DM_FILE_TYPE()
+	    || $file->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE() ) {
+
+	    # Process this later, after all the per-hyb objects have
+	    # been created.
+	    my $identifier_template
+		= sprintf ("%s:%s",
+			   $self->get_id_prefix(),
+			   $self->get_expt_accession());
+
+	    my $dm = ArrayExpress::MAGETAB::DataMatrix->new({
+		datafile    => $file,
+		id_template => $identifier_template,
+		mage_helper => $self,
+	    });
+	    $dm->create_mage();
+	}
+	else {
+
+	    # Process file as per-hyb data.
+	    # QuantitationType and QuantitationTypeDimension.
+	    my ( $qtlist, $qtd_key ) = $self->qts_from_names( $file );
+	    
+	    my $qtd = $self->get_bags->{qtd}->(
+		$qtd_key,
+		{   qt_list             => $qtlist,
+		    identifier_template => sprintf("%s:%s.%s",
+						   $self->get_id_prefix(),
+						   $self->get_expt_accession(),
+						   unique_identifier()),
+		}
+	    );
+	    $file->set_mage_qtd($qtd);
+	    $data->setQuantitationTypeDimension($qtd) if $data;
+	}
+    }
+
+    # Store DesignElementDimension and native file info for later
+    # writing out as MAGE-ML in the postprocessing step.
+    $self->set_ded_filehandle(   $parser->get_feature_fh() );
+    $self->set_ded_count(        $parser->get_ded_count()  );
+    $self->set_native_filetypes( $parser->get_cel_types()  );
+
+    return;
+}
+
+sub qts_from_names {
+
+    my ( $self, $file, $qt_names ) = @_;
+
+    my $bag_of = $self->get_bags();
+
+    $qt_names ||= $file->get_column_headings();
+
+    my ( @qtd_qtlist, $qtd_key );
+    my ( $QT_prefix, $software );
+
+    # FIXME maybe either use dm_chip_type or qt_type here to detect
+    # alternative Affy data (e.g. data matrix). At the moment Affy
+    # data matrix QTs are non-standard format.
+    if ( $file->get_format_type() eq 'Affymetrix' ) {
+        $QT_prefix = 'Affymetrix:QuantitationType:';
+	$software  = 'Affymetrix';
+    }
+    elsif ( my ( $type, $qt_namespace )
+        = ( $file->get_qt_type =~ m{\A (.*?) \[ (.*) \] \z}xms ) ) {
+        $QT_prefix = "${qt_namespace}:${type}_QuantitationType:";
+	$software  = $type;
+    }
+    else {
+        $QT_prefix = sprintf('Unknown:%s_QuantitationType:', $file->get_qt_type());
+	$software  = $file->get_qt_type() eq 'Ambiguous'
+	           ? 'Unknown'
+		   : $file->get_qt_type();
+    }
+
+    foreach my $heading (@$qt_names) {
+
+        my $identifier       = $QT_prefix . $heading;
+        my $quantitationtype = $bag_of->{quantitationtype}->(
+            $identifier,
+            {   identifier   => $identifier,
+                prefix       => $QT_prefix,
+                name         => $heading,
+		software     => $software,
+                data_metrics => $file->get_data_metrics(),
+                container    => $bag_of->{quantitationtype},
+            }
+        );
+        push( @qtd_qtlist, $quantitationtype );
+
+        $qtd_key .= $heading;
+
+    }
+
+    # Quick check for sane QTs (This error should no longer be triggered
+    # by missing confidence indicator targets - see MAGE.pm).
+    foreach my $qt ( @{ $bag_of->{quantitationtype}->() } ) {
+        unless ( $qt->getIdentifier ) {
+            print STDERR (<<'END_ERROR');
+ERROR: At least one QuantitationType found lacking an identifier.
+Please check that the confidence indicator targets in your QT file 
+are represented in the data files.
+
+END_ERROR
+
+            croak(    qq{Error occurred at data file }
+                    . $file->get_name()
+                    . q{QT name: "}
+                    . $qt->getName()
+                    . q{"} );
+        }
+    }
+
+    $qtd_key ||= 'NULL';    # In case of no QTs, e.g. missing file
+
+    return ( \@qtd_qtlist, $qtd_key );
+}
+    
+sub generate_prot_accn_service : PRIVATE {
+
+    # If configured to do so will return a coderef suitable for
+    # passing to MAGETAB::IDF::protocol_accession_service.
+    my ( $self ) = @_;
+
+    # Skip this if we want to keep our original accessions.
+    return if $self->get_keep_protocol_accns();
+
+    # Use user-specified service if available.
+    if ( my $coderef = $self->get_protocol_accession_service() ) {
+	return $coderef;
+    }
+
+    if ( $CONFIG->get_AUTOSUBS_DSN() ) {
+
+	require ArrayExpress::AutoSubmission::DB::Protocol;
+
+	ArrayExpress::AutoSubmission::DB::Protocol->accession_prefix(
+	    $self->get_protocol_accession_prefix(),
+	);
+
+	return sub {
+
+	    my ($user_accession, $expt_accession) = @_;
+	    warn(
+		sprintf(
+		"Assigning accession for protocol %s using autosubmission system...\n",
+		$user_accession,
+		)
+	    );
+
+	    # Arguments are user_accession, expt_accession, protocol name.
+	    my $new_accession
+		= ArrayExpress::AutoSubmission::DB::Protocol->reassign_protocol(
+		$user_accession,
+		$expt_accession,
+		$user_accession,
+	    );
+
+	    return $new_accession;
+	}
+    }
+    else {
+	return;
+    }
+}
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../index.html">
+                <img src="../../T2M_logo.png"
+                     border="0" height="50" alt="Tab2MAGE logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: MAGETAB.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::MAGETAB - A parser class for MAGE-TAB documents.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::MAGETAB;
+ my $magetab = ArrayExpress::MAGETAB->new({
+     idf              => $idf_file,
+     target_directory => $dir,
+     expt_accession   => $accn,
+ });
+
+ $magetab->write_mageml();
+
+=head1 DESCRIPTION
+
+This module acts as a front end to the MAGE-TAB parsing API. Parser
+objects are instantiated with a number of attributes to control how
+the MAGE-TAB document is parsed. Support is only provided for IDF and
+SDRF documents at present; it is anticipated that the parser will be
+extended to support ADF at a later date.
+
+Currently the parser is built using a MAGEv1.1 object model to store
+the MAGE-TAB metadata. It is envisioned that this dependence on the
+Bio::MAGE modules may be removed once a full MAGE-TAB object model is
+agreed upon by the community.
+
+To simplify the process of data submission, ArrayExpress has
+introduced a new flavour of MAGE-TAB in which the IDF and SDRF
+sections are combined into a single worksheet. This parser supports
+both MAGE-TAB v1.1 documents (with separate IDF and SDRF) and these
+combined documents.
+
+=head1 METHODS
+
+=over 2
+
+=item C<new>
+
+Object constructor. This recognises the following attributes:
+
+=over 4
+
+=item C<idf>
+
+The path of the IDF file with which to start parsing.
+
+=item C<magetab_doc>
+
+The path of a combined IDF+SDRF file to parse.
+
+=item C<output_file>
+
+The name of the output MAGE-ML file.
+
+=item C<namespace>
+
+The namespace to use in MAGE identifier creation.
+
+=item C<authority>
+
+The authority to use in MAGE identifier creation.
+
+=item C<expt_accession>
+
+The accession number assigned to the experiment.
+
+=item C<target_directory>
+
+The directory into which to write the output files.
+
+=item C<source_directory>
+
+The directory which contains all data and SDRF files.
+
+=item C<is_standalone>
+
+A flag indicating whether the script is able to connect to
+ArrayExpress to retrieve array design information. It is sometimes
+desirable to skip these downloads, which can be quite large.
+
+=item C<qt_filename>
+
+QuantitationType file. This option allows you to specify a custom
+QuantitationType definition file to override those defined as part of
+the Tab2MAGE package. See L<ArrayExpress::Datafile::QT_list> for more
+information.
+
+=item C<include_default_qts>
+
+This option can be used in conjunction with C<qt_filename> to indicate
+that the QuantitationType listing from the Tab2MAGE package itself
+should be included in the lists of known QuantitationTypes used in
+data file parsing. The default behaviour is to deactivate these known
+QTs if a custom QT file is to be used.
+
+=item C<keep_all_qts>
+
+A flag indicating whether unrecognised QuantitationTypes in data files
+should be kept or not. The default behaviour is to strip unrecognised
+columns out of the data files.
+
+=item C<reporter_prefix>
+
+The prefix to be used during Reporter identifier construction. This
+prefix is prepended to the identifiers listed in the data files.
+
+=item C<compseq_prefix>
+
+The prefix to be used during CompositeElement (CompositeSequence)
+identifier construction. This prefix is prepended to the identifiers
+listed in the data files.
+
+=item C<protocol_accession_service>
+
+A code reference used to reassign protocol accessions. See
+L<PROTOCOL_ACCESSIONS>, below.
+
+=item C<protocol_accession_prefix>
+
+The prefix to be used for protocol accession creation, when the
+autosubmissions system is in use. See L<PROTOCOL_ACCESSIONS>, below.
+
+=item C<keep_protocol_accns>
+
+A flag indicating whether the protocols in the IDF should be assigned
+new accession numbers. This option overrides C<protocol_accession_service>.
+
+=item C<use_plain_text>
+
+Some file formats are only supported in their native forms by
+ArrayExpress. Nonetheless, this package can parse some of these data
+formats into tab-delimited representations fully encoded in the
+MAGE-ML document (examples include Nimblegen data, Affymetrix CEL
+files).
+
+=item C<skip_datafiles>
+
+This option tells the parser to skip attempting to read the data files
+referenced by a given MAGE-TAB document, and instead attempts to
+generate MAGE in their absence. This option is particularly useful for
+unsupported data file formats.
+
+=item C<ignore_size_limits>
+
+The Tab2MAGE configuration file allows the user to set maximum data
+file sizes for parsing and web download, to provide some protection
+from overloading the system in a production pipeline setting. To
+temporarily ignore these size limits, use this option.
+
+=item C<in_relaxed_mode>
+
+A flag indicating whether to allow minor errors during parsing. At the
+moment the only errors which are ignored by this option are C<Term
+Source REF>, C<Protocol REF>, C<Parameter Value []> and C<Factor Value
+[]> columns which reference Names which have not been defined in the
+IDF.
+
+=item C<clobber>
+
+Flag indicating whether or not to overwrite existing files without
+prompting the user.
+
+=back
+
+=item C<parse>
+
+Starts the MAGE-TAB parse and loads the document into memory.
+
+=item C<write_mageml>
+
+Writes out MAGE-ML corresponding to the input MAGE-TAB document. If
+the MAGE-TAB has not yet been parsed, C<parse()> is called
+automatically.
+
+=back
+
+=head1 PROTOCOL_ACCESSIONS
+
+The parser provides a set of callbacks which can be used to assign
+MAGE-TAB Protocol Names to unique accessions at the point of
+parsing. If the autosubmissions system has been set up and configured,
+then the parser will default to using that mechanism to assign
+protocol accessions. If you wish to use your own service, you may use
+the C<protocol_accession_service> and C<protocol_accession_prefix>
+attributes to control this. The C<protocol_accession_service> should
+point to a code reference which will accept two arguments: (a) the
+Protocol Name as given in the IDF, and (b) the experiment
+accession. The code reference should return a unique accession which
+will then be assigned to the protocol in the output MAGE-ML.
+
+If the autosubmissions system is to be used, the
+C<protocol_accession_prefix> attribute must be set, e.g. to "P-MTAB-".
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2008.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments. 
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+1;
diff --git a/lib/ArrayExpress/MAGETAB/Checker.pm b/lib/ArrayExpress/MAGETAB/Checker.pm
new file mode 100644
index 0000000..7d0962f
--- /dev/null
+++ b/lib/ArrayExpress/MAGETAB/Checker.pm
@@ -0,0 +1,2034 @@
+#!/usr/bin/env perl
+#
+# Module to provide basic methods used by the expt_check script.
+#
+# Tim Rayner 2005, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Checker.pm 2069 2008-06-04 14:33:52Z tfrayner $
+#
+
+use strict;
+use warnings;
+
+=pod
+
+=begin html
+
+    <div><a name="top"></a>
+      <table class="layout">
+	  <tr>
+	    <td class="whitetitle" width="100">
+              <a href="../../../index.html">
+                <img src="../../MAGETAB_logo.png"
+                     border="0" height="50" alt="MAGE-TAB logo"></td>
+              </a>
+	    <td class="pagetitle">Module detail: Checker.pm</td>
+	  </tr>
+      </table>
+    </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::MAGETAB::Checker - MAGE-TAB experiment checking.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::MAGETAB::Checker;
+ 
+ my $checker = ArrayExpress::MAGETAB::Checker->new({
+     idf => $magetab_idf,
+ });
+ 
+ $checker->check();
+
+=head1 DESCRIPTION
+
+This module provides the basic operations needed for checking a
+MAGE-TAB document and associated data files.
+
+=head1 OPTIONS
+
+The following options must be used in addition to those provided by the
+parent class (see L<ArrayExpress::Curator::ExperimentChecker>):
+
+=over 2
+
+=item C<idf>
+
+The MAGE-TAB IDF filename to be checked.
+
+=back
+
+=head1 TESTS
+
+The following tests are performed by this module, with output
+printed to the error and/or report logs:
+
+=over 4
+
+=item B<IDF Contents>
+
+Checks that at least one of each of the following items are present in
+the IDF, warning the user if any are missing:
+
+ Experimental Factor
+ Contact
+ Publication
+ Protocol
+ SDRF
+
+=item B<Experiment info>
+
+Warns the user if any of the following pieces of experiment annotation
+are missing:
+
+ Experiment Design
+ Release Date
+ Title
+ Description
+
+=item B<Term Sources>
+
+Checks all instances of Term Source REFs, and warns the user if an
+undeclared Term Source Name has been used. Also notifies the user if a
+Term Source declared in the IDF has not been used in the SDRF.
+
+=item B<Experimental Factors>
+
+Checks all SDRF Factor Value columns, and warns the user if an
+undeclared Experimental Factor Name has been used. Also notifies the
+user if a Experimental Factor declared in the IDF has not been used in
+the SDRF.
+
+=item B<Undeclared Experimental Factors>
+
+Checks all material characteristics against the factors described by
+the document, alerting the user if any such characteristics vary
+during the experiment without having been declared as an experimental
+factor.
+
+=item B<Source Characteristics>
+
+Checks that each Source material has at least some annotation. 
+This is not, however, a full test of MIAME compliance.
+
+=item B<PubMed ID>
+
+Warns the user if a non-numeric PubMed ID has been entered.
+
+=item B<Submission and release dates>
+
+Warns the user if experiment or release dates are in an incorrect
+format.
+
+=item B<Submitter>
+
+Checks that a submitter contact has been provided, and that their last
+name and email address have both been included in the IDF.
+
+=item B<Protocols>
+
+Checks that all protocols referenced in the Hybridization spreadsheet 
+section are declared in the Protocol section (and vice versa).
+
+=item B<Protocol description length>
+
+Warns if any of the protocol texts seem too brief.
+
+=item B<Protocol usage>
+
+Checks that a protocol has been attached to each step of the 
+experiment (biomaterial treatments, hybridization, scanning and normalization).
+
+=item B<Parameters>
+
+Checks that all parameters referenced in the Hybridization spreadsheet 
+section are declared in the Protocol section (and vice versa).
+
+=item B<Files>
+
+Confirms that each data file is associated with an array design (i.e.,
+Array Design REF or Array Design File), and at least one experimental
+factor value.
+
+=item B<Experiment Design Graph>
+
+Creates the links between sample, extract, labeled extract and
+hybridization, and writes a biomaterials log file listing the 
+numbers of each. If the Graphviz software is installed 
+(http://www.graphviz.org) then the script will use the 'dot' 
+program to produce a PNG format graph showing how the
+various components relate to each other.
+
+=back
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2007.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+  <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" 
+       width="125" 
+       height="37" 
+       border="0" 
+       alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::MAGETAB::Checker;
+
+use Carp;
+use English qw( -no_match_vars );
+use Class::Std;
+use List::Util qw(first);
+use List::MoreUtils qw(pairwise);
+use File::Spec;
+use File::Basename;
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(
+    get_filepath_from_uri
+    $RE_EMPTY_STRING
+    $RE_COMMENTED_STRING
+    $RE_SURROUNDED_BY_WHITESPACE
+);
+
+require ArrayExpress::Datafile;
+require ArrayExpress::MAGETAB;
+require ArrayExpress::MAGETAB::Checker::IDF;
+require ArrayExpress::MAGETAB::SDRF;
+
+use ArrayExpress::Curator::Report qw(
+    format_description
+);
+
+use base qw(ArrayExpress::Curator::ExperimentChecker);
+
+# The entry-point IDF file.
+my %idf          : ATTR( :get<idf>,         :init_arg<idf>,    :default<undef> );
+
+# Attributes used for checking combined IDF/SDRF documents.
+my %magetab_doc     : ATTR( :name<magetab_doc>,     :default<undef> );
+my %idf_filehandle  : ATTR( :name<idf_filehandle>,  :default<undef> );
+my %sdrf_filehandle : ATTR( :name<sdrf_filehandle>, :default<undef> );
+my %eol_char        : ATTR( :name<eol_char>,        :default<undef> );
+
+# Flag indicating we can overwrite files.
+my %clobber      : ATTR( :name<clobber>, :default<0>  );
+
+# Used to store Names defined in the IDF.
+my %termsources  : ATTR( :get<termsources>, :set<termsources>, :default<{}> );
+my %factors      : ATTR( :get<factors>,     :set<factors>,     :default<{}> );
+my %protocols    : ATTR( :get<protocols>,   :set<protocols>,   :default<{}> );
+my %parameters   : ATTR( :get<parameters>,  :set<parameters>,  :default<{}> );
+
+# Used to accumulate SDRF info for passing back to the data file
+# checks.
+my %file_info      : ATTR( :get<file_info>,      :set<file_info>,      :default<[]> );
+my %hybridizations : ATTR( :get<hybridizations>, :set<hybridizations>, :default<{}> );
+my %scans          : ATTR( :get<scans>,          :set<scans>,          :default<{}> );
+my %normalizations : ATTR( :get<normalizations>, :set<normalizations>, :default<{}> );
+
+# Used to store per-row SDRF info.
+my %row_files    : ATTR( :get<row_files>,   :set<row_files>,   :default<[]>    );
+my %row_array    : ATTR( :get<row_array>,   :set<row_array>,   :default<undef> );
+my %row_factors  : ATTR( :get<row_factors>, :set<row_factors>, :default<[]>    );
+
+# Used to check usage of IDF objects in the SDRF.
+my %protocols_used   : ATTR( :get<protocols_used>,   :set<protocols_used>,   :default<{}> );
+my %parameters_used  : ATTR( :get<parameters_used>,  :set<parameters_used>,  :default<{}> );
+my %factors_used     : ATTR( :get<factors_used>,     :set<factors_used>,     :default<{}> );
+my %termsources_used : ATTR( :get<termsources_used>, :set<termsources_used>, :default<{}> );
+
+# Used to track Characteristics for each material, to check for consistency.
+my %current_material : ATTR( :get<current_material>, :set<current_material>, :default<undef> );
+my %char_cache       : ATTR( :get<char_cache>,       :set<char_cache>,       :default<{}>    );
+my %reported_cache   : ATTR( :get<reported_cache>,   :set<reported_cache>,   :default<{}>    );
+
+# Used to store the MAGE container hashrefs for e.g. visualization.
+my %mage_generator : ATTR( :get<mage_generator>, :set<mage_generator>, :default<undef> );
+
+sub START {
+    my ( $self, $id, $args ) = @_;
+
+    unless ( defined( $idf{$id} || defined( $magetab_doc{$id} ) ) ) {
+	croak("Error: No IDF filename passed to MAGE-TAB checker.");
+    }
+
+    # Localise to CWD, or value of get_log_to_current_dir.
+    my $inputfile = $self->get_idf() || $self->get_magetab_doc();
+    my $logfile_string = File::Spec->rel2abs($inputfile);
+    $logfile_string =~ s/\.\w{3,4}$//;     # strip off the extension
+    my ( $vol, $dir, $name ) = File::Spec->splitpath($logfile_string);
+    $self->localize_logfiles({
+	directory => $dir,
+	volume    => $vol,
+	name      => $name,
+    });
+
+    return;
+}
+
+sub get_files_and_annotation : RESTRICTED {
+
+    my ( $self ) = @_;
+
+    print STDOUT ("Running in MAGE-TAB mode...\n");
+
+    $self->logprint( 'error',  "* MAGE-TAB mode *\n\n" );
+    $self->logprint( 'report', "* MAGE-TAB mode *\n\n" );
+
+    # Sort out any combined MAGE-TAB IDF+SDRF documents here.
+    if ( $self->get_magetab_doc() ) {
+	my ( $idf_fh, $sdrf_fh, $eol_char );
+	my $sighandler = $SIG{__DIE__};
+	delete $SIG{__DIE__};
+	local $EVAL_ERROR;
+	eval {
+	   ( $idf_fh, $sdrf_fh, $eol_char ) =
+	       ArrayExpress::MAGETAB->preprocess_magetab_doc(
+		   $self->get_magetab_doc(),
+	       );
+        };
+	$SIG{__DIE__} = $sighandler if $sighandler;
+	if ( $EVAL_ERROR ) {
+	    $self->logprint(
+		'error',
+		"ERROR: Failed to parse IDF and SDRF section headings: $EVAL_ERROR\n",
+	    );
+	    $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+	    return;
+	}
+	else {
+	    $self->set_idf_filehandle( $idf_fh );
+	    $self->set_sdrf_filehandle( $sdrf_fh );
+	    $self->set_eol_char( $eol_char );
+	}
+    }
+
+    # Check the passed IDF/SDRF and set up our list of Datafile
+    # objects.
+
+    # Check the document naively before we invoke the main MAGE export
+    # parser, which will most likely crash in any case.
+    $self->logprint_line( 'error', 'Naive parsing START' );
+
+    $self->parse_naively();
+
+    $self->logprint_line( 'error', 'Naive parsing END' );
+
+    # Attempt to read in the document using the parser. This will fail
+    # unless the document is structurally valid.
+    $self->logprint_line( 'error', 'Full MAGE-TAB parsing START' );
+
+    # Check if our error level is at ERROR_PARSEFAIL before attempting
+    # MAGE creation.
+    my ( $filelist, $bags, $mage );
+    if ( $self->get_error() & $CONFIG->get_ERROR_PARSEFAIL() ) {
+	$self->logprint(
+	    'error',
+	    "ERROR: Previous checks have determined that a full MAGE-TAB"
+	  . " parse will fail. Skipping and attempting to continue.\n",
+	);
+    }
+    else {
+
+	# Attempt to generate MAGE.
+	( $filelist, $bags, $mage ) = $self->generate_mage();
+    }
+
+    $self->logprint_line( 'error', 'Full MAGE-TAB parsing END' );
+
+    unless ( $mage && $bags ) {
+
+	# If MAGE creation failed, fall back to the naive parse for
+	# files to check; hyb, scan and norm ids.
+	$filelist = $self->get_files_from_naive_parse();
+    }
+
+    # Rewrite Datafile objects which use URIs to reference files.
+    unless ( $self->get_skip_data_checks() ) {
+	foreach my $file ( @$filelist ) {
+	    $self->rewrite_datafile_uri( $file );
+	}
+    }
+
+    # Keys used to map FGEM column headings to hybs, scans, norms;
+    # also stores links to the actual objects as values (PBA for hyb,
+    # DBA for norm, BAT for scan; these are checked later for
+    # protocolapps).
+    my $normalizations  = $self->get_normalizations();
+    my $scans           = $self->get_scans();
+    my $hybridizations  = $self->get_hybridizations();
+    
+    $self->logprint_line( 'error', 'ADF parsing START' );
+    
+    # Populate the $file->get_adf_features() hashrefs within @$filelist
+    if ( $self->get_adf_filename || $self->get_array_accession ) {
+	$self->cache_user_supplied_arrays( $filelist );
+    }
+    else {
+
+	# just populate the keys for now
+	my %array_designs;
+	foreach my $file (@$filelist) {
+	    unless ( $file->get_is_exp() ) {
+		$array_designs{ $file->get_array_design_id() }++;
+	    }
+	}
+
+	# populate the array designs hashref
+	unless ( $self->get_is_standalone() ) {
+
+	    my @accno_table;
+	    foreach my $accno ( keys %array_designs ) {
+
+		# ADF retrieval crashes when AE is down; we trap that here.
+		$self->get_ae_arraydesign({
+		    accession => $accno,
+		});
+	    }
+	}
+    }
+    $self->populate_file_arraydesigns( $filelist );
+    
+    $self->logprint_line( 'error', 'ADF parsing END' );
+
+    # FIXME consider returning $scans as well.
+    return ( $filelist, $hybridizations, $normalizations );
+}
+
+sub generate_mage : PRIVATE {
+
+    my ( $self ) = @_;
+
+    my ( $filelist, $bags, $mage, $magetab );
+
+    my %common_opts = (
+	authority                 => '{AUTHORITY}',
+	namespace                 => '{NAMESPACE}',
+	expt_accession            => '{ACCESSION}',
+	target_directory          => ( $self->get_log_to_current_dir() || q{.} ),
+	source_directory          => $self->get_source_directory(),
+	is_standalone             => $self->get_is_standalone(),
+	qt_filename               => $self->get_qt_filename(),
+	include_default_qts       => $self->get_include_default_qts(),
+	keep_protocol_accns       => 1,
+	skip_datafiles            => 1,
+	clobber                   => $self->get_clobber(),
+    );
+
+    # Don't propagate fatal errors up the call stack.
+    my $sighandler = $SIG{__DIE__};
+    delete $SIG{__DIE__};
+    local $EVAL_ERROR;
+
+    # Either one or the other of the following should be valid.
+    if ( my $idf = $self->get_idf() ) {
+	$magetab = ArrayExpress::MAGETAB->new({
+	    idf => $idf,
+	    %common_opts,
+	});
+
+	# Attempt to generate MAGE.
+	eval {
+	    ( $filelist, $bags, $mage ) = $magetab->parse();
+	};
+    }
+    elsif ( my $doc = $self->get_magetab_doc() ) {
+	$magetab = ArrayExpress::MAGETAB->new({
+	    magetab_doc => $doc,
+	    %common_opts,
+	});
+
+	# Attempt to generate MAGE.
+	eval {
+	    ( $filelist, $bags, $mage ) = $magetab->parse_magetab_doc();
+	};
+    }
+    else {
+	croak("ERROR: No IDF or combined MAGE-TAB document to parse.");
+    }
+
+    # Reinstate our DIE sighandler.
+    $SIG{__DIE__} = $sighandler if $sighandler;
+
+    # Reset our clobber value, based on any changes in the magetab
+    # parser value.
+    $self->set_clobber( $magetab->get_clobber() );
+
+    if ( $EVAL_ERROR ) {
+	$self->logprint(
+	    'error',
+	    "ERROR: MAGE-TAB document is not structurally valid."
+	  . " Parser fails with this error: $EVAL_ERROR\n",
+	);
+	$self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+    }
+    else {
+
+	# Store this object for later visualization.
+	$self->set_mage_generator( $magetab );
+
+	# Record hyb, scan and normalization names for later use in
+	# checking DataMatrices.
+	my (%hybridization, %scan, %normalization);
+	foreach my $pba ( @{ $bags->{'pba'}->() } ) {
+	    $hybridization{$pba->getName()} = $pba;
+
+	    # Single-channel scan BATs. We assume that in such cases
+	    # the extra two-color PBAs have been pruned and we can
+	    # assume a single BAT which represents the Scan event.
+	    if ( scalar @{ $pba->getChannels() || [] } == 1 ) {
+		foreach my $bat ( @{ $pba->getBioAssayTreatments() || [] } ) {
+		    $scan{$bat->getName()} = $bat;
+		}
+	    }
+	}
+	foreach my $pba ( @{ $bags->{'extended_pba'}->() } ) {
+
+	    # Two-channel scan BATs. We look for PBAs pointing to
+	    # themselves as indication that this is the last PBA in
+	    # the PBA -> PBA -> PBA -> MBA chain.
+	    foreach my $bat ( @{ $pba->getBioAssayTreatments() || [] } ) {
+		if ( my $target = $bat->getTarget() ) {
+		    if ( $pba->getIdentifier() eq $target->getIdentifier() ) {
+
+			# End of the PBA chain, must therefore be the Scan PBA.
+			$scan{$bat->getName()} = $bat;
+		    }
+		}
+	    }
+	}
+	foreach my $dba ( @{ $bags->{'dba'}->() } ) {
+	    $normalization{$dba->getName()} = $dba;
+	}
+	$self->set_hybridizations(\%hybridization);
+	$self->set_scans(\%scan);
+	$self->set_normalizations(\%normalization);
+
+	# Check Labeled Extract - Label association is present.
+	$self->check_mage_labeledextracts( $bags->{'labeledextract'}->() );
+
+	# Check annotation of the Sources.
+	$self->check_mage_bmchars( $bags->{'source'}->() );
+
+	# Check that all variation in BMCs are also FVs.
+	$self->check_bmchars_against_fvs( $bags );
+
+	# FIXME check protocols attached at various stages.
+	$self->check_protocol_attachments( $bags );
+
+	# Generate SDRF workflow in biomaterials log.
+	$self->print_sdrf_workflow( $bags );
+    }
+
+    # $mage should be undef on failure.
+    return ( $filelist, $bags, $mage );
+}
+
+sub rewrite_datafile_uri : PRIVATE {
+
+    my ( $self, $file ) = @_;
+
+    my $path;
+    my $sighandler = $SIG{__DIE__};
+    delete $SIG{__DIE__};
+    local $EVAL_ERROR;
+    eval {
+	$path = get_filepath_from_uri(
+	    $file->get_path(),
+	    $self->get_source_directory(),
+	);
+    };
+    $SIG{__DIE__} = $sighandler if $sighandler;
+    if ( $EVAL_ERROR ) {
+	$self->logprint(
+	    'error',
+	    $EVAL_ERROR,
+	);
+	$self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+    }
+    else {
+	$file->set_path( $path );
+	$file->set_name( basename( $path ) );
+    }
+}
+
+sub parse_naively : PRIVATE {
+
+    my ( $self ) = @_;
+
+    # We start with the IDF.
+    my $sdrfs = $self->parse_idf_naively();
+
+    # Check we're not about to read an OS-level file when we've been told not to.
+    foreach my $sdrf ( @{ $sdrfs } ) {
+	if ( File::Spec->file_name_is_absolute($sdrf)
+	    && $self->get_safe_filechecks() ) {
+	    $self->logprint(
+		'error',
+		"ERROR: Absolute SDRF path used in safe file checking mode.\n",
+	    );
+	    $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+	}
+    }
+
+    # This also populates the files attribute with a list of Datafile
+    # objects.
+    $self->parse_sdrfs_naively($sdrfs);
+
+    # Rewind any filehandles (not needed for regular IDF/SDRF). Maybe
+    # consider pos/tell here FIXME?
+    if ( my $idf_fh = $self->get_idf_filehandle() ) {
+	seek($idf_fh, 0, 0)
+	    or croak("Error rewinding IDF temporary filehandle: $!");
+    }
+    if ( my $sdrf_fh = $self->get_sdrf_filehandle() ) {
+	seek($sdrf_fh, 0, 0)
+	    or croak("Error rewinding SDRF temporary filehandle: $!");
+    }
+
+    return;
+}
+
+sub parse_individual_sdrf_naively : PRIVATE {
+
+    my ( $self, $sdrf_parser ) = @_;
+
+    # Check for the presence of the SDRF, or alternatively a filehandle.
+    my $sdrf = $sdrf_parser->get_sdrf();
+
+    # This will crash if the SDRF isn't present, so we eval it.
+    my $sdrf_fh;
+    eval { $sdrf_fh = $sdrf_parser->get_sdrf_filehandle() };
+    
+    unless ( $sdrf_fh ) {
+	$self->logprint(
+	    'error',
+	    qq{ERROR: Unable read SDRF file "$sdrf".\n},
+	);
+	$self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+
+	return;
+    }	
+
+    my $BLANK      = $sdrf_parser->get_blank_regexp();
+    my $csv_parser = $sdrf_parser->get_csv_parser();
+
+    # Scan past any empty or commented lines to get to the header row.
+    my $headings;
+    my $header_string;
+    HEADERLINE:
+    while ( $headings = $csv_parser->getline( $sdrf_fh ) ) {
+	$header_string = join( q{}, @$headings ) if $headings;
+        last HEADERLINE if (   $header_string
+	                    && $header_string !~ $RE_EMPTY_STRING
+	                    && $header_string !~ $RE_COMMENTED_STRING );
+    }
+
+    # Check we've parsed to the end of the file.
+    my ( $error, $mess ) = $csv_parser->error_diag();
+    unless ( $headings || $error == 2012 ) {    # 2012 is the Text::CSV_XS EOF code.
+	$self->logprint(
+	    'error',
+	    sprintf(
+		"Error in tab-delimited format: %s. Bad input was:\n\n%s\n",
+		$mess,
+		$csv_parser->error_input(),
+	    ),
+	);
+	$self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+	return;
+    }
+
+    if ( $header_string && $headings && scalar @{ $headings } ) {
+	my $rowchecks = $self->check_sdrf_header( $headings );
+
+	# Read through the rest of the file, checking values as appropriate.
+	FILE_LINE:
+	while ( my $row = $csv_parser->getline( $sdrf_fh ) ) {
+
+	    # Reset our per-row trackers.
+	    $self->set_row_array(undef);
+	    $self->set_row_files([]);
+	    $self->set_row_factors([]);
+
+	    # Skip empty lines.
+	    my $line = join( q{}, @$row );
+	    next FILE_LINE if ( $line =~ $RE_EMPTY_STRING );
+
+	    # Allow hash comments (FIXME NOT IN SPEC).
+	    next FILE_LINE if ( $line =~ $RE_COMMENTED_STRING );
+
+	    # Strip surrounding whitespace from each element.
+	    foreach my $element ( @$row ) {
+		$element =~ s/$RE_SURROUNDED_BY_WHITESPACE/$1/xms;
+	    }
+
+	    # Actually run the row-level checks here. FIXME we might
+	    # want to run certain tests even if the value is blank;
+	    # e.g. characteristics consistency checking.
+	    for ( my $i = 0; $i < scalar @$row; $i++ ) {
+		unless ( $row->[$i] =~ $BLANK ) {
+		    my $sub = $rowchecks->[$i];
+		    if ( ref $sub eq 'CODE' ) {
+			$sub->( $row->[$i], $headings->[$i] );
+		    }
+		}
+	    }
+
+	    $self->check_file_info();
+	}
+
+	# Check we've parsed to the end of the file.
+	my ( $error, $mess ) = $csv_parser->error_diag();
+	unless ( $error == 2012 ) {    # 2012 is the Text::CSV_XS EOF code.
+	    $self->logprint(
+		'error',
+		sprintf(
+		    "Error in tab-delimited format: %s. Bad input was:\n\n%s\n",
+		    $mess,
+		    $csv_parser->error_input(),
+		),
+	    );
+	    $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+	    return;
+	}
+    }
+    else {
+	my $sdrf = $sdrf_parser->get_sdrf() || 'section';
+	$self->logprint(
+	    'error',
+	    qq{ERROR: Unable to parse header line of SDRF $sdrf.\n},
+	);
+	$self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+    }
+
+    return;
+}
+
+sub parse_sdrfs_naively : PRIVATE {
+
+    my ( $self, $sdrfs ) = @_;
+
+    $self->logprint_line( 'error', '  Naive SDRF parsing START  ' );
+
+    if ( $sdrfs && scalar @{ $sdrfs } ) {
+	foreach my $sdrf ( @{ $sdrfs } ) {
+	    my $sdrf_parser = ArrayExpress::MAGETAB::SDRF->new({
+		sdrf             => $sdrf,
+		source_directory => $self->get_source_directory(),
+	    });
+	    $self->parse_individual_sdrf_naively( $sdrf_parser );
+	}
+    }
+    elsif ( my $sdrf_fh = $self->get_sdrf_filehandle() ) {
+	my $sdrf_parser = ArrayExpress::MAGETAB::SDRF->new({
+	    sdrf_filehandle  => $sdrf_fh,
+	    eol_char         => $self->get_eol_char(),
+	    source_directory => $self->get_source_directory(),
+	});
+	$self->parse_individual_sdrf_naively( $sdrf_parser );
+    }
+    else {
+	$self->logprint(
+	    'error',
+	    "ERROR: No SDRF files or annotation available.\n",
+	);
+	$self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+    }
+
+    $self->check_idf_term_usage();
+    
+    $self->logprint_line( 'error', '  Naive SDRF parsing END  ' );
+
+    return;
+}
+
+sub check_char_against_cache : PRIVATE {
+
+    my ( $self, $value, $heading ) = @_;
+
+    my $cache    = $self->get_char_cache();
+    my $reported = $self->get_reported_cache();
+    my $material = $self->get_current_material();
+
+    # Drop out if there's no material for these characteristics (this
+    # is a separate error, and will be reported elsewhere).
+    return unless defined($material);
+
+    # Beware autovivification here (of the $material keys).
+    if (     exists $cache->{ $material }{ $heading }
+        && ! exists $reported->{ $material }{ $heading } ){
+
+	unless ( $cache->{ $material }{ $heading } eq $value ) {
+	    $self->logprint(
+		'error',
+		qq{ERROR: The material "$material" has inconsistent }
+	      . qq{characteristics ("$heading") between SDRF rows.\n},
+	    );
+	    $self->add_error( $CONFIG->get_ERROR_PARSEBAD );
+	    $reported->{ $material }{ $heading } = $value;
+	}
+    }
+    else {
+	$cache->{ $material }{ $heading } = $value;
+    }
+
+    $self->set_char_cache( $cache );
+    $self->set_reported_cache( $reported );
+
+    return;
+}
+
+sub check_sdrf_protocol : PRIVATE {
+
+    my ( $self, $name ) = @_;
+
+    # Check we have a declared protocol.
+    unless ( $self->is_valid_protocol( $name ) ) {
+	$self->logprint(
+	    'error',
+	    qq{Warning: Unknown Protocol REF in SDRF: "$name" (must be declared in IDF).\n},
+	);
+
+	# FIXME this should really be PARSEFAIL, but for Protocol REF
+	# with associated Term Source REF this would lead to false
+	# positives. PARSEFAIL will be set if the main parser fails.
+	$self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+    }
+
+    # Record the protocol usage for later.
+    $self->get_protocols_used()->{$name}++;
+
+    return;
+}
+
+sub check_sdrf_termsource : PRIVATE {
+
+    my ( $self, $name ) = @_;
+
+    # Check this is a valid term source.
+    unless ( $self->is_valid_termsource( $name ) ) {
+	$self->logprint(
+	    'error',
+	    qq{ERROR: Unknown Term Source REF in SDRF: "$name" (must be declared in IDF).\n},
+	);
+	$self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+    }
+
+    # Record the termsource usage for later.
+    $self->get_termsources_used()->{$name}++;
+
+    return;
+}
+
+sub check_sdrf_factorvalue : PRIVATE {
+
+    my ( $self, $name ) = @_;
+
+    # Check this is a valid factor name.
+    unless ( $self->is_valid_factor( $name ) ) {
+	$self->logprint(
+	    'error',
+	    qq{ERROR: Unknown Experimental Factor in SDRF: "$name" (must be declared in IDF).\n},
+	);
+	$self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+    }
+
+    # Record the factor usage for later.
+    $self->get_factors_used()->{$name}++;
+
+    return;
+}
+
+sub check_sdrf_parameter : PRIVATE {
+
+    my ( $self, $name ) = @_;
+
+    # Check this is a valid parameter.
+    unless ( $self->is_valid_parameter( $name ) ) {
+	$self->logprint(
+	    'error',
+	    qq{ERROR: Unknown Parameter in SDRF: "$name" (must be declared in IDF).\n},
+	);
+	$self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+    }
+
+    # Record the parameter usage for later.
+    $self->get_parameters_used()->{$name}++;
+
+    return;
+}
+
+sub add_hybridization : PRIVATE {
+
+    my ( $self, $name ) = @_;
+
+    $hybridizations{ident $self}{$name}++;
+
+    return;
+}
+
+sub add_scan : PRIVATE {
+
+    my ( $self, $name ) = @_;
+
+    $scans{ident $self}{$name}++;
+
+    return;
+}
+
+sub add_normalization : PRIVATE {
+
+    my ( $self, $name ) = @_;
+
+    $normalizations{ident $self}{$name}++;
+
+    return;
+}
+
+sub check_sdrf_header : PRIVATE {
+
+    my ( $self, $headings ) = @_;
+
+    # Used in multiple sets of tests, we define these regexps here only once.
+    my $characteristics_re = qr/ Characteristics?             # heading prefix
+				 [ ]*
+				 \[ [ ]* ([^\]]+?) [ ]* \]    # bracketed term
+				 /ixms;
+    my $factorvalue_re     = qr/ Factor [ ]* Values?          # heading prefix
+				 [ ]*
+				 \[ [ ]* ([^\]]+?) [ ]* \]    # bracketed term
+				 (
+				   [ ]*
+				   \( [ ]* ([^\)]+?) [ ]* \)  # optional parens term
+			         )?
+				 /ixms;
+    my $parameter_re       = qr/ Parameter [ ]* Values?       # heading prefix
+				 [ ]*
+				 \[ [ ]* ([^\]]+?) [ ]* \]    # bracketed term
+				 /ixms;
+    my $unit_re            = qr/ Unit                         # heading prefix
+				 [ ]*
+				 \[ [ ]* ([^\]]+?) [ ]* \]    # bracketed term
+				 /ixms;
+
+    # Define the tests to be run on header values. Any subs here will
+    # be passed match values $1 and $2 from the key regexps. FIXME
+    # consider validating the ontology terms held in Characteristics[]
+    # and Unit[] column headers.
+    my %header_test = (
+	$characteristics_re => 1,
+	$factorvalue_re     => sub { $self->check_sdrf_factorvalue(@_) },
+	$parameter_re       => sub { $self->check_sdrf_parameter(@_) },
+	$unit_re            => 1,
+    );
+
+    # Define any tests and/or aggregation methods to be run on the
+    # column values. Any subs here will be passed each value from the
+    # column in turn, and the full column header itself.
+    my %recognized = (
+
+	qr/ Source    [ ]*              Names? /ixms
+	    => sub { $self->set_current_material(shift) },
+
+	qr/ Sample    [ ]*              Names? /ixms
+	    => sub { $self->set_current_material(shift) },
+
+	qr/ Extract   [ ]*              Names? /ixms
+	    => sub { $self->set_current_material(shift) },
+
+	qr/ Labell?ed [ ]* Extract [ ]* Names? /ixms
+	    => sub { $self->set_current_material(shift) },
+
+	qr/ Term [ ]* Accession [ ]* Numbers?  /ixms => 1,
+	qr/ Providers?                         /ixms => 1,
+	qr/ Material [ ]* Types?               /ixms => 1,
+	qr/ Technology [ ]* Types?             /ixms => 1,
+	qr/ Labels?                            /ixms => 1,
+	qr/ Descriptions?                      /ixms => 1,
+	qr/ Performers?                        /ixms => 1,
+	qr/ Dates?                             /ixms => 1,
+	qr/ Image [ ]* Files?                  /ixms => 1,
+
+	qr/ Hybridi[sz]ation [ ]* Names? /ixms
+	    => sub { $self->add_hybridization(@_) },
+
+	qr/ Assay [ ]* Names? /ixms
+	    => sub { $self->add_hybridization(@_) },
+
+	qr/ Scan [ ]* Names? /ixms
+	    => sub { $self->add_scan(@_) },
+
+	qr/ Normali[sz]ation [ ]* Names? /ixms
+	    => sub { $self->add_normalization(@_) },
+
+	qr/ Array [ ]* Design [ ]* Files? /ixms
+	    => sub { $self->protest_unsupported('Array Design File') },
+
+	qr/ Array [ ]* Data [ ]* Files? /ixms
+	    => sub { $self->add_row_file('raw', @_) },
+
+	qr/ Derived [ ]* Array [ ]* Data [ ]* Files? /ixms
+	    => sub { $self->add_row_file('normalized', @_) },
+
+	qr/ Array [ ]* Data [ ]* Matrix [ ]* Files? /ixms
+	    => sub { $self->add_row_matrix_file($CONFIG->get_RAW_DM_FILE_TYPE(), @_) },
+
+	qr/ Derived [ ]* Array [ ]* Data [ ]* Matrix [ ]* Files? /ixms
+	    => sub { $self->add_row_matrix_file($CONFIG->get_FGEM_FILE_TYPE(), @_) },
+
+	# Only check Term Source REF if the optional namespace is omitted.
+	qr/ Term [ ]* Source [ ]* REFs?  # main heading only
+	   /ixms
+	       => sub { $self->check_sdrf_termsource(@_) },
+
+	qr/ Term [ ]* Source [ ]* REFs?  # main heading, and
+	    [ ]* :[^\t\"]+                # optional namespace
+	    /ixms => 1,
+
+	# Only check Protocol REF if the optional namespace is omitted.
+	qr/ Protocol [ ]* REFs?          # main heading only
+	    /ixms
+	       => sub { $self->check_sdrf_protocol(@_) },
+
+	qr/ Protocol [ ]* REFs?          # main heading, and
+	    [ ]* :[^\t\"]+                # optional namespace       
+	    /ixms => 1,
+
+	qr/ Array [ ]* Design [ ]* REFs? # main heading
+	    ( [ ]* :[^\t\"]+ )?           # optional namespace       
+	    /ixms
+	       => sub { $self->set_row_array(shift) },
+
+	qr/ Comments?                    # heading prefix
+	    [ ]*
+	    \[ [ ]* ([^\]]+?) [ ]* \]    # bracketed term
+	    /ixms => 1,
+
+
+	# See above for these regexps.
+	$characteristics_re => sub { $self->check_char_against_cache(@_) },
+	$factorvalue_re     => sub { $self->add_row_factor(@_) },
+	$parameter_re       => 1,
+	$unit_re            => 1,
+    );
+
+    # An array of either CODE refs to run on a given column, or
+    # other true scalar values.
+    my @rowchecks;
+
+    foreach my $col ( @{ $headings || [] } ) {
+	if ( my $result = first { $col =~ m/\A [ ]* $_ [ ]* \z/xms } keys %recognized ) {
+	    push @rowchecks, $recognized{$result};
+
+	    # Check Parameter Value, Factor Value headings here.
+	    my @args;
+	    if ( my $key = first { @args = ($col =~ $_) } keys %header_test ) {
+		my $test = $header_test{$key};
+		if ( ref $test eq 'CODE' ) {
+		    $test->(@args);
+		}
+	    }
+	}
+	else {
+	    push @rowchecks, 0;
+	    $self->logprint(
+		'error',
+		qq{ERROR: Unrecognized SDRF column heading: "$col".\n},
+	    );
+	    $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+	}
+    }
+
+    return \@rowchecks;
+}
+
+sub parse_idf_naively : PRIVATE {
+
+    my ( $self ) = @_;
+
+    $self->logprint_line( 'error', '  Naive IDF parsing START  ' );
+
+    my $idf_checker = ArrayExpress::MAGETAB::Checker::IDF->new({
+	idf              => $self->get_idf(),
+	checker          => $self,
+	source_directory => $self->get_source_directory(),
+
+	# magetab_doc-specific options. These should be undef for
+	# regular IDFs.
+	idf_filehandle => $self->get_idf_filehandle(),
+	eol_char       => $self->get_eol_char(),
+
+	expt_accession => 'DUMMY',
+    });
+
+    # Read the file in, performing some basic checks on structure.
+    eval {
+	$idf_checker->read_file();
+    };
+    if ( $EVAL_ERROR ) {
+	$self->logprint(
+	    'error',
+	    "Error reading IDF format: $EVAL_ERROR",
+	);
+	$self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+    }
+
+    # Start the checks. These should now be mainly content, not
+    # structure.
+
+    # First, simply check on the existence of required annotation.
+    $self->check_required_idf_annotation($idf_checker);
+
+    # Capture the Term Source Names for later checking (this obviously
+    # needs to happen before any such checking takes place).
+    $self->populate_termsources($idf_checker);
+
+    # Experiment designs, dates, description.
+    $self->check_experiment_annotation($idf_checker);
+
+    # Publications.
+    $self->check_publications($idf_checker);
+
+    # Submitters.
+    $self->check_submitters($idf_checker);
+
+    # Protocols.
+    $self->check_protocols($idf_checker);
+
+    # Experimental Factors.
+    $self->check_factors($idf_checker);
+
+    $self->logprint_line( 'error', '  Naive IDF parsing END  ' );
+
+    return $idf_checker->get_sdrfs();
+}
+    
+sub check_required_idf_annotation : PRIVATE {
+
+    my ( $self, $idf_checker ) = @_;
+
+    my %required_list = (
+	'Experimental Factors'  => 'get_factors',
+	'Contacts (Persons)'    => 'get_people',
+	'Publications'          => 'get_publications',
+	'Protocols'             => 'get_protocols',
+    );
+    while ( my ( $needed_items, $method ) = each %required_list ) {
+	unless ( scalar( @{ $idf_checker->$method } ) ) {
+	    $self->logprint(
+		'error',
+		"Warning: IDF provides no $needed_items.\n",
+	    );
+	    $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+	}
+    }
+
+    # SDRFs are a special case - they're a real problem if missing,
+    # but not if this is a combined IDF+SDRF document.
+    unless ( scalar( @{ $idf_checker->get_sdrfs() } )
+		 || $self->get_magetab_doc() ) {
+	$self->logprint(
+	    'error',
+	    "ERROR: IDF references no SDRF documents.\n",
+	);
+	$self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+    }
+
+    # FIXME these hashes should perhaps instead be lightweight
+    # objects? (re-implement in MAGETAB::IDF superclass).
+    my $expt_hash = $idf_checker->get_experiment();
+
+    $self->check_required_expt_info( $expt_hash );
+
+    my $people = $idf_checker->get_people();
+    if ( scalar @{ $people } ) {
+	$self->check_required_person_info( $people );
+    }
+
+    return;
+}
+
+sub check_required_expt_info : PRIVATE {
+
+    my ( $self, $expt_hash ) = @_;
+
+    my %required_expt_info = (
+	'Release Date'    => 'releasedate',
+	'Title'           => 'title',
+	'Description'     => 'description',
+    );
+    while ( my ( $needed_info, $key ) = each %required_expt_info ) {
+	if ( ! defined( $expt_hash->{ $key } ) ) {
+	    $self->logprint(
+		'error',
+		"Warning: IDF provides no Experiment $needed_info.\n",
+	    );
+	    $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+	}
+	elsif ( my ($type) = ($needed_info =~ /\A (\w*) [ ]* date \z/ixms) ) {
+	    $self->check_date_format( $expt_hash->{ $key }, $type );
+	}
+    }
+
+    return;
+}
+
+sub check_required_person_info : PRIVATE {
+
+    my ( $self, $people ) = @_;
+
+    # Info required for all contacts.  N.B. submitter-specific checks
+    # are made later, in check_submitters().
+    my %required_info = (
+	'Last Name'   => 'lastname',
+	'Affiliation' => 'affiliation',
+    );
+
+    foreach my $person ( @{ $people } ) {
+
+	# Find something to use to identify the Person.
+	my $name =  $person->{'lastname'}
+	         || $person->{'email'}
+		 || '**UNNAMED CONTACT**';
+
+	# Check required info.
+	while ( my ( $needed_info, $key ) = each %required_info ) {
+	    if ( ! defined( $person->{ $key } ) ) {
+		$self->logprint(
+		    'error',
+		    qq{Warning: IDF provides no $needed_info for Person "$name".\n},
+		);
+
+		# FIXME - we may want to upgrade this if truly required
+		# info is being checked for; currently any requirement
+		# for Affiliation is a bit wishy-washy.
+		$self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+	    }
+	}
+    }
+
+    return;
+}
+
+sub populate_termsources : PRIVATE {
+
+    my ( $self, $idf_checker ) = @_;
+
+    my %termsourcename;
+    foreach my $termsource ( @{ $idf_checker->get_termsources() } ) {
+
+	$termsourcename{$termsource->{'name'}}++
+	    if defined($termsource->{'name'});
+
+	# Term Source without file risks creating meaningless MAGE
+	# Database objects.
+	unless ( $termsource->{'file'} ) {
+	    $self->logprint(
+		'error',
+		sprintf(
+		    "Warning: Term Source %s has no File information.\n",
+		    ($termsource->{'name'} || q{}),
+		),
+	    );
+	    $self->add_error($CONFIG->get_ERROR_PARSEBAD());
+	}
+    }
+    $self->set_termsources(\%termsourcename);
+
+    return;
+}
+
+sub check_experiment_annotation : PRIVATE {
+
+    my ( $self, $idf_checker ) = @_;
+
+    # Experiment Designs is an arrayref.
+    my $expt_hash = $idf_checker->get_experiment();
+    my $designs  = $expt_hash->{'design'};
+    if ( defined $designs && scalar @{ $designs || [] } ) {
+
+	# Collect ExperimentDesignType terms for DW checking.
+	$self->add_expt_designs( @{ $designs } );
+    }
+    else {
+	$self->logprint(
+	    'error',
+	    "Warning: IDF provides no Experimental Design.\n",
+	);
+	$self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+    }
+
+    # Design term sources.
+    my $design_termsources = $expt_hash->{'termsource'};
+    if ( defined $design_termsources ) {
+	foreach my $ts ( @{ $design_termsources || [] } ) {
+	    unless( $self->is_valid_termsource( $ts ) ) {
+		$self->logprint(
+		    'error',
+		    qq{Error: Experimental Design Term Source "$ts" is not declared in the IDF.\n},
+		);
+		$self->add_error($CONFIG->get_ERROR_PARSEFAIL());
+	    }
+	}
+    }
+
+    # Experiment date format also needs checking.
+    if ( defined ( $expt_hash->{'experimentdate'} ) ) {
+	$self->check_date_format( $expt_hash->{'experimentdate'}, q{} );
+    }
+
+    # Print the experiment description to the report log.
+    if ( defined ( $expt_hash->{'description'} ) ) {
+	format_description(
+	    $expt_hash->{'description'},
+            $self->log_fh('report'),
+	);
+    }
+
+    my %typemap = (
+	'Quality Control Type' => 'get_qualitycontroltypes',
+	'Replicate Type'       => 'get_replicatetypes',
+	'Normalization Type'   => 'get_normalizationtypes',
+    );
+    while ( my ( $typeclass, $method ) = each %typemap ) {
+	foreach my $type ( @{ $idf_checker->$method } ) {
+	    unless ( $type->{'type'} ) {
+		$self->logprint(
+		    'error',
+		    "Warning: $typeclass missing value in IDF.\n",
+		);
+		$self->add_error($CONFIG->get_ERROR_PARSEBAD());
+	    }
+	    if ( my $ts = $type->{'termsource'} ) {
+		unless( $self->is_valid_termsource( $ts ) ) {
+		    $self->logprint(
+			'error',
+			qq{Error: $typeclass Term Source "$ts" is not declared in the IDF.\n},
+		    );
+		    $self->add_error($CONFIG->get_ERROR_PARSEFAIL());
+		}
+	    }
+	}
+    }
+
+    return;
+}
+
+sub check_publications : PRIVATE {
+
+    my ( $self, $idf_checker ) = @_;
+    
+    # Check for numeric PubMed ID.
+    foreach my $publication ( @{ $idf_checker->get_publications() } ) {
+	if (   defined ($publication->{'pubmedid'})
+	    && $publication->{'pubmedid'} =~ m/\D/ ) {
+	    $self->logprint(
+		'error',
+		"Warning: PubMed ID contains non-numeric characters: ",
+		$publication->{'pubmedid'}, "\n",
+	    );
+	    $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+	}
+	if ( my $ts = $publication->{'termsource'} ) {
+	    unless( $self->is_valid_termsource( $ts ) ) {
+		$self->logprint(
+		    'error',
+		    qq{Error: Publication Status Term Source "$ts" is not declared in the IDF.\n},
+		);
+		$self->add_error($CONFIG->get_ERROR_PARSEFAIL());
+	    }
+	}
+    }
+
+    return;
+}
+
+sub check_submitters : PRIVATE {
+
+    my ( $self, $idf_checker ) = @_;
+
+    my $submitters_found;
+    foreach my $contact ( @{ $idf_checker->get_people() } ) {
+	if (   defined($contact->{'roles'})
+	    && $contact->{'roles'} =~ /\b submitter \b/xms ) {
+
+	    $submitters_found++;
+	    unless ( $contact->{'lastname'} ) {
+		$self->logprint(
+		    'error',
+		    "Warning: Experiment submitter has no last name.\n",
+		);
+		$self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+	    }
+	    unless ( $contact->{'email'} ) {
+		$self->logprint(
+		    'error',
+		    "Warning: Submitter email address is missing.\n",
+		);
+		$self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+	    }
+	}
+	if ( my $ts = $contact->{'termsource'} ) {
+	    unless( $self->is_valid_termsource( $ts ) ) {
+		$self->logprint(
+		    'error',
+		    qq{Error: Person Roles Term Source "$ts" is not declared in the IDF.\n},
+		);
+		$self->add_error($CONFIG->get_ERROR_PARSEFAIL());
+	    }
+	}
+    }
+    unless ( $submitters_found ) {
+	$self->logprint(
+	    'error',
+	    "Warning: No submitters provided in IDF.\n",
+	);
+	$self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+    }
+
+    return;
+}
+
+sub check_protocols : PRIVATE {
+
+    my ( $self, $idf_checker ) = @_;
+
+    # Check protocol descriptions.
+    my (%protocolname, %parametername);
+    foreach my $protocol ( @{ $idf_checker->get_protocols() } ) {
+
+	$protocolname{$protocol->{'name'}}++ if defined($protocol->{'name'});
+
+	if ( $protocol->{'description'} ) {
+	    unless ( length( $protocol->{'description'} ) > 50 ) {
+		$self->logprint(
+		    'error',
+		    sprintf(
+			qq{Warning: Description for %s protocol "%s" is too short.\n},
+			( $protocol->{'type'} || q{} ),
+			( $protocol->{'name'} || q{} ),
+		    ),
+		);
+		$self->add_error( $CONFIG->get_ERROR_MIAME() );
+	    }
+	}
+	else {
+	    $self->logprint(
+		'error',
+		sprintf(
+		    qq{Warning: Description is missing for %s protocol "%s".\n},
+		    ( $protocol->{'type'} || q{} ),
+		    ( $protocol->{'name'} || q{} ),
+		),
+	    );
+	    $self->add_error( $CONFIG->get_ERROR_MIAME() );
+	}
+	if ( $protocol->{'parameters'} ) {
+	    foreach my $name ( split /\s*;\s*/, $protocol->{'parameters'} ) {
+
+		# We store the protocol name if we can, so that we can
+		# try and match parameters to protocols later. NOT
+		# IMPLEMENTED FIXME. N.B. this won't work for
+		# identically named parameters in separate protocols
+		# anyway (which is legal). FIXME.
+		$parametername{$name} = ($protocol->{'name'} || 1)
+		    if defined $name;
+	    }
+	}
+	if ( my $ts = $protocol->{'termsource'} ) {
+	    unless ( $self->is_valid_termsource( $ts ) ) {
+		$self->logprint(
+		    'error',
+		    qq{Error: Protocol Term Source "$ts" is not declared in the IDF.\n},
+		);
+		$self->add_error($CONFIG->get_ERROR_PARSEFAIL());
+	    }
+	}
+    }
+    $self->set_protocols(\%protocolname);
+    $self->set_parameters(\%parametername);
+
+    return;
+}
+
+sub check_factors : PRIVATE {
+
+    my ( $self, $idf_checker ) = @_;
+
+    my %factorname;
+    foreach my $factor ( @{ $idf_checker->get_factors() } ) {
+
+	$factorname{$factor->{'name'}}++ if defined($factor->{'name'});
+
+	unless ( $factor->{'type'} ) {
+	    $self->logprint(
+		'error',
+		sprintf(
+		    qq{Warning: Experimental Factor "%s" has no Type.\n},
+		    ( $factor->{'name'} || q{} ),
+		),
+	    );
+	    $self->add_error($CONFIG->get_ERROR_PARSEBAD());
+	}
+	if ( my $ts = $factor->{'termsource'} ) {
+	    unless ( $self->is_valid_termsource( $ts ) ) {
+		$self->logprint(
+		    'error',
+		    qq{Error: Experimental Factor Term Source "$ts" is not declared in the IDF.\n},
+		);
+		$self->add_error($CONFIG->get_ERROR_PARSEFAIL());
+	    }
+	}
+    }
+    $self->set_factors(\%factorname);
+
+    return;
+}
+
+sub is_valid_termsource : PRIVATE {
+
+    my ( $self, $name ) = @_;
+
+    return $termsources{ident $self}{$name} ? 1 : 0;
+}
+
+sub is_valid_factor : PRIVATE {
+
+    my ( $self, $name ) = @_;
+
+    return $factors{ident $self}{$name} ? 1 : 0;
+}
+
+sub is_valid_protocol : PRIVATE {
+
+    my ( $self, $name ) = @_;
+
+    return $protocols{ident $self}{$name} ? 1 : 0;
+}
+
+sub is_valid_parameter : PRIVATE {
+
+    my ( $self, $name ) = @_;
+
+    return $parameters{ident $self}{$name} ? 1 : 0;
+}
+
+sub add_row_file : PRIVATE {
+
+    my ( $self, $type, $name ) = @_;
+
+    my %files = map { $_->{'name'} => $_ } @{ $self->get_row_files() };
+
+    $files{$name} = { 'name' => $name, 'type' => $type };
+
+    $self->set_row_files( [ values %files ] );
+
+    return;
+}
+
+sub add_row_matrix_file : PRIVATE {
+
+    my ( $self, $type, $name ) = @_;
+
+    my %files = map { $_->{'name'} => $_ } @{ $self->get_row_files() };
+
+    my $mage_ba_data;
+
+    if ( $type eq $CONFIG->get_FGEM_FILE_TYPE() ) {
+	require Bio::MAGE::BioAssayData::DerivedBioAssayData;
+	$mage_ba_data = Bio::MAGE::BioAssayData::DerivedBioAssayData->new();
+    }
+    elsif ( $type eq $CONFIG->get_RAW_DM_FILE_TYPE() ) {
+	require Bio::MAGE::BioAssayData::MeasuredBioAssayData;
+	$mage_ba_data = Bio::MAGE::BioAssayData::MeasuredBioAssayData->new();
+    }
+    else {
+	croak("Error: unrecognized file type: $type.");
+    }
+
+    $files{$name} = {
+	'name'    => $name,
+	'type'    => $type,
+	'ba_data' => $mage_ba_data,
+    };
+
+    $self->set_row_files( [ values %files ] );
+
+    return;
+}
+
+sub add_row_factor : PRIVATE {
+
+    my ( $self, $value ) = @_;
+
+    my %factors = map { $_ => 1 } @{ $self->get_row_factors() };
+
+    $factors{$value} = 1;
+
+    $self->set_row_factors( [ keys %factors ] );
+
+    return;
+}
+
+sub get_files_from_naive_parse : PRIVATE {
+
+    my ( $self ) = @_;
+
+    my @filelist;
+    foreach my $info ( @{ $self->get_file_info() } ) {
+
+	my $path;
+	if ( my $dir = $self->get_source_directory() ) {
+	    $path = File::Spec->catfile( $dir, $info->{'name'} );
+	}
+	else {
+	    $path = $info->{'name'};
+	}
+
+	# N.B. this will get rewritten later to handle URIs.
+	my $file = ArrayExpress::Datafile->new({
+	    path            => $path,
+	    name            => $info->{'name'},
+	    data_type       => $info->{'type'},
+	    array_design_id => ($info->{'array'} || 'Unknown'),
+	});
+
+	foreach my $factor ( @{ $info->{'factors'} || [] } ) {
+
+	    # FIXME category not easily determined at this point.
+	    $file->add_factor_value('DUMMY', $factor);
+	}
+
+	# Data matrices are distinguished from ordinary FGEMs by
+	# having a mage_badata attribute. FIXME consider adding CDF
+	# info here as an NVT of the badata object.
+	if ( my $obj = $info->{'ba_data'} ) {
+	    $file->set_mage_badata( $obj );
+	}
+	push @filelist, $file;
+    }
+
+    return \@filelist;
+}
+
+sub check_file_info : PRIVATE {
+
+    # Read the data from row_files, arrays, factors, check for
+    # consistency, and store it in %fileinfo for later.
+
+    my ( $self ) = @_;
+
+    foreach my $info ( @{ $self->get_row_files() } ) {
+
+	if ( my $array = $self->get_row_array() ) {
+	    $info->{'array'} = $array;
+	}
+	else {
+	    $self->logprint(
+		'error',
+		qq{WARNING: Data file "$info->{name}" not associated with any array designs.\n},
+	    );
+	    $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+	}
+
+	my @factors = @{ $self->get_row_factors() };
+	if ( scalar @factors ) {
+	    $info->{'factors'} = \@factors;
+	}
+	else {
+	    $self->logprint(
+		'error',
+		qq{Warning: Data file "$info->{name}" not associated with any factor values.\n},
+	    );
+	    $self->add_error( $CONFIG->get_ERROR_MIAME() );
+	}
+
+	$self->add_file_info( $info );
+    }
+
+    return;
+}
+
+sub add_file_info : PRIVATE {
+
+    my ( $self, $info ) = @_;
+
+    my %files = map { $_->{'name'} => $_ } @{ $self->get_file_info() };
+
+    $files{$info->{'name'}} = $info;
+
+    $self->set_file_info( [ values %files ] );
+
+    return;
+}
+
+sub check_idf_term_usage : PRIVATE {
+
+    my ( $self ) = @_;
+
+    # Check that Term Sources, Experimental Factors, Protocols and
+    # Parameters declared in the IDF are all used in the SDRF.
+
+    my %check = (
+	'Term Source'         => { 'idf'  => 'get_termsources',
+				   'sdrf' => 'get_termsources_used', },
+	'Experimental Factor' => { 'idf'  => 'get_factors',
+				   'sdrf' => 'get_factors_used', },
+	'Protocol'            => { 'idf'  => 'get_protocols',
+				   'sdrf' => 'get_protocols_used', },
+	'Parameter'           => { 'idf'  => 'get_parameters',
+				   'sdrf' => 'get_parameters_used', },
+    );
+
+    while ( my ($item, $methods) = each %check ) {
+	my $idf_terms  = $methods->{idf};
+	my $sdrf_terms = $methods->{sdrf};
+	foreach my $term ( keys %{ $self->$idf_terms } ) {
+	    unless ( $self->$sdrf_terms()->{$term} ) {
+		$self->logprint(
+		    'error',
+		    qq{Warning: $item Name "$term" is declared in the IDF but not used in the SDRF.\n},
+		);
+		$self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+	    }
+	}
+    }
+
+    return;
+}
+
+sub protest_unsupported : PRIVATE {
+
+    my ( $self, $feature ) = @_;
+
+    $self->logprint(
+	'error',
+	qq{ERROR: The feature "$feature" is unsupported at this time.\n},
+    );
+    $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+
+    return;
+}
+
+sub print_sdrf_workflow : PRIVATE {
+
+    my ( $self, $bags ) = @_;
+
+    # keys are from Tab2MAGE::create_bags()
+    my @classes = (
+        { 'source'            => 'Sources'               },
+        { 'sample'            => 'Samples'               },
+        { 'extract'           => 'Extracts'              },
+        { 'labeledextract'    => 'Labeled extracts'      },
+        { 'pba'               => 'Hybridizations'        },
+        { 'mbad'              => 'Raw data files'        },
+        { 'dba'               => 'Normalizations'        },
+        { 'dbad'              => 'Normalized data files' },
+    );
+
+    $self->generate_workflow_from_bags( $bags, [ @classes ] );
+
+    return;
+}
+
+sub check_protocol_attachments : PRIVATE {
+
+    my ( $self, $bags ) = @_;
+
+    foreach my $bm_type qw(sample extract labeledextract) {
+	$self->check_mage_treatments_protapps(
+	    $bags->{$bm_type}->(),
+	    $bm_type,
+	);
+    }
+
+    $self->check_mage_hyb_protapps( $bags->{'pba'}->() );
+
+    # This needs to support one and two-color scans, but we've already
+    # gathered all the relevant BATs while generating MAGE.
+    $self->check_mage_scan_protapps( [ values %{ $self->get_scans() } ] );
+    $self->check_mage_norm_protapps( $bags->{'dbad'}->() );
+
+    return;
+}
+
+sub check_mage_scan_protapps : PRIVATE {
+
+    # Check all passed BioAssayTreatments have protocol apps attached
+    # to them. This is MAGE-TAB specific, and we're only using it here
+    # to check on Scan protocols.
+    my ( $self, $bats ) = @_;
+
+    foreach my $bat ( @$bats ) {
+	my $found;
+	foreach my $pa ( @{ $bat->getProtocolApplications() || [] } ) {
+	    $found++ if $pa->getProtocol();
+	}
+	unless ( $found ) {
+	    $self->logprint(
+		'error',
+		sprintf(
+		    qq{Warning: Scan "%s" lacks a scanning protocol.\n},
+		    $bat->getName(),
+		),
+	    );
+	    $self->add_error( $CONFIG->get_ERROR_MIAME() );
+	}
+    }
+
+    return;
+}
+
+sub check_mage_norm_protapps : PRIVATE {
+
+    # Check all DBADs have a protocol attached to them. This is
+    # MAGE-TAB specific.
+    my ( $self, $dbads ) = @_;
+
+    foreach my $dbad ( @$dbads ) {
+	my $found;
+	if ( my $txn = $dbad->getProducerTransformation() ) {
+	    foreach my $pa ( @{ $txn->getProtocolApplications() || [] } ) {
+		$found++ if $pa->getProtocol();
+	    }
+	}
+	unless ( $found ) {
+	    $self->logprint(
+		'error',
+		sprintf(
+		    qq{Warning: Derived Array Data "%s" lacks a normalization protocol.\n},
+		    $dbad->getName(),
+		),
+	    );
+	    $self->add_error( $CONFIG->get_ERROR_MIAME() );
+	}
+    }
+
+    return;
+}
+
+sub check_bmchars_against_fvs : PRIVATE {
+
+    my ( $self, $bags ) = @_;
+
+    # Generate a list of FV OE category => value pairs
+    my @fv_catvals;
+    foreach my $ef ( @{ $bags->{'factor'}->() } ) {
+	foreach my $fv ( @{ $ef->getFactorValues() || [] } ) {
+	    my ( $category, $value );
+	    if ( my $fvvalue = $fv->getValue() ) {
+
+		# Regular FV Value OE.
+		$category = $fvvalue->getCategory();
+		$value    = $fvvalue->getValue();
+	    }
+	    elsif ( my $measurement = $fv->getMeasurement() ) {
+
+		# FV Measurements.
+		my $efc = $ef->getCategory();
+		$category = $efc->getValue();
+
+		# CamelCase the term.
+		$category =~ s/^(.)/uc($1)/e;
+		$category =~ s/_(.)/uc($1)/ge;
+		$category =~ s/_//;
+		
+		$value    = $measurement->getValue();
+	    }
+	    push @fv_catvals, { $category => $value };
+	}
+    }
+
+    # Generate a list of BMC OE category => value pairs.
+    foreach my $type ( qw(source sample extract labeledextract) ) {
+	my @bmc_catvals;
+	foreach my $material ( @{ $bags->{$type}->() || [] } ) {
+	    foreach my $char (
+		@{ $material->getCharacteristics() || [] } ) {
+		my ( $category, $value );
+		if ( $char->getCategory() eq $char->getValue() ) {
+
+		    # Handle nested OEs (e.g. BMC measurements).
+		    my $flattened = $self->flatten_nested_oe($char);
+		    $category = $flattened->getCategory();
+		    $value    = $flattened->getValue();
+		}
+		else {
+
+		    # Regular BMC OEs.
+		    $category = $char->getCategory();
+		    $value    = $char->getValue();
+		}
+		push @bmc_catvals, { $category => $value };
+	    }
+	}
+	$self->compare_fvs_to_bmcs(\@fv_catvals, \@bmc_catvals);
+    }
+
+    return;
+}
+
+sub flatten_nested_oe : PRIVATE {
+
+    my ( $self, $oe ) = @_;
+
+    # Reduce a nested OE to a flattened form in which everything but
+    # the value is discarded. Relies on the value being coded as a
+    # has_value property.
+
+    croak("Error: OntologyEntry is not nested")
+	unless ( $oe->getCategory() eq $oe->getValue() );
+
+    my $category = $oe->getCategory();
+    my $value    = $self->find_nested_oe_value( $oe );
+
+    my $flattened;
+    if ( defined $value ) {
+	require Bio::MAGE::Description::OntologyEntry;
+	$flattened = Bio::MAGE::Description::OntologyEntry->new(
+	    category => $category,
+	    value    => $value,
+	);
+    }
+
+    return $flattened;
+}
+
+sub find_nested_oe_value : PRIVATE {
+
+    my ( $self, $oe ) = @_;
+
+    # Given a nested OE, return the first has_value value encountered
+    # by recursing through the tree.
+
+    # Beware this is very MO-specific FIXME?
+    return $oe->getValue() if ( $oe->getCategory() eq 'has_value'
+			     && $oe->getCategory() ne $oe->getValue() );
+
+    foreach my $child ( @{ $oe->getAssociations() || [] } ) {
+	if ( my $value = $self->find_nested_oe_value( $child ) ) {
+	    return $value;
+	}
+    }
+
+    return;
+}
+
+sub create_data_matrix_mage : PRIVATE {
+
+    my ( $self, $filelist, $magetab ) = @_;
+
+    FILE:
+    foreach my $file ( @{ $filelist || [] } ) {
+	if (  $file->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE()
+	   || $file->get_data_type() eq $CONFIG->get_RAW_DM_FILE_TYPE() ) {
+
+	    # Sort out any data matrix links here. These are processed
+	    # after all the per-hyb objects have been created.
+	    require ArrayExpress::MAGETAB::DataMatrix;
+
+	    my $dm = ArrayExpress::MAGETAB::DataMatrix->new({
+		datafile    => $file,
+		id_template => 'DUMMY',
+		mage_helper => $magetab,
+	    });
+	    my $sighandler = $SIG{__DIE__};
+	    delete $SIG{__DIE__};
+	    local $EVAL_ERROR;
+	    eval{
+		$dm->create_mage();
+	    };
+	    $SIG{__DIE__} = $sighandler if $sighandler;
+	    if ($EVAL_ERROR) {
+		my $err_message = sprintf(
+		    "Error parsing data matrix %s (%s); graph visualization will omit this file.\n",
+		    $file->get_name(),
+		    $EVAL_ERROR,
+		);
+		warn($err_message);
+		$self->logprint(
+		    'error',
+		    $err_message,
+		);
+		$self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+		last FILE;
+	    }
+	}
+    }
+}
+
+sub visualize_experiment : RESTRICTED {
+
+    my ( $self, $filelist ) = @_;
+
+    my $magetab = $self->get_mage_generator();
+
+    unless ( defined($magetab) && $magetab->can('get_bags') ) {
+	$self->logprint(
+	    'error',
+	    "Warning: MAGE objects unavailable for visualization. Skipping.\n",
+	);
+	return;
+    }
+
+    my $bags = $magetab->get_bags();
+
+    # Parse any data matrix headers into MAGE objects.
+    unless ( $self->get_skip_data_checks() ) {
+	$self->create_data_matrix_mage( $filelist, $magetab );
+    }
+
+    # Create the dotfile (and PNG if GraphViz installed)
+    my $output_base = $self->get_idf() || $self->get_magetab_doc();
+    $output_base =~ s/\.\w{3}$// if $output_base;
+    $output_base ||= 'magetab';    # Just in case
+
+    if ( defined($self->get_log_to_current_dir) ) {
+        my $output_dir = $self->get_log_to_current_dir || q{.};
+	my ($vol, $dir, $name) = File::Spec->splitpath($output_base);
+	$output_base = File::Spec->catfile($output_dir, $name);
+    }
+
+    # we'll just use the default font here.
+    require ArrayExpress::Curator::Visualize;
+    ArrayExpress::Curator::Visualize->import(
+	qw(dot_and_png)
+    );
+    my $classes = {
+        'source'            => 'Source',
+        'sample'            => 'Sample',
+        'extract'           => 'Extract',
+        'labeledextract'    => 'Labeled Extract',
+        'pba'               => 'Hybridization',
+        'mba'               => 'Feature Extraction',
+        'datamatrix_mba'    => 'Array Data',
+        'dba'               => 'Normalization',
+        'datamatrix_dba'    => 'Array Data',
+    };
+    $self->set_clobber(
+        dot_and_png(
+	    $bags,
+	    $output_base,
+	    undef,
+	    $self->get_clobber(),
+	    $classes,
+	)
+    );
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/MAGETAB/Checker/IDF.pm b/lib/ArrayExpress/MAGETAB/Checker/IDF.pm
new file mode 100644
index 0000000..c05d4bb
--- /dev/null
+++ b/lib/ArrayExpress/MAGETAB/Checker/IDF.pm
@@ -0,0 +1,76 @@
+#!/usr/bin/env perl
+#
+# $Id: IDF.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::MAGETAB::Checker::IDF;
+
+use Class::Std;
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+use base qw(ArrayExpress::MAGETAB::IDF);
+
+my %checker : ATTR( :name<checker>, :default<\*STDOUT> );
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    unless ( $checker{$id}
+	  && $checker{$id}->isa('ArrayExpress::Curator::ExperimentChecker') ) {
+	die("Error: Invalid or absent checker object.");
+    }
+
+    return;
+}
+
+sub read_file {
+
+    my ( $self ) = @_;
+
+    # Parse the IDF file into memory here.
+    my $array_of_rows = $self->read_as_arrayref();
+
+    # Check tags for duplicates, make sure that tags are recognized.
+    my $idf_hash = $self->validate_arrayref_tags( $array_of_rows );
+
+    # Populate the internal data store with whatever we can recognise;
+    # discard the rest, as it has already been reported.
+    while ( my ( $tag, $values ) = each %{ $idf_hash } ) {
+	if ( my $sub = $self->retrieve_sub( $tag ) ) {
+
+	    # Recognised tag.
+	    $sub->( @$values );
+	}
+    }
+
+    return;
+}
+
+# This method overrides that in the superclass, allowing what would
+# normally be fatal parse errors to be recorded in the logs.
+sub raise_error : RESTRICTED {
+
+    my ( $self, @messages ) = @_;
+
+    my $checker = $self->get_checker();
+
+    my $message = join(q{}, @messages);
+
+    # Make sure the message ends in a single newline.
+    $message =~ s/[\r\n]* \z/\n/xms;
+
+    $checker->logprint(
+	'error',
+	$message,
+    );
+
+    $checker->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/MAGETAB/DataMatrix.pm b/lib/ArrayExpress/MAGETAB/DataMatrix.pm
new file mode 100644
index 0000000..d7ab077
--- /dev/null
+++ b/lib/ArrayExpress/MAGETAB/DataMatrix.pm
@@ -0,0 +1,548 @@
+#!/usr/bin/env perl
+#
+# Module to help in the generation of MAGETAB DataMatrix MAGE objects.
+#
+# Tim Rayner 2007, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: DataMatrix.pm 1852 2007-12-13 10:14:27Z tfrayner $
+#
+
+package ArrayExpress::MAGETAB::DataMatrix;
+
+use strict;
+use warnings;
+
+use Carp;
+use English qw( -no_match_vars );
+use Class::Std;
+
+use ArrayExpress::Curator::Config qw( $CONFIG );
+use ArrayExpress::Curator::MAGE qw(unique_identifier);
+
+use base 'ArrayExpress::Datafile::DataMatrix';
+
+my %id_template    : ATTR( :name<id_template>,    :default<undef> );
+my %datafile       : ATTR( :name<datafile>,       :default<undef> );
+my %mage_helper    : ATTR( :name<mage_helper>,    :default<undef> );
+my %dm_id_template : ATTR( :name<dm_id_template>, :default<undef> );
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    unless ( $self->get_datafile() ) {
+	croak("Error: datafile attribute not set.");
+    }
+    unless ( $self->get_id_template() ) {
+	croak("Error: id_template attribute not set.");
+    }
+    unless ( $self->get_mage_helper() ) {
+	croak("Error: mage_helper attribute not set.");
+    }
+
+    # This is a per-file template. 
+    $self->set_dm_id_template(
+	sprintf(
+	    "%s.%s.DataMatrix",
+	    $self->get_id_template(),
+	    unique_identifier(),
+	)
+    );
+
+    return;
+}
+
+sub create_mage {
+
+    my ( $self ) = @_;
+
+    # Figure out BADim, QTDim and data cube order. Generate MAGE for
+    # QTD and add to data object ($file->get_mage_badata). Map BADim
+    # to bioassays as specified by $file->get_sdrf_id_column.
+
+    my $file    = $self->get_datafile();
+    my $badata  = $file->get_mage_badata();
+    my $dm_hybs = $file->get_heading_hybs();
+    my $dm_qts  = $file->get_heading_qts();
+
+    my $magetab = $self->get_mage_helper();
+    my $bag_of  = $magetab->get_bags();
+
+    # Construct the dimensions
+    my ( $ba_namerefs, $qt_names, $order )
+        = $self->get_dimension_lists( $dm_hybs, $dm_qts );
+
+    # Internal consistency check; order should always be set.
+    croak("Error: Order of Data Matrix data cube is indeterminate.\n")
+        unless $order;
+
+    # Warn on unsupported cube order
+    if ( $order eq 'DQB' ) {
+        warn(
+            qq{WARNING: A BioDataCube order of "$order" is not }
+	  . qq{supported by the ArrayExpress MAGE-ML loader.\n}
+        );
+    }
+    my $bdc = $badata->getBioDataValues();
+    $bdc->setOrder( $order );
+
+    # Create the actual qtd here
+    my ( $qt_list, $qtd_key ) = $magetab->qts_from_names( $file, $qt_names );
+
+    my $qtd = $bag_of->{qtd}->(
+        $qtd_key,
+        {
+	    qt_list             => $qt_list,
+            identifier_template => $self->get_dm_id_template(),
+        }
+    );
+    $file->set_mage_qtd($qtd);
+    $badata->setQuantitationTypeDimension($qtd);
+
+    # Store the old MBAs from the BioAssayDimension for later
+    # repointing to a new DBA/DBAD if this is an Array Data File
+    # converted to Matrix (e.g. Illumina).
+    my @old_mbas;
+    if ( $file->get_data_type() eq $CONFIG->get_RAW_DM_FILE_TYPE() ) {
+	if ( my $ded = $badata->getBioAssayDimension() ) {
+	    foreach my $old ( @{ $ded->getBioAssays() || [] } ) {
+		my $name = $old->getName();
+		push @old_mbas, $old;
+		$bag_of->{mba}->( $name, 'delete' )
+		    or die("Error deleting unwanted MBA: $name");
+	    }
+	}
+    }
+
+    # Find or create list of BioAssay objects given the $ba_namerefs
+    # arrayref of arrayrefs.
+    my $mapped_bioassays = $self->retrieve_mapped_bioassays($ba_namerefs);
+
+    # Sort out the BioAssayMaps here.
+    $self->add_bioassays_to_bioassaymaps( $mapped_bioassays, \@old_mbas );
+
+    # Create and link the BioAssayDimension here.
+    my $bad = Bio::MAGE::BioAssayData::BioAssayDimension->new(
+	identifier => $self->get_dm_id_template() . '.MeasuredBioAssayDimension',
+	bioAssays  => $mapped_bioassays,
+    );
+    $badata->setBioAssayDimension( $bad );
+
+    # FIXME figure out how to add MBA protocol applications? These are
+    # FeatureExtraction protocols, not yet supported by SDRF; this is
+    # not an issue for Derived data, which attaches
+    # ProtocolApplication to ProducerTransformation.
+
+    return;
+}
+
+sub repoint_mba_to_dba : PRIVATE {
+
+    # This is pretty much Illumina-only at the moment.
+
+    my ( $self, $dba, $old_mbas, $file, $bags ) = @_;
+
+    my @bams = @{ $bags->{bam}->() };
+
+    foreach my $old ( @$old_mbas ) {
+	my $old_name = $old->getName();
+	foreach my $bam ( @bams ) {
+	    my @new;
+	    foreach my $source ( @{ $bam->getSourceBioAssays() || [] } ) {
+		if ( $source->isa('Bio::MAGE::BioAssay::MeasuredBioAssay')
+			 && $source->getName() eq $old_name ) {
+
+		    # Replace the old MBA with the new DBA.
+		    push @new, $dba;
+
+		    # Make DBAD from MBAD, repoint Dimensions and
+		    # BioDataCube. FIXME this only remaps the
+		    # first MBAD, not a general solution.
+		    if ( my $mbad = shift @{ $source->getMeasuredBioAssayData() || [] } ) {
+			my $dbad = $bags->{dbad}->(
+			    $mbad->getName(),
+			    {
+				identifier_template => $self->get_dm_id_template(),
+				de_dimension        => $mbad->getDesignElementDimension(),
+				qt_dimension        => $mbad->getQuantitationTypeDimension(),
+				filename            => $mbad->getBioDataValues()->getCube(),
+			    }
+			);
+			$dba->setDerivedBioAssayData( [ $dbad ] );
+			$bags->{mbad}->( $mbad->getName(), 'delete' );
+			$file->set_mage_badata( $dbad );
+		    }
+
+		    my @fvs = @{ $source->getBioAssayFactorValues() || [] };
+		    $dba->setBioAssayFactorValues( \@fvs );
+
+		    # FIXME also switch around FeatureExtraction
+		    # ProtocolApps here?
+		}
+		else {
+		    push @new, $source;
+		}
+	    }
+	    $bam->setSourceBioAssays( \@new );
+	}
+    }
+
+    return;
+}
+
+sub add_bioassays_to_bioassaymaps : PRIVATE {
+
+    my ( $self, $mapped_bioassays, $old_mbas ) = @_;
+
+    my $file    = $self->get_datafile();
+    my $badata  = $file->get_mage_badata();
+    my $magetab = $self->get_mage_helper();
+
+    my $maps = [];
+    if ( $file->get_data_type() eq $CONFIG->get_RAW_DM_FILE_TYPE() ) {
+
+	# Create new DBA if this is an Array Data Matrix, link to the
+	# BioAssay list using $magetab->bam_bag(). Note that we can't
+	# add it to the MBAData because ProducerTransformation is only
+	# for DBAData.
+	my $name = $file->get_name();
+	my $dba = $magetab->get_bags()->{datamatrix_dba}->(
+	    $name,
+	    {
+		identifier_template => $self->get_dm_id_template(),
+		name                => $name,
+	    }
+	);
+	$maps = [
+	    $magetab->get_bags()->{bam}->(
+		$name,
+		{
+		    identifier_template => $self->get_dm_id_template(),
+		    target_bioassay     => $dba,
+		}
+	    ),
+	];
+	$dba->setDerivedBioAssayMap( $maps );
+
+	# Remap old MBA links (generated during SDRF parsing) to the
+	# newly-created DBA.
+	my $bags = $magetab->get_bags();
+	$self->repoint_mba_to_dba( $dba, $old_mbas, $file, $bags );
+
+	# FIXME Array Data Matrix BioAssay lacks FVs at the moment
+	# (Illumina is okay though).
+
+    }
+    elsif ( $file->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE() ) {
+
+	# Use the BioAssayMaps from $badata.
+	my $txn = $badata->getProducerTransformation()
+	    or croak("Error: DBAD has no ProducerTransformation.");
+
+	my $mapping = $txn->getBioAssayMapping()
+	    or croak("Error: DBAD ProducerTransformation has no BioAssayMapping.");
+
+	$maps = $mapping->getBioAssayMaps();
+
+	# Since we're here, add the QuantitationTypeMapping.
+	$self->add_qt_mapping(
+	    $txn,
+	    $file->get_mage_qtd(),
+	    $mapped_bioassays,
+	    $magetab->get_bags()->{qtm},
+	);
+    }
+    else {
+	croak("Error: unrecognized data type: " . $file->get_data_type() );
+    }
+
+    unless ( scalar @$maps ) {
+	croak("Error: BioAssayData cannot be mapped to source BioAssays.");
+    }
+
+    foreach my $map ( @$maps ) {
+	$map->setSourceBioAssays( $mapped_bioassays );
+    }
+
+    return;
+}
+
+sub retrieve_mapped_bioassays : PRIVATE {
+
+    my ( $self, $ba_namerefs ) = @_;
+
+    my $bags      = $self->get_mage_helper()->get_bags();
+    my $id_column = $self->get_datafile->get_sdrf_id_column();
+
+    my $bioassays;
+    if ( $id_column =~ /(?:Hybridi[zs]ation|Assay)(?: *Name)?(?: *REF)?/i ) {
+	$bioassays = $self->map_physical_bioassays(
+	    $ba_namerefs,
+	    $bags->{pba},
+	    $bags->{mba},
+	    $bags->{datamatrix_dba},
+	    $bags->{bam},
+	    $id_column,
+	);	
+    }
+    elsif ( $id_column =~ /Scan(?: *Name)?(?: *REF)?/i ) {
+	$bioassays = $self->map_physical_bioassays(
+	    $ba_namerefs,
+	    $bags->{extended_pba},
+	    $bags->{mba},
+	    $bags->{datamatrix_dba},
+	    $bags->{bam},
+	    $id_column,
+	    $bags->{pba},
+	);
+    }
+    elsif ( $id_column =~ /Normali[zs]ation(?: *Name)?(?: *REF)?/i ) {
+	$bioassays = $self->map_derived_bioassays(
+	    $ba_namerefs,
+	    $bags->{dba},
+	    $bags->{datamatrix_dba},
+	    $bags->{bam},
+	    $id_column,
+	);
+    }
+    else {
+	croak("Error: Unrecognized/unsupported column: $id_column\n");
+    }
+
+    return $bioassays;
+}    
+
+sub map_derived_bioassays : PRIVATE {
+
+    # Data matrices mapped to Normalization allows us to query just for
+    # Normalization Names.
+
+    my ( $self,
+	 $ba_namerefs,
+	 $dba_bag,
+	 $dm_dba_bag,
+	 $bam_bag,
+	 $id_column ) = @_;
+
+    my @ba_list;
+    foreach my $ba_names ( @$ba_namerefs ) {
+
+	my @column_bas;
+	foreach my $ba_name ( @$ba_names ) {
+	    my $col_ba = $self->retrieve_bioassay($ba_name, $dba_bag, $id_column);
+	    push @column_bas, $col_ba;
+	}
+
+	my $bioassay = $self->find_or_create_per_column_bioassay(
+	    \@column_bas,
+	    $self->get_dm_id_template(),
+	    $dm_dba_bag,
+	    $bam_bag,
+	);
+	push @ba_list, $bioassay;
+    }
+
+    return \@ba_list;
+}
+
+sub map_physical_bioassays : PRIVATE {
+
+    # Data matrices mapped to Scan or Hybridization need MBAs
+    # generated for each PBA (hyb/scan). Grouping DBAs are created as
+    # necessary.
+
+    my ( $self,
+	 $ba_namerefs,
+	 $pba_bag,
+	 $mba_bag,
+	 $dm_dba_bag,
+	 $bam_bag,
+	 $id_column,
+	 $fallback_bag ) = @_;
+
+    my @ba_list;
+    foreach my $ba_names ( @$ba_namerefs ) {
+
+	my @column_bas;
+	foreach my $ba_name ( @$ba_names ) {
+
+	    # Scan Name can be used to look up PBAs in
+	    # extended_pba_bag, but note that this won't work for
+	    # single-channel data, which needs a suitable fallback to
+	    # hyb PBA having ImageAcquisition with name=Scan Name.
+	    my $col_pba = $self->retrieve_bioassay(
+		$ba_name,
+		$pba_bag,
+		$id_column,
+		$fallback_bag,
+	    );
+
+	    # Follow the BioAssayTreatments to the end of the PBA
+	    # chain.
+	    $col_pba = $self->find_last_pba($col_pba);
+
+	    # Find or create the MBA here.
+	    my $col_mba = $self->find_or_create_mba(
+		$col_pba,
+		$mba_bag,
+	    );
+
+	    push @column_bas, $col_mba;
+	}
+
+	my $bioassay = $self->find_or_create_per_column_bioassay(
+	    \@column_bas,
+	    $self->get_dm_id_template(),
+	    $dm_dba_bag,
+	    $bam_bag,
+	);
+	push @ba_list, $bioassay;
+    }
+
+    return \@ba_list;
+}
+
+sub find_last_pba : PRIVATE {
+
+    my ( $self, $pba ) = @_;
+
+    # If no treatments, give up immediately.
+    my $bats = $pba->getBioAssayTreatments()
+	or return $pba;
+
+    BAT:
+    foreach my $bat ( @$bats ) {
+	my $new_pba = $bat->getTarget() or next BAT;
+
+	if ( $new_pba->getIdentifier() eq $pba->getIdentifier() ) {
+
+	    # Recursion endpoint.
+	    return $new_pba;
+	}
+	else {
+
+	    # Return the PBA linked to the first Target PBA we find.
+	    return $self->find_last_pba($new_pba);
+	}
+    }
+
+    # If no BATs with Target, return our original PBA.
+    return $pba;
+}
+
+sub find_or_create_mba : PRIVATE {
+
+    my ( $self, $pba, $mba_bag ) = @_;
+
+    my $pba_id = $pba->getIdentifier();
+
+    MBA:
+    foreach my $mba ( @{ $mba_bag->() } ) {
+
+	my $fext = $mba->getFeatureExtraction()
+	    or next MBA;
+
+	my $mba_pba = $fext->getPhysicalBioAssaySource()
+	    or next MBA;
+
+	if ( $mba_pba->getIdentifier() eq $pba_id ) {
+
+	    # Success; return the MBA
+	    return $mba;
+	}
+    }
+
+    # Not found; create a new MBA.
+    my $name = $pba->getName();
+    my $mba  = $mba_bag->(
+	$name,
+	{
+	    name                => $name,
+	    physical_bioassay   => $pba,
+	    identifier_template => $self->get_id_template() . ".$name",
+	}
+    );
+
+    # Propagate factor values.
+    $self->update_bioassay_fvs(
+	$mba,
+	$pba->getBioAssayFactorValues()
+    );
+
+    return $mba;
+}
+
+sub add_qt_mapping : PRIVATE {
+
+    my ( $self, $txn, $mage_qtd, $source_bioassays, $qtm_bag ) = @_;
+
+    $self->add_target_qts_to_mapping( $txn, $mage_qtd, $qtm_bag );
+    
+    $self->add_source_qts_to_mapping( $txn, $source_bioassays, $qtm_bag );
+
+    return;
+}
+
+sub add_target_qts_to_mapping : PRIVATE {
+
+    my ( $self, $txn, $mage_qtd, $qtm_bag ) = @_;
+
+    my $qtmapping;
+    unless ( $qtmapping = $txn->getQuantitationTypeMapping ) {
+	$qtmapping = Bio::MAGE::BioAssayData::QuantitationTypeMapping->new();
+	$txn->setQuantitationTypeMapping( $qtmapping );
+    }
+
+    my %found;
+    foreach my $qtmap ( @{ $qtmapping->getQuantitationTypeMaps() || [] } ) {
+	if ( my $oldqt = $qtmap->getTargetQuantitationType() ) {
+	    $found{ $oldqt->getIdentifier() }++;
+	}
+    }
+    foreach my $newqt ( @{ $mage_qtd->getQuantitationTypes() || [] } ) {
+	unless ( $found{ $newqt->getIdentifier() } ) {
+	    my $qtmap = $qtm_bag->(
+		$newqt->getName(),
+		{
+		    target_qt           => $newqt,
+		    identifier_template => $self->get_id_template(),
+		}
+	    );
+	    $qtmapping->addQuantitationTypeMaps( $qtmap );
+	}
+    }
+
+    return;
+}
+
+sub add_source_qts_to_mapping : PRIVATE {
+
+    my ( $self, $txn, $source_bioassays, $qtm_bag ) = @_;
+
+    my ($source_qts, $source_data) = $self->get_bioassay_qts( $source_bioassays );
+
+    $txn->setBioAssayDataSources( $source_data );
+
+    my $qtmapping;
+    unless ( $qtmapping = $txn->getQuantitationTypeMapping() ) {
+	carp("Warning: No QuantitationTypeMapping for Transformation object "
+		 . $txn->getIdentifier() );
+	return;
+    }
+
+    foreach my $qtmap ( @{ $qtmapping->getQuantitationTypeMaps() || [] } ) {
+	my %found;
+	foreach my $oldqt ( @{ $qtmap->getSourcesQuantitationType() || [] } ) {
+	    $found{ $oldqt->getIdentifier() }++;
+	}
+	foreach my $newqt ( @$source_qts ) {
+	    unless ( $found{ $newqt->getIdentifier() } ) {
+		$qtmap->addSourcesQuantitationType( $newqt );
+	    }
+	}
+    }
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/MAGETAB/IDF.pm b/lib/ArrayExpress/MAGETAB/IDF.pm
new file mode 100644
index 0000000..4b04795
--- /dev/null
+++ b/lib/ArrayExpress/MAGETAB/IDF.pm
@@ -0,0 +1,881 @@
+#!/usr/bin/env perl
+#
+# Module providing IDF parsing functions to the ArrayExpress MAGE-TAB
+# implementation.
+#
+# Tim Rayner, 2007, EMBL-EBI Microarray Informatics Team
+#
+# $Id: IDF.pm 2069 2008-06-04 14:33:52Z tfrayner $
+
+package ArrayExpress::MAGETAB::IDF;
+
+use strict;
+use warnings;
+
+use Class::Std;
+use Carp;
+use List::Util qw(first);
+use Bio::MAGE qw(:ALL);
+use Readonly;
+
+use ArrayExpress::Curator::MAGE qw(
+    unique_identifier
+    make_container
+);
+use ArrayExpress::Curator::Common qw(
+    get_filepath_from_uri
+    $RE_EMPTY_STRING
+    $RE_COMMENTED_STRING
+    $RE_SURROUNDED_BY_WHITESPACE
+);
+
+use base qw(ArrayExpress::MAGETAB::TabFile);
+
+# Accessor attributes.
+my %idf                : ATTR( :name<idf>,                :default<undef> );
+my %idf_filehandle     : ATTR( :init_arg<idf_filehandle>, :default<undef> );
+my %dispatch           : ATTR( :name<dispatch>,           :default<{}>    );
+my %sdrfs              : ATTR( :name<sdrfs>,              :default<[]>    );
+my %protocol_accession_service : ATTR( :name<protocol_accession_service>, :default<undef> );
+
+# Singleton data.
+my %experiment         : ATTR( :get<experiment>,          :default<{}> );
+
+# Grouped data.
+my %factor             : ATTR( :get<factors>,             :default<[]> );
+my %person             : ATTR( :get<people>,              :default<[]> );
+my %publication        : ATTR( :get<publications>,        :default<[]> );
+my %protocol           : ATTR( :get<protocols>,           :default<[]> );
+my %termsource         : ATTR( :get<termsources>,         :default<[]> );
+my %qualitycontrol     : ATTR( :get<qualitycontroltypes>, :default<[]> );
+my %replicate          : ATTR( :get<replicatetypes>,      :default<[]> );
+my %normalization      : ATTR( :get<normalizationtypes>,  :default<[]> );
+my %comment            : ATTR( :get<comments>,            :default<{}> );
+
+Readonly my $COMMENT_TAG => qr/\A \s* Comment \s* \[ ([^\]]+) \] \s* \z/ixms;
+
+#########################
+# Object initialization #
+#########################
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    # If we have a filename, great. If all we have is a filehandle
+    # then eol_char *must* be set because check_linebreaks can't be
+    # used. Linebreak checking must be the responsibility of the
+    # caller when filehandles are used.
+    unless (     $self->get_idf()
+	    || ( $self->get_idf_filehandle() && $self->get_eol_char() ) ) {
+	confess("Error: MAGETAB::IDF object requires either filename or (filehandle and eol_char) attributes.");
+    }
+
+    unless ( $self->get_expt_accession() ) {
+	confess("Error: MAGETAB::IDF object requires expt_accession to be set.");
+    }
+
+    # This dispatch table is used to validate IDF tags and to populate
+    # internal data structures prior to MAGE generation.
+    $self->set_dispatch({
+	qr/Investigation *Title/i
+	    => sub{ $self->add_singleton_datum(\%experiment, 'title',          @_) },
+	qr/Date *Of *Experiment/i
+	    => sub{ $self->add_singleton_datum(\%experiment, 'experimentdate', @_) },
+	qr/Public *Release *Date/i
+	    => sub{ $self->add_singleton_datum(\%experiment, 'releasedate',    @_) },
+	qr/Experiment *Description/i
+	    => sub{ $self->add_singleton_datum(\%experiment, 'description',    @_) },
+
+	qr/Experimental *Designs?/i
+	    => sub{ $self->add_singleton_data(\%experiment, 'design',     @_) },
+	qr/Experimental *Designs? *Term *Source *REF/i
+	    => sub{ $self->add_singleton_data(\%experiment, 'termsource', @_) },
+	qr/Experimental *Designs? *Term *Accession *Numbers?/i
+	    => sub{ $self->add_singleton_data(\%experiment, 'termaccession', @_) },
+
+	qr/Experimental *Factor *Names?/i
+	    => sub{ $self->add_grouped_data(\%factor, 'name',       @_) },
+	qr/Experimental *Factor *Types?/i
+	    => sub{ $self->add_grouped_data(\%factor, 'type',       @_) },
+	qr/Experimental *Factor *(Types?)? *Term *Source *REF/i
+	    => sub{ $self->add_grouped_data(\%factor, 'termsource', @_) },
+	qr/Experimental *Factor *(Types?)? *Term *Accession *Numbers?/i
+	    => sub{ $self->add_grouped_data(\%factor, 'termaccession', @_) },
+
+	qr/Person *Last *Names?/i
+	    => sub{ $self->add_grouped_data(\%person, 'lastname',    @_) },
+	qr/Person *First *Names?/i
+	    => sub{ $self->add_grouped_data(\%person, 'firstname',   @_) },
+	qr/Person *Mid *Initials?/i
+	    => sub{ $self->add_grouped_data(\%person, 'midinitials', @_) },
+	qr/Person *Emails?/i
+	    => sub{ $self->add_grouped_data(\%person, 'email',       @_) },
+	qr/Person *Phones?/i
+	    => sub{ $self->add_grouped_data(\%person, 'phone',       @_) },
+	qr/Person *Fax(es)?/i
+	    => sub{ $self->add_grouped_data(\%person, 'fax',         @_) },
+	qr/Person *Address(es)?/i
+	    => sub{ $self->add_grouped_data(\%person, 'address',     @_) },
+	qr/Person *Affiliations?/i
+	    => sub{ $self->add_grouped_data(\%person, 'affiliation', @_) },
+	qr/Person *Roles?/i
+	    => sub{ $self->add_grouped_data(\%person, 'roles',       @_) },
+	qr/Person *Roles? *Term *Source *REF/i
+	    => sub{ $self->add_grouped_data(\%person, 'termsource',  @_) },
+	qr/Person *Roles? *Term *Accession *Numbers?/i
+	    => sub{ $self->add_grouped_data(\%person, 'termaccession',  @_) },
+
+	qr/Quality *Control *Types?/i
+	    => sub{ $self->add_grouped_data(\%qualitycontrol, 'type',       @_) },
+	qr/Quality *Control *(Types?)? *Term *Source *REF/i
+	    => sub{ $self->add_grouped_data(\%qualitycontrol, 'termsource', @_) },
+	qr/Quality *Control *(Types?)? *Term *Accession *Numbers?/i
+	    => sub{ $self->add_grouped_data(\%qualitycontrol, 'termaccession', @_) },
+	qr/Replicate *Types?/i
+	    => sub{ $self->add_grouped_data(\%replicate,      'type',       @_) },
+	qr/Replicate *(Types?)? *Term *Source *REF/i
+	    => sub{ $self->add_grouped_data(\%replicate,      'termsource', @_) },
+	qr/Replicate *(Types?)? *Term *Accession *Numbers?/i
+	    => sub{ $self->add_grouped_data(\%replicate,      'termaccession', @_) },
+	qr/Normali[sz]ation *Types?/i
+	    => sub{ $self->add_grouped_data(\%normalization,  'type',       @_) },
+	qr/Normali[sz]ation *(Types?)? *Term *Source *REF/i
+	    => sub{ $self->add_grouped_data(\%normalization,  'termsource', @_) },
+	qr/Normali[sz]ation *(Types?)? *Term *Accession *Numbers?/i
+	    => sub{ $self->add_grouped_data(\%normalization,  'termaccession', @_) },
+ 
+	qr/PubMed *IDs?/i
+	    => sub{ $self->add_grouped_data(\%publication, 'pubmedid',   @_) },
+	qr/Publication *DOIs?/i
+	    => sub{ $self->add_grouped_data(\%publication, 'doi',        @_) },
+	qr/Publication *Authors? *Lists?/i
+	    => sub{ $self->add_grouped_data(\%publication, 'authorlist', @_) },
+	qr/Publication *Titles?/i
+	    => sub{ $self->add_grouped_data(\%publication, 'title',      @_) },
+	qr/Publication *Status/i
+	    => sub{ $self->add_grouped_data(\%publication, 'status',     @_) },
+	qr/Publication *Status *Term *Source *REF/i
+	    => sub{ $self->add_grouped_data(\%publication, 'termsource', @_) },
+	qr/Publication *Status *Term *Accession *Numbers?/i
+	    => sub{ $self->add_grouped_data(\%publication, 'termaccession', @_) },
+
+	qr/Protocol *Names?/i
+	    => sub{ $self->add_grouped_data(\%protocol, 'name',        @_) },
+	qr/Protocol *Types?/i
+	    => sub{ $self->add_grouped_data(\%protocol, 'type',        @_) },
+	qr/Protocol *Descriptions?/i
+	    => sub{ $self->add_grouped_data(\%protocol, 'description', @_) },
+	qr/Protocol *Parameters?/i
+	    => sub{ $self->add_grouped_data(\%protocol, 'parameters',  @_) },
+	qr/Protocol *Hardwares?/i
+	    => sub{ $self->add_grouped_data(\%protocol, 'hardware',    @_) },
+	qr/Protocol *Softwares?/i
+	    => sub{ $self->add_grouped_data(\%protocol, 'software',    @_) },
+	qr/Protocol *Contacts?/i
+	    => sub{ $self->add_grouped_data(\%protocol, 'contact',     @_) },
+	qr/Protocol *(Types?)? *Term *Source *REF/i
+	    => sub{ $self->add_grouped_data(\%protocol, 'termsource',  @_) },
+	qr/Protocol *(Types?)? *Term *Accession *Numbers?/i
+	    => sub{ $self->add_grouped_data(\%protocol, 'termaccession',  @_) },
+
+	qr/Term *Source *Names?/i
+	    => sub{ $self->add_grouped_data(\%termsource, 'name',     @_) },
+	qr/Term *Source *Files?/i
+	    => sub{ $self->add_grouped_data(\%termsource, 'file',     @_) },
+	qr/Term *Source *Versions?/i
+	    => sub{ $self->add_grouped_data(\%termsource, 'version',  @_) },
+
+	qr/SDRF *Files?/i
+	    => sub{ $self->add_sdrfs( @_) },
+    });
+
+    # ArrayExpress-specific tweak; AE wants these NVTs added
+    # regardless of whether they're specified or not. This is a
+    # departure from the MAGE-TAB specification, but can be easily
+    # deactivated by deleting the following two lines:
+    $self->add_comment('AEExperimentDisplayName', q{});
+    $self->add_comment('SecondaryAccession',      q{});
+
+    return;
+}
+
+##################
+# Public methods #
+##################
+
+sub parse {
+
+    my ( $self ) = @_;
+
+    # Parse the IDF file into memory here.
+    my $array_of_rows = $self->read_as_arrayref();
+
+    # Check tags for duplicates, make sure that tags are recognized.
+    my $idf_hash = $self->validate_arrayref_tags($array_of_rows);
+
+    # Populate the IDF object's internal data structures.
+    while ( my ($tag, $values) = each %$idf_hash ) {
+	$self->dispatch($tag, @$values);
+    }
+
+    # FIXME store this somewhere?
+    my ( $mage, $experiment ) = $self->generate_mage();
+
+    return ( $mage, $experiment, $self->get_sdrfs() );
+}
+
+###################
+# Private methods #
+###################
+
+sub generate_mage : PRIVATE {
+
+    my ( $self ) = @_;
+
+    # Databases.
+    $self->create_termsources();
+
+    # Protocols.
+    foreach my $protocol_data ( @{ $self->get_protocols() } ) {
+	$self->create_protocol($protocol_data);
+    }
+
+    my $experiment = $self->create_experiment();
+
+    # MAGE top-level object.
+    my $mage = Bio::MAGE->new();
+
+    $mage->add_objects( [ $experiment ] );
+
+    foreach my $obj_type ( qw(termsource protocol people) ) {
+	my $method = "${obj_type}_bag";
+	$mage->add_objects( $self->$method() );
+    }
+
+    return ( $mage, $experiment );
+}
+
+sub create_termsources : PRIVATE {
+
+    my ( $self ) = @_;
+
+    my @termsources;
+    TS_DATA:
+    foreach my $ts_data ( @{ $self->get_termsources() } ) {
+
+	# FIXME the Database authority:namespace tag hard-coded at the
+	# moment.
+	unless( defined( $ts_data->{name} ) ) {
+	    if ( scalar( grep { defined $ts_data->{$_} } qw(version file) ) ) {
+	        $self->raise_error("Error: Found a Term Source without a Name.\n");
+	    }
+	    else {
+	        next TS_DATA;
+	    }
+	}
+	my $identifier =
+	    sprintf("%s:%s",
+		    'ebi.ac.uk:Database',
+		    $ts_data->{name});
+	my $termsource = $self->termsource_bag(
+	    $ts_data->{name},
+	    {
+		identifier => $identifier,
+		name       => $ts_data->{name},
+		version    => $ts_data->{version},
+		uri        => $ts_data->{file},
+	    }
+	);
+	push @termsources, $termsource;
+    }
+
+    return \@termsources;
+}
+
+sub create_experiment : PRIVATE {
+
+    my ( $self ) = @_;
+
+    my $factors      = $self->create_factors();
+    my $people       = $self->create_people();
+    my $publications = $self->create_publications();
+    my $design       = $self->create_design($factors);
+
+    my $description = Bio::MAGE::Description::Description->new(
+	text                    => $self->get_experiment()->{description},
+	bibliographicReferences => $publications,
+    );
+
+    my $comments = $self->create_comments();
+    my $dates    = $self->create_dates();
+
+    my $experiment = Bio::MAGE::Experiment::Experiment->new(
+	name              => $self->get_experiment()->{title},
+	identifier        => $self->get_expt_accession(),
+	experimentDesigns => [$design],
+	providers         => $people,
+	descriptions      => [$description],
+	propertySets      => [@{ $comments }, @{ $dates }],
+    );
+
+    return $experiment;
+}
+
+sub create_comments : PRIVATE {
+
+    my ( $self ) = @_;
+
+    my @comments;
+    while (my ($name, $value) = each %{ $self->get_comments() } ) {
+	my $comment = Bio::MAGE::NameValueType->new(
+	    name  => $name,
+	    value => $value, 
+	);
+	push @comments, $comment;
+    }
+
+    return \@comments;
+}
+
+sub create_dates : PRIVATE {
+
+    my ( $self ) = @_;
+
+    # Validate the dates here FIXME.
+    # NVT names are hard-coded for now FIXME.
+    my @dates;
+    if ( my $date = $self->get_experiment()->{releasedate} ) {
+	push @dates, Bio::MAGE::NameValueType->new(
+	    name  => 'ArrayExpressReleaseDate',
+	    value => $date,
+	);
+    }
+    if ( my $date = $self->get_experiment()->{experimentdate} ) {
+	push @dates, Bio::MAGE::NameValueType->new(
+	    name  => 'ExperimentDate',
+	    value => $date,
+	);
+    }
+
+    return \@dates;
+}
+    
+sub create_design : PRIVATE {
+
+    my ( $self, $factors ) = @_;
+
+    my @types;
+    foreach my $value ( @{ $self->get_experiment()->{design} } ) {
+	my $termsource = shift ( @{ $self->get_experiment()->{termsource} } );
+	my $termaccno  = shift ( @{ $self->get_experiment()->{termaccession} } );
+	my $oe = $self->create_ontologyentry(
+	    'ExperimentDesignType',
+	    $value,
+	    $termsource,
+	    $termaccno,
+	);
+	push @types, $oe;
+    }
+
+    # Quality Control, Normalization and Replicate Types.
+    my $qc_description = $self->create_exptdescriptiontype(
+	'QualityControlDescriptionType',
+	$self->get_qualitycontroltypes(),
+    );
+    my $norm_description = $self->create_exptdescriptiontype(
+	'NormalizationDescriptionType',
+	$self->get_normalizationtypes(),
+    );
+    my $repl_description = $self->create_exptdescriptiontype(
+	'ReplicateDescriptionType',
+	$self->get_replicatetypes(),
+    );
+
+    my $design = Bio::MAGE::Experiment::ExperimentDesign->new(
+	types                     => \@types,
+	experimentalFactors       => $factors,
+    );
+
+    $design->setQualityControlDescription( $qc_description )
+	if $qc_description;
+    $design->setNormalizationDescription( $norm_description )
+	if $norm_description;
+    $design->setReplicateDescription( $repl_description )
+	if $repl_description;
+
+    return $design;
+}
+
+sub create_exptdescriptiontype : PRIVATE {
+
+    # Used to generate OE-containing Descriptions for QC,
+    # normalization and replicate types to attach to ExperimentDesign.
+    my ( $self, $category, $typelist ) = @_;
+
+    my @types;
+    foreach my $type_data ( @{ $typelist } ) {
+	my $type = $self->create_ontologyentry(
+	    $category,
+	    $type_data->{type},
+	    $type_data->{termsource},
+	    $type_data->{termaccession},
+	);
+	push @types, $type;
+    }
+
+    my $description;
+    if ( scalar @types ) {
+	$description = Bio::MAGE::Description::Description->new(
+	    annotations => \@types,
+	);
+    }
+
+    return $description;
+}
+    
+sub create_factors : PRIVATE {
+
+    my ( $self ) = @_;
+
+    my @factors;
+
+    FACTOR_DATA:
+    foreach my $factor_data ( @{ $self->get_factors() } ) {
+
+	unless( defined( $factor_data->{name} ) ) {
+	    if ( scalar( grep { defined $factor_data->{$_} }
+			     qw(type termsource termaccession) ) ) {
+	        $self->raise_error("Error: Found an Experimental Factor without a Name.\n");
+	    }
+	    else {
+	        next FACTOR_DATA;
+	    }
+	}
+	my $factor = $self->factor_bag(
+	    $factor_data->{name}, $factor_data
+	);
+	push @factors, $factor;
+    }
+
+    return \@factors;
+}
+
+sub create_people : PRIVATE {
+
+    my ( $self ) = @_;
+
+    my @people;
+    foreach my $p_data ( @{ $self->get_people() } ) {
+
+	my @nameparts;
+	foreach my $field qw(firstname midinitials lastname) {
+	    push @nameparts, $p_data->{$field} if $p_data->{$field};
+	}
+	unless( scalar @nameparts ) {
+	    croak(
+		"Error: Found a Person without First Name, Last Name or Mid Initials.\n"
+	    );
+	}
+	$p_data->{identifier} = sprintf("%s:%s.Person",
+				$self->get_id_prefix(),
+				join(q{ }, @nameparts));
+
+	my $person = $self->people_bag(
+	    $p_data->{identifier}, $p_data
+	);
+
+	push @people, $person;
+    }
+
+    return \@people;
+}
+
+sub create_publications : PRIVATE {
+
+    my ( $self ) = @_;
+
+    # FIXME PubMed database is hard-coded for the moment.
+    my $pubmed_db = Bio::MAGE::Description::Database->new(
+	name       => 'Entrez PubMed',
+	identifier => 'ebi.ac.uk:Database:pubmed',
+	URI        => 'http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=PubMed',
+    );
+
+    my @publications;
+    foreach my $article_data ( @{ $self->get_publications() } ) {
+
+	my (@parameters, @nvts, @pubmed_accns);
+
+	# FIXME MAGE-TAB v1.0 does not capture publication type
+	# (e.g. journal_article). Deferred until a later release.
+
+	if ( $article_data->{pubmedid} ) {
+	    push @pubmed_accns, Bio::MAGE::Description::DatabaseEntry->new(
+		accession => $article_data->{pubmedid},
+		database  => $pubmed_db,
+	    );
+	}
+
+        # Nowhere to specify this in MAGE-TAB, but MAGE requires at
+        # least one parameter so we add this.
+        my $pubtype = $self->create_ontologyentry(
+            'PublicationType',
+            'journal_article',
+        );
+        push @parameters, $pubtype;
+
+	# FIXME "PublicationStatus" not in MO yet.
+	if ( $article_data->{status} ) {
+	    my $status = $self->create_ontologyentry(
+		'PublicationStatus',
+		$article_data->{status},
+		$article_data->{termsource},
+		$article_data->{termaccession},
+	    );
+	    push @parameters, $status;
+	}
+
+	if ( $article_data->{doi} ) { 
+	    my $doi = Bio::MAGE::NameValueType->new(
+		name  => 'DOI',
+		value => $article_data->{doi},
+	    );
+	    push @nvts, $doi;
+	}
+
+	my $publication = Bio::MAGE::BQS::BibliographicReference->new(
+	    authors      => $article_data->{authorlist},
+	    title        => $article_data->{title},
+	    accessions   => \@pubmed_accns,
+	    parameters   => \@parameters,
+	    propertySets => \@nvts,
+	);
+
+	push @publications, $publication;
+    }
+
+    return \@publications;
+}
+
+sub create_protocol : PRIVATE {
+
+    my ( $self, $protocol_data ) = @_;
+
+    unless( defined( $protocol_data->{name} ) ) {
+        if ( scalar( grep { defined $protocol_data->{$_} }
+		     qw(type
+			description
+			parameters
+			hardware
+			software
+			contact
+			termsource
+			termaccession) ) ) {
+	    $self->raise_error("Error: Found a Protocol without a Name.\n");
+	}
+	else {
+	    return;
+	}
+    }
+
+    # First, give the protocol_accession_service callback the
+    # opportunity to reassign protocol accession.
+    $protocol_data->{accession} = $self->get_protocol_accession(
+	$protocol_data->{name},
+    );
+
+    # Then generate the protocol. This also handles parameters,
+    # software and hardware.
+    my $protocol = $self->protocol_bag(
+	$protocol_data->{name}, $protocol_data
+    );
+
+    return $protocol;
+}
+    
+sub get_protocol_accession : PRIVATE {
+
+    my ( $self, $accession ) = @_;
+
+    if ( my $service = $self->get_protocol_accession_service() ) {
+	$accession = $service->(
+	    $accession,
+	    $self->get_expt_accession(),
+	);
+    }
+    
+    return $accession;
+}
+
+sub read_as_arrayref : RESTRICTED {
+
+    # Method to parse the IDF object file into an array of
+    # arrayrefs. This method uses Text::CSV_XS to parse tab-delimited
+    # text.
+
+    my ( $self ) = @_;
+
+    # First, determine the file linebreak type and generate a CSV
+    # parser object.
+    my $csv_parser = $self->get_csv_parser();
+
+    # This is still required for Text::CSV_XS.
+    local $/ = $self->calculate_eol_char();
+
+    # Open the file
+    my $fh = $self->get_idf_filehandle();
+
+    my (@rows, $larry);
+
+    FILE_LINE:
+    while ( $larry = $csv_parser->getline($fh) ) {
+
+        # Skip empty lines.
+        my $line = join( q{}, @$larry );
+        next FILE_LINE if ( $line =~ $RE_EMPTY_STRING );
+
+        # Allow hash comments (FIXME NOT IN SPEC).
+        next FILE_LINE if ( $line =~ $RE_COMMENTED_STRING );
+
+	# Strip surrounding whitespace from each element.
+	foreach my $element ( @$larry ) {
+	    $element =~ s/$RE_SURROUNDED_BY_WHITESPACE/$1/xms;
+	}
+
+	# Strip off empty trailing values.
+	my $end_value;
+	until ( defined($end_value) && $end_value !~ /\A \s* \z/xms ) {
+	    $end_value = pop(@$larry);
+	}
+	push @$larry, $end_value;
+
+	# Reset empty strings to undefs.
+	foreach my $value ( @$larry ) {
+	    undef($value) if ( defined($value) && $value eq q{} );
+	}
+
+	push @rows, $larry;
+    }
+
+    # Check we've parsed to the end of the file.
+    my ( $error, $mess ) = $csv_parser->error_diag();
+    unless ( $error == 2012 ) {    # 2012 is the Text::CSV_XS EOF code.
+	croak(
+	    sprintf(
+		"Error in tab-delimited format: %s. Bad input was:\n\n%s\n",
+		$mess,
+		$csv_parser->error_input(),
+	    ),
+	);
+    }
+
+    return \@rows;
+}
+
+sub normalize_tag : PRIVATE {
+
+    # Takes a string, returns the lowercase, whitespace-stripped
+    # version.
+    my ( $self, $tag ) = @_;
+
+    $tag =~ s/\s+//g;
+    $tag = lc($tag);
+
+    return $tag
+}
+
+sub validate_arrayref_tags : RESTRICTED {
+
+    # Method to check the return value from read_as_arrayref to check
+    # for (a) duplicate tags, and (b) unrecognised tags. Returns a
+    # hash with keys corresponding to IDF tags and value arrayrefs
+    # containing the IDF annotation.
+
+    my ($self, $array_of_rows) = @_;
+
+    # Duplicate tag check. This is somewhat primitive at the moment,
+    # and can be fooled FIXME.
+    my (%seen);
+    foreach my $row ( @$array_of_rows ) {
+
+	# Two-dimensional hash; normalized then actual IDF tags.
+	my $normtag = $self->normalize_tag( $row->[0] );
+	$seen{ $normtag }{ $row->[0] } ++;
+    }
+    while ( my ($norm_tag, $idf_tags) = each %seen ) {
+
+	# Differently typed but identical tags.
+	if ( scalar (grep { defined $_ } values %$idf_tags ) > 1 ) {
+	    my $tagstring = join(", ", keys %$idf_tags);
+	    $self->raise_error(qq{Error: duplicated IDF tag(s): "$tagstring"});
+	}
+
+	# Identically typed duplicate tags.
+	while ( my ($idf_tag, $count) = each %$idf_tags ) {
+	    if ( $count > 1 ) {
+		$self->raise_error(qq{Error: duplicated IDF tag: "$idf_tag"});
+	    }
+	}
+    }
+
+    # Hash of row tag keys with rest-of-row arrayref values.
+    my %idf_hash = map
+	{ $_->[0] => [ @{ $_ }[1 .. $#$_] ] }
+	    @{ $array_of_rows };
+
+    # A list of acceptable tags, expressed as qr//
+    my @acceptable = keys %{ $self->get_dispatch() };
+    while ( my ( $tag, $values ) = each %idf_hash ) {
+
+	# N.B. acceptable tag REs may contain whitespace; no x option
+	# here.
+	next if $tag =~ /\A\s*$COMMENT_TAG\s*\z/ms;
+
+	# Check for recognised tags here.
+	unless ( first { $tag =~ /\A\s*$_\s*\z/ms } @acceptable ) {
+	    $self->raise_error(qq{Error: unrecognized IDF tag(s): "$tag"});
+	}
+
+	# Empty Name tags are invalid and will cause fatal crashes
+	# later; we check for them here.
+	if ( $tag =~ m/name \s* \z/ixms ) {
+	    foreach my $value ( @$values ) {
+		warn(
+		    qq{Warning: IDF Name attribute "$tag" is empty.\n}
+		) unless $value;
+	    }
+	}
+    }
+
+    return \%idf_hash;
+}
+
+sub add_grouped_data : PRIVATE {
+
+    # Create an ordered set of data groups indexed by $i.
+    my ( $self, $group, $tag, @args ) = @_;
+
+    for ( my $i = 0; $i <= $#args; $i++ ) {
+	$group->{ident $self}[$i]{$tag} = $args[$i];
+    }
+
+    return;
+}
+
+sub add_singleton_data : PRIVATE {
+
+    # Record a 1:n object:args relationship.
+    my ( $self, $object, $tag, @args ) = @_;
+
+    # Make a copy of @args, just in case.
+    $object->{ident $self}{$tag} = [ @args ];
+
+    return;
+}
+
+sub add_singleton_datum : PRIVATE {
+
+    # Record a 1:1 object:arg relationship.
+    my ( $self, $object, $tag, $arg ) = @_;
+
+    $object->{ident $self}{$tag} = $arg;
+
+    return;
+}
+
+sub add_comment : PRIVATE {
+
+    # Comments are currently processed at the level of experiment
+    # only.
+    my ( $self, $name, $value ) = @_;
+
+    $comment{ident $self}{$name} = $value;
+
+    return;
+}
+    
+sub add_sdrfs : PRIVATE {
+
+    # Store the list of SDRF files.
+    my ( $self, @sdrf_uri_strings ) = @_;
+
+    my @sdrfs = map { get_filepath_from_uri(
+	$_, $self->get_source_directory(),
+    ) } @sdrf_uri_strings;
+
+    $self->set_sdrfs(\@sdrfs);
+
+    return;
+}
+
+sub retrieve_sub : RESTRICTED {
+
+    my ( $self, $tag ) = @_;
+
+    my $rc;
+
+    while ( my ( $key, $sub ) = each %{ $self->get_dispatch() } ) {
+
+	# $key may contain whitespace, no x option here.
+	if ( $tag =~ /\A\s*$key\s*\z/ms ) {
+	    $rc = $sub;
+	}
+	
+	# Have to loop through the rest of the list to reset while()
+	# on the hash.
+    }
+
+    return $rc;
+}
+
+sub dispatch : PRIVATE {
+
+    my ( $self, $tag, @args ) = @_;
+
+    unless (defined $tag) {
+	confess("Error: get_dispatch needs a defined tag name.");
+    }
+
+    my $sub = $self->retrieve_sub( $tag );
+    unless (defined $sub && ref $sub eq 'CODE') {
+	if ( my ( $commentname ) = ( $tag =~ /\A\s*$COMMENT_TAG\s*\z/ms ) ) {
+	    $self->add_comment($commentname, @args);
+	}
+	else {
+
+	    # This should have been caught in validate_arrayref_tags
+	    croak(qq{Error: Cannot parse the IDF tag: "$tag".});
+	}
+    }
+
+    return $sub ? $sub->(@args) : undef;
+}
+
+sub get_filename : RESTRICTED {
+
+    # Method to pass the IDF filename to check_linebreaks, called from
+    # SUPER->calculate_eol_char()
+    my ( $self ) = @_;
+    
+    return $self->get_idf();
+}
+    
+sub get_idf_filehandle : RESTRICTED {
+
+    my ( $self ) = @_;
+
+    unless ( $idf_filehandle{ident $self} ) {
+	if ( my $file = $self->get_idf() ) {
+	    open (my $fh, '<', $file)
+		or croak("Error opening IDF for reading: $!\n");
+	    $idf_filehandle{ident $self} = $fh;
+	}
+	else {
+	    confess("Error: No IDF filename given.");
+	}
+    }
+    return $idf_filehandle{ident $self};
+}
+
+1;
diff --git a/lib/ArrayExpress/MAGETAB/SDRF.pm b/lib/ArrayExpress/MAGETAB/SDRF.pm
new file mode 100644
index 0000000..512aaed
--- /dev/null
+++ b/lib/ArrayExpress/MAGETAB/SDRF.pm
@@ -0,0 +1,3486 @@
+#!/usr/bin/env perl
+#
+# Module providing SDRF parsing functions to the ArrayExpress MAGE-TAB
+# implementation.
+#
+# Tim Rayner, 2007, EMBL-EBI Microarray Informatics Team
+#
+# $Id: SDRF.pm 2069 2008-06-04 14:33:52Z tfrayner $
+
+package ArrayExpress::MAGETAB::SDRF;
+
+use strict;
+use warnings;
+
+use Class::Std;
+use Carp;
+use English qw( -no_match_vars );
+use Parse::RecDescent;
+use Storable qw(dclone);
+use List::Util qw(first);
+use Readonly;
+use File::Basename;
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(
+    get_filepath_from_uri
+    $RE_EMPTY_STRING
+    $RE_COMMENTED_STRING
+    $RE_SURROUNDED_BY_WHITESPACE
+);
+use ArrayExpress::Curator::MAGE qw(unique_identifier);
+use ArrayExpress::Curator::MAGE::Definitions qw(
+    $AE_LABELCOMPOUND_PREFIX
+    $AE_CHANNEL_PREFIX
+);
+use ArrayExpress::Datafile;
+
+use base qw(ArrayExpress::MAGETAB::TabFile);
+
+Readonly my $BLANK => qr/\A [ ]* \z/xms;
+
+# The Parse::RecDescent grammar is stored in the __DATA__ section, below.
+Readonly my $GRAMMAR => join("\n", <DATA> );
+
+my %sdrf              : ATTR( :name<sdrf>,                :default<undef> );
+my %sdrf_filehandle   : ATTR( :init_arg<sdrf_filehandle>, :default<undef> );
+my %datafiles_cache   : ATTR( :default<{}> );
+my %row_parser        : ATTR( :default<undef> );
+my %skip_datafiles    : ATTR( :name<skip_datafiles>,      :default<undef> );
+my %native_filetypes  : ATTR( :get<native_filetypes>,     :default<{}>    );
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    # If we have a filename, great. If all we have is a filehandle
+    # then eol_char *must* be set because check_linebreaks can't be
+    # used. Linebreak checking must be the responsibility of the
+    # caller when filehandles are used.
+    unless (     $self->get_sdrf()
+	    || ( $self->get_sdrf_filehandle() && $self->get_eol_char() ) ) {
+	croak("Error: MAGETAB::SDRF object requires either filename or (filehandle and eol_char) attributes.");
+    }
+
+    return;
+}
+
+sub parse {
+
+    my ( $self ) = @_;
+
+    # This has to be set for Text::CSV_XS.
+    local $/ = $self->calculate_eol_char();
+
+    my $row_parser = $self->parse_header();
+    my $sdrf_fh    = $self->get_sdrf_filehandle();
+    my $csv_parser = $self->get_csv_parser();
+
+    my $larry;
+
+    # Run through the rest of the file with the row-level parser.
+    FILE_LINE:
+    while ( $larry = $csv_parser->getline($sdrf_fh) ) {
+    
+        # Skip empty lines.
+        my $line = join( q{}, @$larry );
+        next FILE_LINE if ( $line =~ $RE_EMPTY_STRING );
+
+        # Allow hash comments (FIXME NOT IN SPEC).
+        next FILE_LINE if ( $line =~ $RE_COMMENTED_STRING );
+
+	# Strip surrounding whitespace from each element.
+	foreach my $element ( @$larry ) {
+	    $element =~ s/$RE_SURROUNDED_BY_WHITESPACE/$1/xms;
+	}
+
+	# FIXME some error handling wouldn't go amiss here.
+	$row_parser->(@$larry);
+    }
+
+    # Check we've parsed to the end of the file.
+    my ( $error, $mess ) = $csv_parser->error_diag();
+    unless ( $error == 2012 ) {    # 2012 is the Text::CSV_XS EOF code.
+	croak(
+	    sprintf(
+		"Error in tab-delimited format: %s. Bad input was:\n\n%s\n",
+		$mess,
+		$csv_parser->error_input(),
+	    ),
+	);
+    }
+
+    $self->simplify_single_channels();
+
+    $self->propagate_fvs_to_datafiles();
+
+    return $self->get_datafiles();
+}
+
+sub parse_header {
+
+    # Generates a row-level parser function based on the first line in the SDRF.
+
+    # $::sdrf is a qualified $self, used below in the Parse::RecDescent grammar.
+    ( $::sdrf ) = @_;
+
+    # Globals to record the previous material and event (bioassay) in
+    # the chain.
+    our ($previous_material,
+	 $previous_event,
+	 $previous_data,
+	 $array_accession,
+	 $channel);
+
+    # FIXME add support for REF:namespaces (currently accepted, but
+    # discarded for termsource).
+
+    # Check linebreaks; get first line as $header_string and generate
+    # row-level parser.
+    my $csv_parser = $::sdrf->get_csv_parser();
+    my $sdrf_fh    = $::sdrf->get_sdrf_filehandle();
+
+    # Get the header line - the first non-empty, non-comment line in the file.
+    my ( $header_string, $harry );
+    HEADERLINE:
+    while ( $harry = $csv_parser->getline($sdrf_fh) ) {
+
+	$header_string = join( qq{\x{0}}, @$harry );
+
+	# Skip empty and commented lines.
+        if (   $header_string
+	    && $header_string !~ $RE_EMPTY_STRING
+	    && $header_string !~ $RE_COMMENTED_STRING ) {
+
+	    # We've found the header line. Add a starting skip
+	    # character for the benefit of the parser.
+	    $header_string = qq{\x{0}} . $header_string;
+	    last HEADERLINE;
+	}
+    }
+
+    # Check we have no CSV parse errors.
+    my ( $error, $mess ) = $csv_parser->error_diag();
+    unless ( $harry || $error == 2012 ) {    # 2012 is the Text::CSV_XS EOF code.
+	croak(
+	    sprintf(
+		"Error in tab-delimited format: %s. Bad input was:\n\n%s\n",
+		$mess,
+		$csv_parser->error_input(),
+	    ),
+	);
+    }
+
+    # N.B. MAGE-TAB 1.1 SDRFs can actually be empty, so an empty
+    # $header_string is valid at this point.
+
+    # FIXME This next line is an egregious hack; it reopens an
+    # (*undocumented*) filehandle in the Parse::RecDescent module to a
+    # local variable so we can capture STDERR. I tried doing this the
+    # correct way (open(local *STDERR, '>', \$parse_error)) but
+    # couldn't get it to work properly. We'll want to revisit this at
+    # some point.
+    open( *Parse::RecDescent::ERROR, '>', \(my $parse_error) )
+	or croak("Error: unable to redirect STDERR for MAGE-TAB header parsing.");
+
+    # Set the token separator character.
+    $Parse::RecDescent::skip = ' *\x{0} *';
+
+    # FIXME most of these are removable once development is advanced.
+    $::RD_ERRORS++;       # unless undefined, report fatal errors
+    $::RD_WARN++;         # unless undefined, also report non-fatal problems
+    $::RD_HINT++;         # if defined, also suggestion remedies
+
+    # Generate the grammar parser first.
+    my $parser = Parse::RecDescent->new($GRAMMAR) or die("Bad grammar!");
+
+    # The parser should return a function which can process each SDRF
+    # row as an array (row-level parser).
+    my $row_parser = $parser->header($header_string);
+
+    # Typically the grammar will generate some error messages before
+    # we get here. N.B. $! doesn't really give a useful error here so
+    # we don't use it.
+    unless (defined $row_parser) {
+	die(
+	    qq{\nERROR parsing header line:\n} . $parse_error
+	);
+    }
+
+    return $row_parser;
+}
+
+sub generate_id_template {
+
+    my ( $self, $name ) = @_;
+
+    # FIXME consider unique_identifier here.
+    my $identifier_template = sprintf(
+	"%s:%s.%s",
+	$self->get_id_prefix(),
+	$self->get_expt_accession(),
+	$name,
+    );    
+
+    return $identifier_template;
+}
+
+sub create_source {
+
+    my ( $self, $name ) = @_;
+
+    return if ( $name =~ $BLANK );
+
+    my $identifier_template = $self->generate_id_template( $name );
+
+    my $source = $self->source_bag(
+	$name,
+	{
+	    name                => $name,
+	    identifier_template => $identifier_template,
+	    material_type       => 'unknown',
+	},
+    );
+
+    return $source;
+}
+
+sub create_sample {
+
+    my ( $self, $name, $previous, $protocolapps ) = @_;
+
+    return if ( $name =~ $BLANK );
+
+    my $identifier_template = $self->generate_id_template( $name );
+
+    my $sample = $self->sample_bag(
+	$name,
+	{
+	    name                => $name,
+	    identifier_template => $identifier_template,
+	    material_type       => 'unknown',
+	},
+    );
+
+    if ( ( $protocolapps && scalar @{ $protocolapps } ) || $previous ) {
+	my $treatments = $self->create_material_treatments(
+	    $protocolapps,
+	    $previous,
+	    'specified_biomaterial_action',
+	);
+	$self->add_treatments_to_material( $treatments, $sample );
+    }
+
+    return $sample;
+}
+
+sub create_extract {
+
+    my ( $self, $name, $previous, $protocolapps ) = @_;
+
+    return if ( $name =~ $BLANK );
+
+    my $identifier_template = $self->generate_id_template( $name );
+
+    my $extract = $self->extract_bag(
+	$name,
+	{
+	    name                => $name,
+	    identifier_template => $identifier_template,
+	    material_type       => 'unknown',
+	},
+    );
+
+    if ( ( $protocolapps && scalar @{ $protocolapps } ) || $previous ) {
+	my $treatments = $self->create_material_treatments(
+	    $protocolapps,
+	    $previous,
+	    'nucleic_acid_extraction',
+	);
+	$self->add_treatments_to_material( $treatments, $extract );
+    }
+
+    return $extract;
+}
+
+sub create_labeled_extract {
+
+    my ( $self, $name, $previous, $protocolapps ) = @_;
+
+    return if ( $name =~ $BLANK );
+
+    my $identifier_template = $self->generate_id_template( $name );
+
+    my $le = $self->labeledextract_bag(
+	$name,
+	{
+	    name                => $name,
+	    identifier_template => $identifier_template,
+	    material_type       => 'unknown',
+	},
+    );
+
+    if ( ( $protocolapps && scalar @{ $protocolapps } ) || $previous ) {
+	my $treatments = $self->create_material_treatments(
+		$protocolapps,
+		$previous,
+		'labeling',
+	    );
+	$self->add_treatments_to_material( $treatments, $le );
+    }
+
+    return $le;
+}
+
+sub create_label {
+
+    my ( $self, $dyename, $termsource, $accession, $le ) = @_;
+
+    return if ( $dyename =~ $BLANK );
+
+    my $oe;
+    if ( $termsource ) {
+	$oe = $self->create_ontologyentry(
+	    'LabelCompound',
+	    $dyename,
+	    $termsource,
+	    $accession,
+	);
+    }
+
+    my $label = $self->label_bag(
+	$dyename,
+	{
+	    dye     => $dyename,
+	    indices => $oe ? [ $oe ] : undef,
+	},
+    );
+
+    $self->add_label_to_le( $label, $le ) if $le;
+
+    return $label;
+}
+
+sub create_protocolapplication {
+
+    my ( $self, $name, $namespace, $termsource, $accession ) = @_;
+
+    # FIXME do something more with Term Source, accession?
+
+    return if ( $name =~ $BLANK );
+
+    $name = ( $namespace || q{} ) . $name;
+
+    my $args;
+    if ( $self->get_in_relaxed_mode() ) {
+	$args = { accession => $name };
+    }
+
+    my $protocol = $self->protocol_bag(	$name, $args );
+
+    # Protocol not found;
+    unless ( $protocol ) {
+
+	# If we have a valid namespace or termsource, let it through.
+	if ( $namespace ) {
+	    $protocol = $self->protocol_bag(
+		$name, { accession => $name },
+	    )
+	}
+	elsif ( $termsource ) {
+
+	    my $args;
+	    if ( $self->get_in_relaxed_mode() ) {
+		$args = {name       => $termsource,
+			 identifier => "ebi.ac.uk:Database:$termsource"};
+	    }
+
+	    if ( $namespace || $self->termsource_bag( $termsource, $args ) ) {
+		$protocol = $self->protocol_bag(
+		    $name, { accession => $accession || $name }, # FIXME not sure about $accession here.
+		)
+	    }
+	    else {
+		croak(qq{Error: Protocol Name "$name" not found,}
+		    . qq{ and Term Source Name "$termsource" is invalid.\n});
+	    }
+	}
+	else {
+	    croak("Error: Protocol Name not found: $name\n");
+	};
+    }
+
+    # FIXME add Hardware, SoftwareApplications, since we have the
+    # protocol.
+    my $protocol_app
+	= Bio::MAGE::Protocol::ProtocolApplication->new(
+            protocol     => $protocol,
+            activityDate => 'n/a',
+	);
+
+    return $protocol_app;
+}
+
+sub create_parametervalue {
+
+    my ( $self, $paramname, $value, $protocolapp ) = @_;
+
+    return if ( $value =~ $BLANK );
+
+    my $protocol = $protocolapp->getProtocol();
+    my $protname = $protocol->getName() || q{};
+
+    # Unless running in "relaxed" mode, this requires that all
+    # parameters be declared in the IDF FIXME.
+    my $args;
+    if ( $self->get_in_relaxed_mode() ) {
+	$args = { identifier => $paramname };
+    }
+    my $parameter = $self->parameter_bag( "$paramname.$protname", $args );
+
+    unless ( $parameter ) {
+	croak(
+     qq{Error: Parameter Name "$paramname" not found for protocol "$protname"\n}
+        );
+    }
+
+    my $parameterval
+	= Bio::MAGE::Protocol::ParameterValue->new(
+	    parameterType => $parameter,
+	    value         => $value,
+	);
+
+    $self->add_parameterval_to_protocolapp(
+	$parameterval,
+	$protocolapp,
+    ) if $protocolapp;
+
+    return $parameterval;
+}
+
+sub add_value_to_parameter {
+
+    my ( $self, $parameter, $termsource, $accession ) = @_;
+
+    return if ( $termsource =~ $BLANK || ! $parameter );
+
+    # Pre-check that the termsource is valid. We don't actually use
+    # the database object returned.
+    my $args;
+    if ( $self->get_in_relaxed_mode() ) {
+	$args = {name       => $termsource,
+		 identifier => "ebi.ac.uk:Database:$termsource"};
+    }
+    my $database = $self->termsource_bag( $termsource, $args )
+	or croak(qq{Error: cannot find Term Source Name "$termsource"\n});
+
+    # Create NVTs; this is the best we can do in MAGE.
+    my $nvt = $self->create_nvt(
+	'Term Source Name',
+	$termsource,
+	undef,
+	$parameter,
+    );
+    if ( defined( $accession ) && $accession !~ $BLANK ) {
+	$self->create_nvt(
+	    'Term Accession Number',
+	    $accession,
+	    undef,
+	    $nvt,
+	);
+    }
+
+    return;
+}
+
+sub create_unit {
+
+    my ( $self, $type, $name, $termsource, $accession ) = @_;
+
+    # FIXME maybe do something with Term Source ?
+
+    return if ( $name =~ $BLANK );
+
+    my $unit;
+
+    # Don't propagate errors here to any signal handler.
+    my $sighandler = $SIG{__DIE__};
+    delete $SIG{__DIE__};
+
+    {   # Create the Unit, or fall back to QuantityUnit.
+	local $EVAL_ERROR;
+
+	UNITCLASS:
+	until ( ! $EVAL_ERROR && $unit ) {
+	    eval {
+		$unit = ("Bio::MAGE::Measurement::$type")->new();
+	    };
+	    if ( $EVAL_ERROR ) {
+
+		# Fall-through default is generic QuantityUnit
+		warn (
+		    "Warning: $type is not a MAGE Unit subclass."
+		  . " Creating generic QuantityUnit.\n"
+	        );
+
+		$type = 'QuantityUnit';
+		next UNITCLASS;
+	    }
+	}
+    }
+    {   # Attempt to set the UnitNameCV.
+	local $EVAL_ERROR;
+	eval {
+	    $unit->setUnitNameCV($name);
+	};
+	if ( $EVAL_ERROR ) {
+	    warn (
+		"Warning: $name is not a valid MAGE UnitNameCV here."
+	      . " Adding term as UnitName instead.\n"
+	    );
+	    $unit->setUnitName($name);
+	    $unit->setUnitNameCV('other');
+	}
+    }
+
+    # Reinstate signal handler.
+    $SIG{__DIE__} = $sighandler if $sighandler;
+
+    return $unit;
+}
+
+sub create_nested_oe_measurement {
+
+    # Create a nested OE measurement value, return the top-level OE
+    # and the Measurement-level OE. Units are handled elsewhere.
+
+    my ( $self, $category, $value ) = @_;
+
+    return if ( $value =~ $BLANK );
+
+    my $oe_meas_val = Bio::MAGE::Description::OntologyEntry->new(
+	category     => 'has_value',
+	value        => $value,
+    );
+
+    my $oe_meas_has_val = Bio::MAGE::Description::OntologyEntry->new(
+	category     => 'has_value',
+	value        => 'has_value',
+	associations => [$oe_meas_val],
+    );
+
+    my $oe_meas = Bio::MAGE::Description::OntologyEntry->new(
+	category     => 'Measurement',
+	value        => 'Measurement',
+	associations => [$oe_meas_has_val],
+    );
+
+    my $oe_has_meas = Bio::MAGE::Description::OntologyEntry->new(
+	category     => 'has_measurement',
+	value        => 'has_measurement',
+	associations => [$oe_meas],
+    );
+
+    my $oe = Bio::MAGE::Description::OntologyEntry->new(
+	category     => $category,
+	value        => $category,
+	associations => [$oe_has_meas],
+    );
+
+    return ($oe, $oe_meas);
+}
+
+sub create_nested_oe_unit {
+
+    my ( $self, $type, $name, $termsource, $accession, $oe_meas ) = @_;
+
+    return if ( $name =~ $BLANK );
+
+    my $oe_unit_subclass_val = $self->create_ontologyentry(
+	$type,
+	$name,
+	$termsource,
+	$accession,
+    );
+
+    my $oe_unit_subclass = Bio::MAGE::Description::OntologyEntry->new(
+	category     => $type,
+	value        => $type,
+	associations => [$oe_unit_subclass_val],
+    );
+
+    my $oe_unit = Bio::MAGE::Description::OntologyEntry->new(
+	category     => 'Unit',
+	value        => 'Unit',
+	associations => [$oe_unit_subclass],
+    );
+
+    my $oe_has_units = Bio::MAGE::Description::OntologyEntry->new(
+	category     => 'has_units',
+	value        => 'has_units',
+	associations => [$oe_unit],
+    );
+
+    $oe_meas->addAssociations($oe_has_units);
+
+    return $oe_meas;
+}
+
+sub create_performer {
+
+    my ( $self, $performername, $protocolapp ) = @_;
+
+    return if ( $performername =~ $BLANK );
+
+    # This is very basic at the moment. Maybe FIXME to fully parse
+    # performer name?
+    my $identifier = sprintf("%s:%s.Person",
+			     $self->get_id_prefix(),
+			     $performername);
+
+    my $performer = $self->people_bag(
+	$performername, {
+	    identifier => $identifier,
+	    name       => $performername,
+	    roles      => 'investigator',
+	},
+    );
+
+    $self->add_performer_to_protocolapp(
+	$performer,
+	$protocolapp,
+    ) if $protocolapp;
+
+    return $performer;
+}
+
+sub create_provider {
+
+    my ( $self, $providername, $source ) = @_;
+
+    return if ( $providername =~ $BLANK );
+
+    # This is very basic at the moment. Maybe FIXME to fully parse
+    # provider name?
+    my $identifier = sprintf("%s:%s.Person",
+			     $self->get_id_prefix(),
+			     $providername);
+
+    my $provider = $self->people_bag(
+	$providername, {
+	    identifier => $identifier,
+	    name       => $providername,
+	    roles      => 'biomaterial_provider',
+	},
+    );
+
+    $self->add_provider_to_source(
+	$provider,
+	$source,
+    ) if $source;
+
+    return $provider;
+}
+
+sub extract_protocolapps_by_type : PRIVATE {
+
+    my ( $self, $protocolapps, $desired_types ) = @_;
+
+    my ( @wanted_apps, @other_apps );
+
+    # FIXME? at the moment we just look at protocol type for
+    # disambiguation.
+    foreach my $app ( @{ $protocolapps || [] } ) {
+	my $protocol = $app->getProtocol();
+	my $oe       = $protocol->getType();
+	my $is_hyb;
+	if ( $oe ) {
+	    my $type = $oe->getValue();
+	    if ( first { $type eq $_ } @{ $desired_types || [] } ) {
+		push (@wanted_apps, $app);
+		$is_hyb++;
+	    }
+	}
+	unless ( $is_hyb ) {
+	    push (@other_apps, $app);
+	}
+    }
+
+    return ( \@wanted_apps, \@other_apps );
+}
+
+sub create_hybridization {
+
+    my ( $self, $name, $previous, $protocolapps, $channel ) = @_;
+
+    return if ( $name =~ $BLANK );
+
+    my $identifier_template = $self->generate_id_template( "hyb.$name" );
+
+    $channel ||= 'Unknown';
+    my $label = $self->label_bag( $channel, {dye => $channel} );
+
+    my $hybridization = $self->pba_bag(
+	$name,
+	{
+	    name                => $name,
+	    derived_from        => $previous,
+	    label               => $label,
+	    identifier_template => $identifier_template,
+	    hyb_protocolapps    => [ @{ $protocolapps } ],  # needs to be a copy.
+	},
+    );
+
+    return $hybridization;
+}
+
+sub create_assay {
+
+    my ( $self, $name, $previous, $protocolapps, $channel ) = @_;
+
+    return if ( $name =~ $BLANK );
+
+    my $identifier_template = $self->generate_id_template( "assay.$name" );
+
+    $channel ||= 'Unknown';
+    my $label = $self->label_bag( $channel, {dye => $channel} );
+
+    my $assay = $self->pba_bag(
+	$name,
+	{
+	    name                => $name,
+	    derived_from        => $previous,
+	    label               => $label,
+	    identifier_template => $identifier_template,
+	    hyb_protocolapps    => [ @{ $protocolapps } ],  # needs to be a copy.
+	    is_assay            => 1,
+	},
+    );
+
+    return $assay;
+}
+
+sub create_array {
+
+    my ( $self, $accession, $namespace, $termsource, $alt_accession, $pba ) = @_;
+
+    # accession is the term in the Array Design REF column;
+    # alt_accession is the optional contents of the Term Accession
+    # Number column; we use it preferentially where given.
+
+    return if ( $accession =~ $BLANK );
+
+    $accession = $alt_accession || ( ( $namespace || q{} ) . $accession );
+
+    my $arraydesign = $self->arraydesign_bag(
+	$accession,
+	{
+	    accession  => $accession,
+	    termsource => $termsource,
+	},
+    );
+
+    my $hybname = $pba->getName();
+
+    my $identifier_template = $self->generate_id_template( $hybname );
+
+    my $array = $self->array_bag(
+	$hybname,
+	{
+	    identifier_template => $identifier_template,
+	    arraydesign         => $arraydesign,
+	    armanuf_bag         => $self->get_bags->{armanuf},
+	},
+    );
+
+    my $hyb = $pba->getBioAssayCreation();
+
+    $hyb->setArray( $array );
+
+    return $array;
+}
+
+sub create_scan {
+
+    my ( $self, $name, $hyb_pba, $protocolapps, $channel, $previous_material ) = @_;
+
+    return if ( $name =~ $BLANK );
+
+    # Unless $hyb_pba is a true hyb-level PBA, make a new one.
+    my @old_pbas;
+    unless (    $hyb_pba
+	     && $hyb_pba->isa('Bio::MAGE::BioAssay::PhysicalBioAssay')
+	     && $hyb_pba->getBioAssayCreation() ) {
+
+	my $hybname;
+	if (    $hyb_pba 
+	     && $hyb_pba->can('getName')
+	     && $hyb_pba->getName() ) {
+	    $hybname = $hyb_pba->getName();
+	}
+	my $hyb_protoapps;
+	( $hyb_protoapps, $protocolapps )
+	    = $self->extract_protocolapps_by_type(
+		$protocolapps,
+		[ 'hybridization' ],
+	    );
+	$hyb_pba = $self->create_hybridization(
+	    $hybname || "hyb $name",
+	    $previous_material,
+	    $hyb_protoapps,
+	    $channel,
+	);
+	@old_pbas = $hyb_pba;
+    }
+
+    unless ( $hyb_pba && $hyb_pba->isa('Bio::MAGE::BioAssay::PhysicalBioAssay') ) {
+	croak("Error: PBA is incorrect type: " . ref $hyb_pba);
+    }
+
+    # If this is truly a virgin $hyb_pba, we need to remove the
+    # original BioAssayTreatment generated by create_hybridization.
+    my @cleaned_bats;
+    foreach my $bat ( @{ $hyb_pba->getBioAssayTreatments || [] } ) {
+	unless ( $bat->getTarget()->getIdentifier eq $hyb_pba->getIdentifier() ) {
+	    push @cleaned_bats, $bat
+	}
+    }
+    $hyb_pba->setBioAssayTreatments(\@cleaned_bats);
+
+    my $identifier_template = $self->generate_id_template( "scan.$name" );
+
+    # Note the hashref below *should* be unnecessary, but it's
+    # included here just in case.
+    $channel ||= 'Unknown';
+    my $label = $self->label_bag( $channel, {dye => $channel} );
+
+    # Create a channel-specific PBA.
+    my $channel_pba = $self->create_perchannel_pba(
+	$identifier_template,
+	$channel,
+	$label,
+	$hyb_pba,
+	$name,
+    );
+		
+    # Create/update the merged PBA here. Note that the .merged suffix
+    # convention is assumed in other modules for mapping the hyb PBA
+    # to the merged PBA and vice versa.
+    my $merged_pba = $self->extended_pba_bag(
+	$name,
+	{
+	    identifier_template => $identifier_template,
+	    name                => $name,
+	    label               => $label,
+	    scan_protocolapps   => [ @{ $protocolapps } ],  # needs to be a copy.
+	},
+    );
+
+    # Link the merged and channel-specific PBAs.
+    my $scan_id_template = "$identifier_template.$channel.merged";
+    $channel_pba->setBioAssayTreatments(
+	[
+	    $self->new_scan(
+		{
+		    identifier_template => $scan_id_template,
+		    name                => $name,
+		    pba                 => $merged_pba,
+		    target              => $merged_pba,
+		},
+	    )
+	]
+    );
+
+    return wantarray
+	   ? ( $merged_pba, $channel_pba, @old_pbas )
+	   : $merged_pba;
+}
+
+sub create_image {
+
+    my ( $self, $imagefile, $pba, $protocolapps, $channel, $previous_material ) = @_;
+
+    return if ( $imagefile =~ $BLANK );
+
+    # Unless $pba is a true scan-level PBA, make a new one.
+    # ( Scanning PBAs have no BAC; FIXME check that the BAT target is $pba? ).
+    my @old_pbas;
+    unless (    $pba
+	     && $pba->isa('Bio::MAGE::BioAssay::PhysicalBioAssay')
+	     && ! $pba->getBioAssayCreation() ) {
+
+	my $scanname;
+	if (    $pba 
+	     && $pba->can('getName')
+	     && $pba->getName() ) {
+	    $scanname = $pba->getName();
+	}
+	my $channel_pba;
+	($pba, $channel_pba, @old_pbas) = $self->create_scan(
+	    $scanname || $imagefile,
+	    $pba,
+	    $protocolapps,
+	    $channel,
+	    $previous_material,
+	);
+	push( @old_pbas, $pba, $channel_pba );
+    }
+
+    my $identifier_template = $self->generate_id_template( $imagefile );
+
+    # FIXME no support for ImageFormat OEs as yet (not in
+    # specification). We attempt here to derive it from the image
+    # filename extension.
+    my %known = (
+	tif    => 'TIFF',
+	tiff   => 'TIFF',
+	jpg    => 'JPEG',
+	jpeg   => 'JPEG',
+	png    => 'PNG',
+	gif    => 'GIF',
+    );
+
+    my $format = 'unknown';
+    if ( my ( $ext ) = ( $imagefile =~ m/\.(\w{3,4})$/ ) ) {
+	if ( my $term = $known{lc($ext)} ) {
+	    $format = $term;
+	}
+    }
+
+    my $image = $self->image_bag(
+	$imagefile,
+	{
+	    identifier_template => $identifier_template,
+	    uri                 => $imagefile,
+	    format              => $format,
+	},
+    );
+
+    $self->add_images_to_pba( [ $image ], $pba )
+	if $pba;
+
+    return wantarray
+	  ? ( $image, @old_pbas )
+	  : $image;
+}
+
+sub create_raw_data_file {
+
+    my ( $self,
+	 $name,
+	 $pba,
+	 $protocolapps,
+	 $array_accession,
+	 $channel,
+	 $previous_material, ) = @_;
+
+    return if ( $name =~ $BLANK );
+
+    my $identifier_template = $self->generate_id_template( $name );
+    my $filename = "$name.proc";
+
+    # Allow Affy CEL to be coded as native when skip_datafiles is
+    # used.
+    if ( $self->get_skip_datafiles() ) {
+	$filename = $name;
+	if ( $name =~ /\.CEL \z/ixms ) {
+	    $self->add_native_filetypes( $filename, 'CEL' );
+	}
+    }
+
+    my $mbad = $self->mbad_bag(
+	$name,
+	{
+	    name                => $name,
+	    filename            => $filename,
+	    identifier_template => $identifier_template,
+	}
+    );
+
+    my ( $mba, @old_pbas) = $self->create_feature_extraction(
+	$name,
+	$pba,
+	$protocolapps,
+	$channel,
+	$previous_material,
+	$mbad,
+    );
+
+    unless ( $mbad->getBioAssayDimension() ) {
+	my $bad = Bio::MAGE::BioAssayData::BioAssayDimension->new(
+	    identifier => "$identifier_template.MeasuredBioAssayDimension",
+	    bioAssays  => [ $mba ],
+	);
+	$mbad->setBioAssayDimension($bad);
+    }
+
+    my $datafile = $self->create_datafile(
+	$name,
+	'raw',
+	$mbad,
+	$array_accession,
+    );
+
+    $self->add_datafiles( $datafile );
+
+    return wantarray ? ( $mba, $mbad, @old_pbas ) : $mba;
+}
+
+sub create_feature_extraction : PRIVATE {
+
+    my ( $self,
+	 $name,
+	 $pba,
+	 $protocolapps,
+	 $channel,
+	 $previous_material,
+	 $mbad, ) = @_;
+
+    return if ( $name =~ $BLANK );
+    
+    # Unless $pba is a true scan-level PBA, make a new one.
+    # ( Scanning PBAs have no BAC; FIXME check that the BAT target is $pba? ).
+    my @old_pbas;
+    unless (    $pba
+	     && $pba->isa('Bio::MAGE::BioAssay::PhysicalBioAssay')
+	     && ! $pba->getBioAssayCreation() ) {
+
+	my $scan_protoapps;
+	( $scan_protoapps, $protocolapps )
+	    = $self->extract_protocolapps_by_type(
+		$protocolapps,
+		[ 'image_acquisition', 'hybridization' ]
+	    );
+
+	# Scan name incorporates hyb name if available; this will help
+	# when creating Illumina scan objects.
+	my $scanname;
+	if (    $pba 
+	     && $pba->can('getName')
+	     && $pba->getName() ) {
+	    $scanname = $pba->getName();
+	}
+	my $channel_pba;
+	($pba, $channel_pba, @old_pbas) = $self->create_scan(
+	    $scanname || $name,
+	    $pba,
+	    $scan_protoapps,
+	    $channel,
+	    $previous_material,
+	);
+	push( @old_pbas, $pba, $channel_pba );
+    }
+
+    my $identifier_template = $self->generate_id_template( $name );
+
+    my $mba = $self->mba_bag(
+	$name,
+	{
+	    identifier_template    => $identifier_template,
+	    name                   => $name,
+	    physical_bioassay      => $pba,
+	    protocolapps           => [ @{ $protocolapps } ],  # needs to be a copy.
+	    measured_bioassay_data => $mbad,
+	},
+    );
+
+    return wantarray ? ( $mba, @old_pbas ) : $mba;
+}
+
+sub create_normalization {
+
+    my ( $self,
+	 $name,
+	 $previous_event,
+	 $protocolapps,
+	 $channel,
+	 $previous_material, ) = @_;
+
+    return if ( $name =~ $BLANK );
+
+    # Unless $previous_event is at least MBA or DBA, make a new MBA.
+    my @old_bioassays;
+    unless (    $previous_event
+	     && (   $previous_event->isa('Bio::MAGE::BioAssay::MeasuredBioAssay')
+	 	 || $previous_event->isa('Bio::MAGE::BioAssay::DerivedBioAssay') ) ) {
+
+	my $fext_protoapps;
+	( $fext_protoapps, $protocolapps )
+	    = $self->extract_protocolapps_by_type(
+		$protocolapps,
+		[ 'feature_extraction', 'image_acquisition', 'hybridization' ]
+	    );
+
+	my $fextname;
+	if (    $previous_event 
+	     && $previous_event->can('getName')
+	     && $previous_event->getName() ) {
+	    $fextname = $previous_event->getName();
+	}
+	($previous_event, @old_bioassays) = $self->create_feature_extraction(
+	    $fextname || $name,
+	    $previous_event,
+	    $fext_protoapps,
+	    $channel,
+	    $previous_material,
+	);
+	push( @old_bioassays, $previous_event );
+    }
+
+    my $identifier_template = $self->generate_id_template( $name );
+
+    my $dba = $self->dba_bag(
+	$name,
+	{
+	    identifier_template => $identifier_template,
+	    name                => $name,
+	    source_bioassays    => [ $previous_event ],
+	    bioassaymap_bag     => $self->get_bags()->{bam},
+	},
+    );
+
+    return ( $dba, $protocolapps, @old_bioassays );
+}
+
+sub create_normalized_data {
+
+    my ( $self,
+	 $name,
+	 $dba,
+	 $protocolapps,
+	 $previous_data,
+	 $array_accession,
+	 $channel,
+	 $previous_material, ) = @_;
+
+    return if ( $name =~ $BLANK );
+
+    # Unless $dba is an actual DBA, make a new DBA.
+    my @old_bioassays;
+    unless (    $dba
+	     && $dba->isa('Bio::MAGE::BioAssay::DerivedBioAssay') ) {
+	my $fext_protoapps;
+	( $fext_protoapps, $protocolapps )
+	    = $self->extract_protocolapps_by_type(
+		$protocolapps,
+		[ 'feature_extraction', 'image_acquisition', 'hybridization' ]
+	    );
+
+	# Here we just default to the file name as internal identifier.
+	my $exhausted_fext_apps;
+	($dba, $exhausted_fext_apps, @old_bioassays) = $self->create_normalization(
+	    $name,
+	    $dba,
+	    $fext_protoapps,
+	    $channel,
+	    $previous_material,
+	);
+
+	# N.B. $exhausted_fext_apps should be empty here.
+	unless ( ! scalar( @{ $exhausted_fext_apps || [] } ) ) {
+	    die("Internal error: Some feature extraction protocol apps unused.");
+	}
+
+	push( @old_bioassays, $dba );
+    }
+
+    if ( ! defined($dba) ) {
+	warn("Undefined DBA object (no Normalization Name?); will not create DBAData $name.\n");
+	return;
+    }
+
+    my $identifier_template = $self->generate_id_template( $name );
+    my $filename = "$name.proc";
+
+    # Allow Affy CHP to be coded as native when skip_datafiles is
+    # used.
+    if ( $self->get_skip_datafiles() ) {
+	$filename = $name;
+	if ( $name =~ /\.CHP \z/ixms ) {
+	    $self->add_native_filetypes( $filename, 'CHP' );
+	}
+    }
+
+    my $dbad = $self->dbad_bag(
+	$name,
+	{
+	    name                    => $name,
+	    filename                => $filename,
+	    identifier_template     => $identifier_template,
+	    protocolapps            => [ @{ $protocolapps } ],  # needs to be a copy.
+	    source_bioassay_dataset => $previous_data ? [ $previous_data ] : undef,
+	    bioassay_map            => $dba->getDerivedBioAssayMap(),
+	}
+    );
+
+    # Add DBAD to DBA.
+    $self->add_dbad_to_dba($dbad, $dba);
+
+    unless ( $dbad->getBioAssayDimension() ) {
+	my $bad = Bio::MAGE::BioAssayData::BioAssayDimension->new(
+	    identifier => "$identifier_template.DerivedBioAssayDimension",
+	    bioAssays  => [ $dba ],
+	);
+	$dbad->setBioAssayDimension($bad);
+    }
+
+    my $datafile = $self->create_datafile(
+	$name,
+	'normalized',
+	$dbad,
+	$array_accession,
+    );
+
+    $self->add_datafiles( $datafile );
+
+    return ($dbad, @old_bioassays);
+}
+
+sub create_raw_data_matrix {
+
+    my ( $self, $name, $protocolapps, $array_accession ) = @_;
+
+    # FIXME nothing done with $protocolapps yet; since this is a
+    # feature extraction protocol, and MeasuredBioAssayData doesn't
+    # have ProducerTransformation, they must be attached to the
+    # MBA. We don't create those until we've read the file, however.
+
+    # In effect, this boils down to a requirement that Scan Name be
+    # used in conjunction with Array Data Matrix File if the hybs are
+    # multi-channel, or if there's a scan protocol (since otherwise
+    # the scan PBAs are not created either). Feature extraction
+    # protocol cannot be supported on Array Data Matrix File at this
+    # time. All these BioAssay objects would also have to be inferred
+    # not only here but also in the data matrix management code. Since
+    # Array Data Matrix is an edge use-case (Illumina, by contrast, is
+    # handled as Array Data File).
+
+    return if ( $name =~ $BLANK );
+
+    my $identifier_template = $self->generate_id_template( $name );
+    my $filename = "$name.proc";
+
+    # Allow files to be coded as native when skip_datafiles is
+    # used.
+    if ( $self->get_skip_datafiles() ) {
+	$filename = $name;
+    }
+
+    my $mbad = $self->mbad_bag(
+	$name,
+	{
+	    name                => $name,
+	    filename            => $filename,
+	    identifier_template => $identifier_template,
+	}
+    );
+
+    my $datafile = $self->create_datafile(
+	$name,
+	$CONFIG->get_RAW_DM_FILE_TYPE(),
+	$mbad,
+	$array_accession,
+    );
+
+    $self->add_datafiles( $datafile );
+
+    return $mbad;
+}
+
+sub create_norm_data_matrix {
+
+    my ( $self,
+	 $name,
+	 $protocolapps,
+	 $array_accession,
+	 $dba,
+	 $channel,
+	 $previous_material, ) = @_;
+
+    return if ( $name =~ $BLANK );
+
+    # Unless $dba is an actual DBA, make a new DBA.
+    my @old_bioassays;
+    unless (    $dba
+	     && $dba->isa('Bio::MAGE::BioAssay::DerivedBioAssay') ) {
+	my $fext_protoapps;
+	( $fext_protoapps, $protocolapps )
+	    = $self->extract_protocolapps_by_type(
+		$protocolapps,
+		[ 'feature_extraction', 'image_acquisition', 'hybridization' ]
+	    );
+
+	my $exhausted_fext_apps;
+	($dba, $exhausted_fext_apps, @old_bioassays) = $self->create_normalization(
+	    $name,
+	    $dba,
+	    $fext_protoapps,
+	    $channel,
+	    $previous_material,
+	);
+
+	# N.B. $exhausted_fext_apps should be empty here.
+	unless ( ! scalar( @{ $exhausted_fext_apps || [] } ) ) {
+	    die("Internal error: Some feature extraction protocol apps unused.");
+	}
+
+	push( @old_bioassays, $dba );
+    }
+
+    my $identifier_template = $self->generate_id_template( $name );
+    my $filename = "$name.proc";
+
+    # Allow files to be coded as native when skip_datafiles is
+    # used.
+    if ( $self->get_skip_datafiles() ) {
+	$filename = $name;
+    }
+
+    my $dbad = $self->dbad_bag(
+	$name,
+	{
+	    name                    => $name,
+	    filename                => $filename,
+	    identifier_template     => $identifier_template,
+	    protocolapps            => [ @{ $protocolapps } ],  # needs to be a copy.
+	}
+    );
+
+    # Add DBAD to DBA.
+    $self->add_dbad_to_dba($dbad, $dba);
+
+    my $txn;
+    unless ( $txn = $dbad->getProducerTransformation() ) {
+	$txn = Bio::MAGE::BioAssayData::Transformation->new(
+	    identifier => "$identifier_template.Transformation",
+	    name       => $name,
+	);
+	$dbad->setProducerTransformation( $txn );
+    }
+
+    my $mapping;
+    unless ( $mapping = $txn->getBioAssayMapping() ) {
+	$mapping = Bio::MAGE::BioAssayData::BioAssayMapping->new();
+	$txn->setBioAssayMapping( $mapping );
+    }
+
+    my $found;
+    foreach my $map ( @{ $mapping->getBioAssayMaps() || [] } ) {
+	if ( my $target = $map->getBioAssayMapTarget() ) {
+	    if ( $target->getIdentifier() eq $dba->getIdentifier() ) {
+		$found++;
+	    }
+	}
+    }
+    unless ( $found ) {
+	my $map = $self->bam_bag(
+	    $dba->getName(),
+	    {
+		identifier_template => $identifier_template,
+		target_bioassay     => $dba,
+	    },
+	);
+	$mapping->addBioAssayMaps( $map );
+    }
+
+    my $datafile = $self->create_datafile(
+	$name,
+	$CONFIG->get_FGEM_FILE_TYPE(),
+	$dbad,
+	$array_accession,
+    );
+
+    $self->add_datafiles( $datafile );
+
+    return $dbad;
+}
+
+sub create_datafile {
+
+    my ( $self, $filename, $datatype, $bad, $array_accession ) = @_;
+
+    unless ( defined $array_accession ) {
+	warn("Error: Datafile $filename needs an array accession number.\n");
+	$array_accession = 'Unknown';
+    }
+
+    my ( $path, $name );
+    if ( $self->get_skip_datafiles() ) {
+	$path = $filename;
+	$name = $filename;
+    }
+    else {
+	$path = get_filepath_from_uri(
+	    $filename,
+	    $self->get_source_directory(),
+	);
+	$name = basename( $path );
+    }
+
+    my $file = ArrayExpress::Datafile->new({
+	path            => $path,
+	name            => $name,
+	data_type       => $datatype,
+	mage_badata     => $bad,
+	array_design_id => $array_accession,
+    });
+
+    return $file;
+}
+
+sub create_factorvalue_value {
+
+    my ( $self, $factorname, $altcategory, $value, $termsource, $accession ) = @_;
+
+    return if ( $value =~ $BLANK );
+
+    my ($factorvalue, $exp_factor)
+	= $self->create_generic_factorvalue( $factorname, $value, $altcategory );
+
+    my $category;
+    if ( $altcategory ) {
+
+	# If we're given a category in parentheses, use it.
+	$category = $altcategory;
+    }
+    else {
+
+	# Otherwise, derive the category from the EF term:
+	my $ef_oe = $exp_factor->getCategory()
+	    or croak("Error: Experimental Factor $factorname has no type.");
+
+	my @ef_catparts = split /_/, $ef_oe->getValue();
+
+	$category = join(q{}, map{ ucfirst($_) } @ef_catparts);
+    }
+
+    my $oe = $self->create_ontologyentry( $category, $value, $termsource, $accession );
+
+    $factorvalue->setValue( $oe );
+
+    $self->add_factorvalue_to_factor( $factorvalue, $exp_factor );
+
+    return $factorvalue;
+}
+
+sub create_factorvalue_measurement {
+
+    my ( $self, $factorname, $altcategory, $value, $unit ) = @_;
+
+    return if ( $value =~ $BLANK );
+
+    # Note: $altcategory is ignored for measurement.
+
+    my $unitname;
+    if ( $unit ) {
+        $unitname = $self->get_unitname_for_unit( $unit );
+    }
+
+    # Here we're retasking the altcategory slot for unitname; this is
+    # a bit naughty. FIXME.
+    my ($factorvalue, $exp_factor) = $self->create_generic_factorvalue(
+        $factorname,
+        $value,
+        $unitname,
+    );
+
+    my $measurement = Bio::MAGE::Measurement::Measurement->new(
+	value => $value,
+        unit  => $unit,
+    );
+
+    $factorvalue->setMeasurement( $measurement );
+
+    # This has to be done *after* adding the unit to the term. It's
+    # now handled in the relevant grammar action.
+#    $self->add_factorvalue_to_factor( $factorvalue, $exp_factor );
+
+    return wantarray ? ($factorvalue, $exp_factor) : $factorvalue;
+}
+
+sub create_generic_factorvalue : PRIVATE {
+
+    my ( $self, $factorname, $value, $altcategory ) = @_;
+
+    my $args;
+    if ( $self->get_in_relaxed_mode() ) {
+	$args = {name => $factorname, type => $factorname};
+    }
+
+    my $exp_factor = $self->factor_bag(	$factorname, $args )
+	or croak(qq{Error: Experimental Factor Name not found: "$factorname"\n}); 
+
+    my $identifier_template = $self->generate_id_template(
+	$altcategory ? "$factorname.$altcategory.$value" : "$factorname.$value"
+    );
+
+    my $name = defined $altcategory ? "$value $altcategory" : $value;
+    my $factorvalue = $self->factorvalue_bag(
+	"$factorname.$name",
+	{
+	    identifier_template => $identifier_template,
+	    name                => $name,
+	},
+    );
+
+    return wantarray
+	   ? ( $factorvalue, $exp_factor )
+	   : $factorvalue;
+}
+
+sub create_description {
+
+    my ( $self, $text, $describable ) = @_;
+
+    return if ( $text =~ $BLANK );
+
+    my $description = Bio::MAGE::Description::Description->new(
+	text => $text,
+    );
+
+    $self->add_description_to_describable( $description, $describable)
+	if $describable;
+
+    return $description;
+}
+
+sub create_nvt {
+
+    my ( $self, $name, $value, $type, $extendable ) = @_;
+
+    return if ( $value =~ $BLANK );
+
+    my $nvt = Bio::MAGE::NameValueType->new(
+	name  => $name,
+	value => $value,
+	type  => $type,
+    );
+
+    $self->add_nvt_to_extendable( $nvt, $extendable )
+	if $extendable;
+
+    return $nvt;
+}
+
+sub add_char_to_material {
+
+    my ( $self, $char, $material ) = @_;
+
+    return unless ( $material && $char );
+
+    my $found;
+    if ( my $preexisting_chars = $material->getCharacteristics() ) {
+
+	my $new_category = $char->getCategory();
+	my $new_value    = $char->getValue();
+	$found = first {
+	       $_->getCategory() eq $new_category
+	    && $_->getValue()    eq $new_value;
+	}   @$preexisting_chars;
+    }
+    $material->addCharacteristics($char) unless $found;
+
+    return;
+}
+
+sub set_material_type {
+
+    my ( $self, $material, $type ) = @_;
+
+    return unless ( $material && $type );
+
+    $material->setMaterialType($type);
+
+    return;
+}
+
+sub set_technology_type {
+
+    my ( $self, $assay, $type ) = @_;
+
+    return unless ( $assay && $type );
+
+    # Look through descriptions for a matching OE, or a description
+    # holding an OE with the same category.
+    my ( $found, $desc );
+    if ( my $descs = $assay->getDescriptions() ) {
+	foreach my $old ( @$descs ) {
+	    if ( my $oes = $old->getAnnotations() ) {
+		foreach my $oe ( @$oes ) {
+
+		    # Same category; record the description object.
+		    if ( $oe->getCategory() eq $type->getCategory() ) {
+			$desc = $old;
+
+			# Same value; we're done here.
+			if ( $oe->getValue() eq $type->getValue() ) {
+			    $found++;
+			}
+		    }
+		}
+	    }
+	}
+    }
+
+    unless ( $found ) {
+	unless ( $desc ) {
+	    $desc = Bio::MAGE::Description::Description->new();
+	    $assay->addDescriptions( $desc );
+	}
+	$desc->addAnnotations( $type );
+    }
+
+    return;
+}
+
+sub add_factorvals_to_bioassays {
+
+    my ( $self, $factorvals, $bioassays ) = @_;
+
+    # Generate a uniqued list of bioassays.
+    my %unique_ba = map { $_->getIdentifier() => $_ } @$bioassays;
+
+    # Takes a list of FactorValues and BioAssays, links the former to
+    # the latter, checking for duplicates.
+
+    # Add the factors if not already linked.
+    foreach my $ba ( values %unique_ba ) {
+	foreach my $fv ( @$factorvals ) {
+	    $self->add_factorval_to_bioassay( $fv, $ba );
+	}
+    }
+
+    return;
+}
+
+sub add_performer_to_protocolapp : PRIVATE {
+
+    my ( $self, $performer, $protocolapp ) = @_;
+
+    my $found;
+    if ( my $preexisting_people = $protocolapp->getPerformers() ) {
+
+	my $new_id = $performer->getIdentifier();
+	$found = first {
+	    $_->getIdentifier()     eq $new_id
+	}   @$preexisting_people;
+    }
+    $protocolapp->addPerformers($performer) unless $found;
+
+    return;
+}
+
+sub add_date_to_protocolapp {
+
+    my ( $self, $date, $protocolapp ) = @_;
+
+    return if ( $date =~ $BLANK );
+
+    $protocolapp->setActivityDate( $date ) if $protocolapp;
+
+    return;
+}
+
+sub add_provider_to_source : PRIVATE {
+
+    my ( $self, $provider, $source ) = @_;
+
+    my $found;
+    if ( my $preexisting_people = $source->getSourceContact() ) {
+
+	my $new_id = $provider->getIdentifier();
+	$found = first {
+	    $_->getIdentifier()     eq $new_id
+	}   @$preexisting_people;
+    }
+    $source->addSourceContact($provider) unless $found;
+
+    return;
+}
+
+sub add_description_to_describable : PRIVATE {
+
+    my ( $self, $description, $describable ) = @_;
+
+    my $found;
+    if ( my $preexisting_descs = $describable->getDescriptions() ) {
+
+	my $new_text = $description->getText();
+	$found = first {
+	    $_->getText()     eq $new_text
+	}   @$preexisting_descs;
+    }
+    $describable->addDescriptions($description) unless $found;
+
+    return;
+}
+
+sub add_nvt_to_extendable : PRIVATE {
+
+    my ( $self, $nvt, $extendable ) = @_;
+
+    my $found;
+    if ( my $preexisting_nvts = $extendable->getPropertySets() ) {
+
+	my $new_name  = $nvt->getName();
+	my $new_value = $nvt->getValue();
+	$found = first {
+	       $_->getName()  eq $new_name
+	    && $_->getValue() eq $new_value;
+	}   @$preexisting_nvts;
+    }
+    $extendable->addPropertySets($nvt) unless $found;
+
+    return;
+}
+
+sub add_label_to_le : PRIVATE {
+
+    my ( $self, $label, $le ) = @_;
+
+    my $found;
+    if ( my $preexisting_labels = $le->getLabels() ) {
+
+	my $new_name = $label->getName();
+	$found = first {
+	    $_->getName() eq $new_name
+	}   @$preexisting_labels;
+    }
+    $le->addLabels($label) unless $found;
+
+    return;
+}
+
+sub add_parameterval_to_protocolapp : PRIVATE {
+
+    my ( $self, $parameterval, $protocolapp ) = @_;
+
+    my $found;
+    if ( my $preexisting_values = $protocolapp->getParameterValues() ) {
+
+	my $parameter = $parameterval->getParameterType();
+	my $new_id    = $parameter->getIdentifier();
+	$found = first {
+	    my $oldparam = $_->getParameterType();
+	    $oldparam->getIdentifier() eq $new_id
+	}   @$preexisting_values;
+    }
+    $protocolapp->addParameterValues($parameterval) unless $found;
+
+    return;
+}
+    
+sub add_factorvalue_to_factor {
+
+    my ( $self, $fv, $factor ) = @_;
+
+    my $found;
+    my $name = $fv->getName();
+    if ( my $preexisting_fvs = $factor->getFactorValues() ) {
+	if ( my $value = $fv->getValue() ) {
+	    my $new_cat = $value->getCategory();
+	    my $new_val = $value->getValue();
+	    $found = first {
+		my $oldvalue = $_->getValue;
+		$oldvalue
+	     && ($oldvalue->getCategory() eq $new_cat )
+	     && ($oldvalue->getValue()    eq $new_val )
+	    } @$preexisting_fvs;
+	}
+	elsif ( my $measurement = $fv->getMeasurement() ) {
+	    my $new_val  = $measurement->getValue();
+	    my $new_unit = $self->get_unitname_from_measure($measurement);
+	    $found = first {
+		my $oldmeas = $_->getMeasurement();
+		$oldmeas
+	    &&  $oldmeas->getValue() eq $new_val
+	    && ($self->get_unitname_from_measure($oldmeas) eq $new_unit)
+	    } @$preexisting_fvs;
+	}
+	elsif ( defined $name ) {
+	    $found = first {
+		$_->getName() eq $name
+	    } @$preexisting_fvs;
+	}
+	else {
+	    croak("Error: FactorValue has no value, measurement or name.");
+	}
+    }
+
+    $factor->addFactorValues($fv) unless $found;
+
+    return;
+}
+
+sub get_unitname_from_measure : PRIVATE {
+
+    my ( $self, $measurement ) = @_;
+
+    my $unitname;
+    if ( my $unit = $measurement->getUnit() ) {
+	$unitname = $self->get_unitname_for_unit( $unit );
+    }
+
+    # Return empty string on failure; this value will be used in
+    # string comparisons so should not be undef.
+    return defined $unitname ? $unitname : q{};
+}
+    
+sub add_unit_to_thing {
+
+    my ( $self, $unit, $thing ) = @_;
+
+    return unless ( $unit && $thing );
+
+    # We attempt to make this smart: $thing can be a ParameterValue
+    # (NOTE: may need to create a new Parameter, attach to the
+    # Protocol if the old and new units don't match), or a Measurement
+    # (in which case just attach directly).
+    if ( $thing->isa('Bio::MAGE::Protocol::ParameterValue') ) {
+	$self->add_unit_to_parameterval($unit, $thing);
+    }
+    elsif ( $thing->isa('Bio::MAGE::Measurement::Measurement') ) {
+	$thing->setUnit($unit);
+    }
+    else {
+	croak("Error: Cannot process argument: $thing (" . ref($thing) .")");
+    }
+
+    return;
+}
+
+sub get_unit_for_param : PRIVATE {
+
+    my ( $self, $parameter ) = @_;
+
+    return unless $parameter;
+
+    my $defaultval = $parameter->getDefaultValue();
+
+    if ($defaultval) {
+	return $defaultval->getUnit();
+    }
+
+    return;
+}
+
+sub get_unitname_for_unit : PRIVATE {
+
+    my ( $self, $unit ) = @_;
+
+    return unless $unit;
+
+    my $unitname = $unit->getUnitNameCV();
+    if ( ! $unitname || $unitname eq 'other' ) {
+	$unitname = $unit->getUnitName() || 'other';
+    }
+    
+    return $unitname;
+}
+
+sub add_unit_to_parameterval : PRIVATE {
+
+    my ( $self, $unit, $paramval ) = @_;
+
+    my $unitname  = $self->get_unitname_for_unit($unit);
+    my $parameter = $paramval->getParameterType();
+
+    if ( my $oldunit = $self->get_unit_for_param($parameter) ) {
+
+	# Check the unit; if different create a new parameter and
+	# add it to the relevant protocol. Note that the last
+	# entry in @::protocolapp_list should point to the protocol
+	# of interest.
+	my $oldunitname = $self->get_unitname_for_unit($oldunit);
+	unless ( $oldunitname eq $unitname ) {
+
+	    # Find the associated protocol.
+	    unless ( scalar @::protocolapp_list ) {
+		die("Error: no protocolapps available.");
+	    }
+	    my $protocol  = $::protocolapp_list[$#::protocolapp_list]->getProtocol();
+	    my $paramname = $parameter->getName();
+	    unless ($protocol) {
+		croak(
+		    "Error: cannot create a new parameter ($paramname,"
+		  . " unit $unitname) without a protocol.\n"
+		);
+	    }
+
+	    # Check through the other parameters linked to this
+	    # protocol. Match by param name and unit.
+	    my $preexisting;
+	    OLDPARAM:
+	    foreach my $old ( @{ $protocol->getParameterTypes() || [] } ) {
+		if ( $paramname eq $old->getName() ) {
+		    my $preex_unit = $self->get_unit_for_param($old);
+		    my $preex_unitname = $self->get_unitname_for_unit($preex_unit);
+		    if ( $preex_unitname eq $unitname ) {
+			$preexisting = $old;
+			last OLDPARAM;
+		    }
+		}
+	    }
+
+	    if ( $preexisting ) {
+
+		# Repoint $paramval to the preexisting ParameterType.
+		$paramval->setParameterType( $preexisting );
+		$parameter = $preexisting;
+	    }
+	    else {
+
+		# Completely new parameter.
+		my $param_id = sprintf(
+		    "%s:%s.%s.Parameter.%s",
+		    $self->get_id_prefix(),
+		    $protocol->getIdentifier(),
+		    $paramname,
+		    $unitname,
+		);
+		my $newparam = $self->parameter_bag(
+		    "$paramname.$unitname." . $protocol->getName(),
+		    {
+			name       => $paramname,
+			identifier => $param_id,
+			unit       => $unit,
+		    },
+		);
+
+		# Link everything together.
+		$protocol->addParameterTypes( $newparam );
+		$paramval->setParameterType( $newparam );
+		$parameter = $newparam;
+	    }
+	}
+    }
+    else {
+
+	# Add the unit to the preexisting parameter.
+	my $newdefault = Bio::MAGE::Measurement::Measurement->new(
+	    unit => $unit,
+	);
+	$parameter->setDefaultValue($newdefault);
+    }
+
+    # Fix the parameter identifier to add $unitname suffix if it's not
+    # already there.
+    my $param_id = $parameter->getIdentifier();
+    unless( $param_id =~ /$unitname \z/xms ) {
+	$param_id .= ".$unitname";
+	$parameter->setIdentifier($param_id);
+    }
+
+    return;
+}
+
+sub create_single_treatment : PRIVATE {
+
+    my ( $self, $app, $bmm, $order, $default_action ) = @_;
+
+    my $action;
+    if ( $app ) {
+
+	# Use Storable::dclone to copy the ProtocolType OE and reset
+	# the category to Action.
+	my $protocol = $app->getProtocol();
+	if ( my $type = $protocol->getType() ) {
+	    $action = dclone($protocol->getType());
+	    $action->setCategory('Action');
+	}
+    }
+
+    unless ( $action ) {
+
+	# Fall back to a generic default.
+	$action = $self->create_ontologyentry(
+	    'Action',
+	    ($default_action || 'specified_biomaterial_action'),
+	);
+    }
+
+    # Quick and dirty, we may want to revisit this FIXME
+    my $identifier = $self->generate_id_template(
+	unique_identifier() . '.Treatment',
+    );
+
+    my $treatment = Bio::MAGE::BioMaterial::Treatment->new(
+	identifier                    => $identifier,
+	action                        => $action,
+	order                         => $order,
+	sourceBioMaterialMeasurements => [ $bmm ],
+    );
+
+    $treatment->setProtocolApplications( [ $app ] ) if $app;
+
+    return $treatment;
+}
+
+sub create_material_treatments : PRIVATE {
+
+    my ( $self, $protocolapps, $previous, $default_action ) = @_;
+
+    my @treatments;
+
+    # One BioMaterialMeasurement used for all Treatments.
+    my $bmm;
+    if ( $previous ) {
+	$bmm = Bio::MAGE::BioMaterial::BioMaterialMeasurement->new(
+	    bioMaterial => $previous,
+	);
+    }
+
+    # If ProtocolApps supplied, generate a treatment for each.
+    my $order = 1;
+    foreach my $app ( @{ $protocolapps } ) {
+
+	my $treatment = $self->create_single_treatment(
+	    $app,
+	    $bmm,
+	    $order,
+	);
+	$order++;
+	push @treatments, $treatment;
+    }
+
+    # Fall back to a single treatment if there's no protocol
+    # applications, but there is a source material.
+    if ( ! scalar( @treatments ) && $bmm ) {
+	my $treatment = $self->create_single_treatment(
+	    undef,
+	    $bmm,
+	    1,
+	    $default_action,
+	);
+	push @treatments, $treatment;
+    }
+
+    return \@treatments;
+}
+
+sub add_treatments_to_material : PRIVATE {
+
+    my ( $self, $treatments, $material ) = @_;
+
+    # FIXME compare old source materials with the new ones, add and
+    # maybe renumber the treatment order if possible?
+
+    # Lookup table of treatments by order (NB this is *not* infallible
+    # FIXME).
+    my %old_treatments = map { $_->getOrder() => $_ }
+	@{ $material->getTreatments() || [] };
+
+    foreach my $new ( @$treatments ) {
+
+	# See if we've seen this treatment before. This relies on the
+	# object having the same order attribute FIXME.
+	if ( my $old = $old_treatments{ $new->getOrder() } ) {
+	    $self->update_treatment_info( $old, $new );
+	}
+	else{
+	    $material->addTreatments($new);
+	}
+    }
+
+    return;
+}
+
+sub update_treatment_info : PRIVATE {
+
+    my ( $self, $old, $new ) = @_;
+
+    if ( my $bmms = $new->getSourceBioMaterialMeasurements() ) {
+	foreach my $bmm ( @{ $bmms } ) {
+	    $self->add_source_to_treatment( $bmm, $old );
+	}
+    }
+
+    if ( my $apps = $new->getProtocolApplications() ) {
+	foreach my $app ( @{ $apps } ) {
+	    $self->add_protocolapp_to_treatment( $app, $old );
+	}
+    }
+
+    return;
+}
+
+sub add_source_to_treatment : PRIVATE {
+
+    my ( $self, $source_bmm, $treatment ) = @_;
+
+    my $found;
+    if ( my $preexisting_bmms
+	     = $treatment->getSourceBioMaterialMeasurements() ) {
+	my $new_id = $source_bmm->getBioMaterial()->getIdentifier();
+	$found = first {
+	    $_->getBioMaterial()->getIdentifier() eq $new_id;
+	}   @$preexisting_bmms;
+    }
+    $treatment->addSourceBioMaterialMeasurements($source_bmm)
+	unless $found;
+
+    return;
+}
+
+sub add_protocolapp_to_treatment : PRIVATE {
+
+    my ( $self, $protoapp, $treatment ) = @_;
+
+    my $found;
+    if ( my $preexisting_apps
+	     = $treatment->getProtocolApplications() ) {
+	my $new_id = $protoapp->getProtocol()->getIdentifier();
+	$found = first {
+	    $_->getProtocol()->getIdentifier() eq $new_id;
+	}   @$preexisting_apps;
+    }
+    $treatment->addProtocolApplications($protoapp)
+	unless $found;
+
+    return;
+}
+
+sub add_images_to_pba : PRIVATE {
+
+    my ( $self, $images, $pba ) = @_;
+
+    my @image_acquisitions;
+
+    # Capture all the ImageAcquisitions attached to $pba
+    foreach my $bat ( @{ $pba->getBioAssayTreatments() || [] } ) {
+	if ( $bat->isa('Bio::MAGE::BioAssay::ImageAcquisition') ) {
+	    push @image_acquisitions, $bat;
+	}
+    }
+
+    # Create ImageAcquisition if necessary, then add $images to all
+    # IAs in the PBA.
+    unless ( scalar @image_acquisitions ) {
+	my $ia_identifier = $pba->getIdentifier();
+	$ia_identifier =~ s/PhysicalBioAssay \z/ImageAcquisition/xms;
+	my $ia = Bio::MAGE::BioAssay::ImageAcquisition->new(
+	    identifier       => $ia_identifier,
+	    target           => $pba,
+	    physicalBioAssay => $pba,
+	);
+	push @image_acquisitions, $ia;
+    }
+
+    foreach my $ia ( @image_acquisitions ) {
+	foreach my $image ( @$images ) {
+	    my $found = first {
+		$_->getURI() eq $image->getURI()
+	    } @{ $ia->getImages() || [] };
+	    $ia->addImages( $image ) unless $found;
+	}
+    }
+
+    foreach my $image ( @$images ) {
+	my $found = first {
+	    $_->getURI() eq $image->getURI()
+	} @{ $pba->getPhysicalBioAssayData() || [] };
+	$pba->addPhysicalBioAssayData( $image ) unless $found;
+    }
+
+    return;
+}
+
+sub add_basource_to_map : PRIVATE {
+
+    my ( $self, $bioassay, $map ) = @_;
+
+    my $found;
+    if ( my $sources = $map->getSourceBioAssays() ) {
+	my $new_id = $bioassay->getIdentifier();
+	$found = first {
+	    $_->getIdentifier() eq $new_id
+	} @$sources;
+    }
+    $map->addSourceBioAssays( $bioassay ) unless $found;
+
+    return;
+}
+    
+sub create_perchannel_pba : PRIVATE {
+
+    my ( $self, $identifier_template, $channelname, $label, $hyb_pba, $scanname ) = @_;
+
+    $channelname ||= 'Unknown';
+    my $new_id_template  = "$identifier_template.$channelname";
+
+    # The following convention is assumed when adding FactorValues to
+    # all the BioAssays in a row.
+    my $channel_pba_name = "$scanname.$channelname";
+
+    # We don't pass $args->{derived_from} down to the extended PBA
+    # generation; this is used as a flag to indicate hyb-level PBAs
+    # only.
+    my $channel_pba = $self->extended_pba_bag(
+	$channel_pba_name,
+	{
+	    identifier_template => $new_id_template,
+	    name                => $channel_pba_name,
+	    label               => $label,
+	},
+    );
+
+    # Add a BioAssayTreatment pointing from $hyb_pba to channel PBAs
+    # here.
+    my %preexisting;
+    if ( my $old_treats = $hyb_pba->getBioAssayTreatments() ) {
+	foreach my $treat ( @$old_treats ) {
+	    if ( my $old_pba = $treat->getPhysicalBioAssay() ) {
+		$preexisting{ $old_pba->getIdentifier() }++;
+	    }
+	}
+    }
+    unless ( $preexisting{ $channel_pba->getIdentifier() } ) {
+	$hyb_pba->addBioAssayTreatments(
+	    $self->new_scan(
+		{
+		    identifier_template => $new_id_template,
+		    name                => $channel_pba_name,
+		    pba                 => $channel_pba,
+		    target              => $channel_pba,
+		},
+	    )
+	);
+    }
+
+    return $channel_pba;
+}
+
+sub add_dbad_to_dba : PRIVATE {
+
+    my ( $self, $dbad, $dba ) = @_;
+
+    my $found;
+    if ( my $preexisting_dbads = $dba->getDerivedBioAssayData() ) {
+	my $new_id = $dbad->getIdentifier();
+	$found = first { $_->getIdentifier() eq $new_id }
+	    @$preexisting_dbads;
+    }
+    $dba->addDerivedBioAssayData( $dbad ) unless $found;
+
+    return;
+}
+    
+sub add_datafiles : PRIVATE {
+
+    my ( $self, @datafiles ) = @_;
+
+    foreach my $file ( @datafiles ) {
+	$datafiles_cache{ ident $self }{ $file->get_path() } = $file;
+    }
+
+    return;
+} 
+
+sub get_datafiles {
+
+    my ( $self ) = @_;
+
+    # Return sorted alphabetically by name for the moment FIXME.
+    return [
+	map { $datafiles_cache{ ident $self }{$_} }
+        sort keys %{ $datafiles_cache{ ident $self } }
+    ];
+}
+
+sub get_sdrf_filehandle {
+
+    my ( $self ) = @_;
+
+    unless ( $sdrf_filehandle{ident $self} ) {
+	if ( my $file = $self->get_sdrf() ) {
+	    open (my $fh, '<', $file)
+		or croak("Error opening SDRF for reading: $!\n");
+	    $sdrf_filehandle{ident $self} = $fh;
+	}
+	else {
+	    confess("Error: No SDRF filename given.");
+	}
+    }
+    return $sdrf_filehandle{ident $self};
+}
+
+sub get_row_parser : PRIVATE {
+
+    my ( $self ) = @_;
+
+    unless ( $row_parser{ident $self} ) {
+	$row_parser{ident $self} = $self->parse_header()
+	    or die("Error: Unable to generate row-level parser.");
+    }
+    return $row_parser{ident $self}
+}
+
+sub get_filename : RESTRICTED {
+
+    my ( $self ) = @_;
+    
+    return $self->get_sdrf();
+}
+    
+sub simplify_single_channels : PRIVATE {
+
+    # Snip out the extra PBAs used for two-colour coding for the
+    # single-channel hybs.
+    my ( $self ) = @_;
+
+    foreach my $hyb_pba ( @{ $self->pba_bag() } ) {
+
+	next unless ( scalar( @{ $hyb_pba->getChannels() || [] } ) == 1 );
+
+	BAT:
+	foreach my $bat ( @{ $hyb_pba->getBioAssayTreatments() || [] } ) {
+	    my $ch_pba = $bat->getTarget();
+
+	    # If scans were never generated, we could have just the hyb PBA.
+	    next BAT if ( $ch_pba->getIdentifier() eq $hyb_pba->getIdentifier()
+			      && $ch_pba->getBioAssayCreation() );
+
+	    my $mrg_pba;
+	    if ( my $ch_bats = $ch_pba->getBioAssayTreatments() ) {
+		warn ("WARNING: discarding all but first BioAssayTreatment.")
+		    if (scalar(@$ch_bats) > 1 );
+		if ( scalar(@$ch_bats) ) {
+		    $mrg_pba = $ch_bats->[0]->getTarget();
+		}
+	    }
+
+	    # Repoint the merged PBA data to the hyb PBA. Note that
+	    # this overwrites any BATs associated with $hyb_pba; this
+	    # was necessary to allow clean deletion of $ch_pba (don't
+	    # ask me why; I assume a reference needs weakening
+	    # somewhere, presumably in the $hyb_pba BAT).
+	    if ( $mrg_pba ) {
+		my ($images, $protocolapps, $mbas, $dbas)
+		    = $self->strip_pba_for_repointing($mrg_pba);
+		$self->repoint_pba_data(
+		    $images,
+		    $protocolapps,
+		    $mbas,
+		    $dbas,
+		    $hyb_pba,
+		    $mrg_pba->getName(),
+		);
+	    }
+
+	    # Also strip the channel PBA. This appears to remove
+	    # references to $mrg_pba.
+	    $self->strip_pba_for_repointing($ch_pba);
+
+	    # Delete the old PBA objects from the bags.
+	    $self->extended_pba_bag($ch_pba->getName(), 'delete');
+#		    or die("Error: Unable to delete unwanted channel PBA: "
+#			       . $ch_pba->getName());
+	    if ( $mrg_pba ) {
+		$self->extended_pba_bag($mrg_pba->getName(), 'delete');
+#		    or die("Error: Unable to delete unwanted merging PBA: "
+#			       . $mrg_pba->getName());
+	    }
+
+	    # We remove the identifiers; this will cause an exception
+	    # if there's a problem with these objects hanging around
+	    # later (better that than silent failure).
+	    $ch_pba->setIdentifier("");
+	    $mrg_pba->setIdentifier("") if $mrg_pba;
+	}
+    }
+
+    return;
+}
+
+sub strip_pba_for_repointing : PRIVATE {
+
+    my ( $self, $pba ) = @_;
+
+    my (%oldimages, %oldprotocolapps, %oldmbas, %olddbas);
+
+    # Gather associations from one hyb to be transferred to another.
+    
+    # Images first.
+    foreach my $image ( @{ $pba->getPhysicalBioAssayData() || [] } ) {
+	$oldimages{ $image->getIdentifier() } = $image;
+    }
+    $pba->setPhysicalBioAssayData([]);
+
+    # Process Images associated with the BAT (should be
+    # the same, but you never know).
+    my $bat = $pba->getBioAssayTreatments()->[0];
+    if ( $bat ) {
+	if ( $bat->isa('Bio::MAGE::BioAssay::ImageAcquisition') ) {
+	    foreach my $image ( @{ $bat->getImages() || [] } ) {
+		$oldimages{ $image->getIdentifier() } = $image;
+	    }
+	}
+
+	# ProtocolApps.
+	foreach my $pa ( @{ $bat->getProtocolApplications() || [] } ) {
+	    $oldprotocolapps{ $pa } = $pa;
+	}
+    }
+
+    $pba->setBioAssayTreatments([]);
+    
+    # MBAs.
+    foreach my $mba ( @{ $self->mba_bag() } ) {
+	if ( my $fext = $mba->getFeatureExtraction() ) {
+	    if ( my $pba_ref = $fext->getPhysicalBioAssaySource() ) {
+		if ( $pba_ref->getIdentifier() eq $pba->getIdentifier() ) {
+		    $oldmbas{ $mba->getIdentifier() } = $mba;
+		}
+	    }
+	}
+    }
+
+    # DBAs (e.g. in the absence of raw data this can happen).
+    foreach my $dba ( @{ $self->dba_bag() } ) {
+	if ( my $maps = $dba->getDerivedBioAssayMap() ) {
+	    foreach my $map ( @$maps ) {
+		my @new_sources;
+		if ( my $bas = $map->getSourceBioAssays() ) {
+		    foreach my $ba ( @$bas ) {
+			if ( $ba->getIdentifier() eq $pba->getIdentifier() ) {
+			    $olddbas{ $dba->getIdentifier() } = $dba;
+			}
+			else {
+			    push @new_sources, $ba;
+			}
+		    }
+		}
+
+		# Break the link between $pba and map.
+		$map->setSourceBioAssays(\@new_sources);
+	    }
+	}
+    }
+
+    $pba->setBioAssayFactorValues([]);
+    $pba->setChannels([]);
+
+    return (
+	[values %oldimages],
+	[values %oldprotocolapps],
+	[values %oldmbas],
+	[values %olddbas],
+    );
+}
+
+sub repoint_pba_data : PRIVATE {
+
+    my ( $self,
+	 $oldimages,
+	 $oldprotocolapps,
+	 $oldmbas,
+	 $olddbas,
+	 $pba,
+	 $name ) = @_;
+
+    my $identifier = $self->generate_id_template($name) . '.ImageAcquisition';
+    my $bat = Bio::MAGE::BioAssay::ImageAcquisition->new(
+	identifier       => $identifier,
+	target           => $pba,
+	name             => $name,
+#	physicalBioAssay => $pba,
+    );
+
+    # Reattach any Images moved from old BAT.
+    if ( scalar ( @{ $oldimages || [] } ) ) {
+	$self->add_images_to_pba( $oldimages, $pba );
+    }
+
+    # Reattach old protocolapps from old BAT.
+    foreach my $pa ( @{ $oldprotocolapps || [] } ) {
+	$self->add_protocolapp_to_treatment( $pa, $bat );
+    }
+
+    # Repoint any MBAs previously pointing to $hyb_pba.
+    if ( scalar ( @{ $oldmbas || [] } ) ) {
+	foreach my $mba ( @$oldmbas ) {
+	    my $fext = $mba->getFeatureExtraction();
+	    $fext->setPhysicalBioAssaySource($pba);
+	}
+    }
+
+    # Repoint any DBAs previously pointing to $hyb_pba.
+    if ( scalar ( @{ $olddbas || [] } ) ) {
+	foreach my $dba ( @$olddbas ) {
+	    my $maps = $dba->getDerivedBioAssayMap();
+	    foreach my $map ( @$maps ) {
+		$self->add_basource_to_map( $pba, $map );
+	    }
+	}
+    }
+
+    $pba->setBioAssayTreatments( [ $bat ] );
+
+    return;
+}
+
+sub propagate_fvs_to_datafiles : PRIVATE {
+
+    my ( $self ) = @_;
+
+    FILE:
+    foreach my $file ( @{ $self->get_datafiles || [] } ) {
+
+	my $badata;
+	next FILE unless ( $badata = $file->get_mage_badata() );
+
+	my $badim;
+	next FILE unless ( $badim = $badata->getBioAssayDimension() );
+
+	foreach my $bioassay ( @{ $badim->getBioAssays() || [] } ) {
+
+	    FACTORVALUE:
+	    foreach my $fv ( @{ $bioassay->getBioAssayFactorValues() || [] } ) {
+		my ( $category, $value );
+		if ( my $oe = $fv->getValue() ) {
+		    $category = $oe->getCategory();
+		    $value    = $oe->getValue();
+		}
+		elsif ( my $measurement = $fv->getMeasurement() ) {
+		    my $unit = $measurement->getUnit();
+		    $category = $unit
+			      ? ( $unit->getUnitNameCV() || $unit->getUnitName() )
+			      : 'UNKNOWN';
+		    $value    = $measurement->getValue();
+		}
+		else {
+		    next FACTORVALUE;
+		}
+		$file->add_factor_value( $category, $value );
+	    }
+	}
+    }
+
+    return;
+}
+
+sub add_native_filetypes : PRIVATE {
+    my ( $self, %type ) = @_;
+
+    foreach my $filename ( keys %type ) {
+        $native_filetypes{ident $self}{$filename} = $type{$filename};
+    }
+    return $native_filetypes{ident $self};
+}
+
+sub get_blank_regexp {
+
+    # Allow access to the RE we're using to detect blanks.
+    my ( $self ) = @_;
+
+    return $BLANK;
+}
+
+1;
+
+__DATA__
+
+    header:                    material_section(?)
+                               edge(s?)
+                               assay_or_hyb(?)
+                               edge(s?)
+                               data_section(?)
+                               factor_value(s?)
+                               end_of_line
+
+                                   { $return = sub{
+
+                                          my @objects;
+
+                                          # Reset some global variables.
+                                          $::channel           = 'Unknown';
+                                          $::array_accession   = undef;
+                                          $::previous_material = undef;
+                                          $::previous_event    = undef;
+                                          $::previous_data     = undef;
+                                          @::protocolapp_list  = ();
+
+                                          # Generate the objects.
+                                          foreach my $sub (@{$item[1][0]},
+                                                           @{$item[2]},
+                                                           @{$item[3]},
+                                                           @{$item[4]},
+                                                           @{$item[5][0]},
+                                                           @{$item[6]}){
+                                              if (ref $sub eq 'CODE') {
+                                                  my @obj = &{ $sub };
+                                                  push @objects, @obj;
+                                              }
+                                              else {
+                                                  die("Error: Grammar rule return value not a CODE ref: $sub");
+                                              }
+                                          }
+
+                                          if ( scalar @_ ) {
+                                              die("Error: SDRF row not completely parsed: " . join("\n", @_));
+                                          }
+
+                                          # Post-process BioAssays and FactorValues.
+                                          my @bioassays
+                                              = grep { $_ && $_->isa('Bio::MAGE::BioAssay::BioAssay') }
+                                                  @objects;
+                                          my @factorvals
+                                              = grep { $_ && $_->isa('Bio::MAGE::Experiment::FactorValue') }
+                                                  @objects;     
+                                          $::sdrf->add_factorvals_to_bioassays(
+                                              \@factorvals,
+                                              \@bioassays,
+                                          );
+
+                                          return \@objects;
+                                     };
+                                   }
+
+                             | <error: Invalid header; unparseable sequence starts here: $text>
+
+    end_of_line:               <skip:'[ \x{0}\r]*'> /\Z/
+
+    material_section:          material edge_and_material(s?)
+
+                               { $return = [$item[1], map { @{ $_ } } @{$item[2]}] }
+
+    data_section:              assay_or_data edge_and_assay_or_data(s?)
+
+                               { $return = [$item[1], map { @{ $_ } } @{$item[2]}] }
+
+    edge_and_material:         edge(s?) material { $return = [ @{ $item[1] }, $item[2] ] }
+
+    edge_and_assay_or_data:    edge(s?) assay_or_data    { $return = [ @{ $item[1] }, $item[2] ] }
+
+    assay_or_data:             event
+                             | data
+
+    edge:                      factor_value
+                             | protocol
+
+    material:                  source
+                             | sample
+                             | extract
+                             | labeled_extract
+
+    event:                     scan
+                             | normalization
+
+    data:                      raw_data
+                             | derived_data
+
+    source_name:               /Source *Names?/i
+
+    source:                    source_name source_attribute(s?)
+
+                                   { $return = sub{
+                                          my $name = shift;
+                                          my $obj  = $::sdrf->create_source($name);
+                                          foreach my $sub (@{$item[2]}){
+                                              unshift( @_, $obj ) and
+                                                  &{ $sub } if (ref $sub eq 'CODE');  
+                                          }
+                                          $::previous_material = $obj if $obj;
+                                          return $obj; 
+                                     };
+                                   }
+
+    sample_name:               /Sample *Names?/i
+
+    sample:                    sample_name material_attribute(s?)
+
+                                   { $return = sub{
+                                          my $name = shift;
+                                          my $obj  = $::sdrf->create_sample(
+                                              $name,
+                                              $::previous_material,
+                                              \@::protocolapp_list,
+                                          );
+                                          @::protocolapp_list = () if $obj;
+                                          foreach my $sub (@{$item[2]}){
+                                              unshift( @_, $obj ) and
+                                                  &{ $sub } if (ref $sub eq 'CODE');  
+                                          }
+                                          $::previous_material = $obj if $obj;
+                                          return $obj; 
+                                     };
+                                   }
+
+    extract_name:              /Extract *Names?/i
+
+    extract:                   extract_name material_attribute(s?)
+
+                                   { $return = sub{
+                                          my $name = shift;
+                                          my $obj  = $::sdrf->create_extract(
+                                              $name,
+                                              $::previous_material,
+                                              \@::protocolapp_list,
+                                          );
+                                          @::protocolapp_list = () if $obj;
+                                          foreach my $sub (@{$item[2]}){
+                                              unshift( @_, $obj ) and
+                                                  &{ $sub } if (ref $sub eq 'CODE');  
+                                          }
+                                          $::previous_material = $obj if $obj;
+                                          return $obj; 
+                                     };
+                                   }
+
+    labeled_extract_name:      /Labell?ed *Extract *Names?/i
+
+    labeled_extract:           labeled_extract_name labeled_extract_attribute(s?)
+
+                                   { $return = sub{
+                                          my $name = shift;
+                                          my $obj  = $::sdrf->create_labeled_extract(
+                                              $name,
+                                              $::previous_material,
+                                              \@::protocolapp_list,
+                                          );
+                                          @::protocolapp_list = () if $obj;
+                                          foreach my $sub (@{$item[2]}){
+                                              unshift( @_, $obj ) and
+                                                  &{ $sub } if (ref $sub eq 'CODE');  
+                                          }
+                                          $::previous_material = $obj if $obj;
+                                          return $obj; 
+                                     };
+                                   }
+
+    source_attribute:          material_attribute
+                             | provider
+
+    labeled_extract_attribute: material_attribute
+                             | label
+
+    material_attribute:        characteristic
+                             | materialtype
+                             | description
+                             | comment
+
+    characteristic_heading:    /Characteristics?/i
+
+    characteristic:            characteristic_heading
+                               <skip:' *'> bracket_term
+                               <skip:' *\x{0} *'> char_fv_attribute(?)
+
+                                   { $return = sub {
+                                         my $material = shift;
+
+                                         my $char;
+                                         if ( ref $item[5][0] eq 'CODE' ) {
+
+                                             # Add a unit (nested OE) to the material.
+                                             my $oe_meas;
+                                             ( $char, $oe_meas )
+                                                 = $::sdrf->create_nested_oe_measurement($item[3], shift);
+                                             unshift( @_, $oe_meas );
+                                             my $unit = &{ $item[5][0] };
+                                             $::sdrf->add_char_to_material($char, $material);
+                                         }
+                                         else {
+
+                                             # Value
+                                             my @args = shift;
+
+                                             if ( ref $item[5][0] eq 'ARRAY' ) {
+
+                                                 # Term Source
+                                                 push @args, shift;
+
+                                                 # Accession
+                                                 push @args, ($item[5][0][1] eq 'term_accession')
+                                                             ? shift
+                                                             : undef;
+                                             }
+                                             else {
+
+                                                 # No term source given
+                                                 push @args, undef, undef;
+                                             }
+
+                                             $char = $::sdrf->create_ontologyentry($item[3], @args);
+                                             $::sdrf->add_char_to_material($char, $material);
+                                         }
+                                         return $char;
+                                     };
+                                   }
+
+    factor_value_heading:      /Factor *Values?/i
+
+    factor_value:              factor_value_heading
+                               <skip:' *'> bracket_term parens_term(?)
+                               <skip:' *\x{0} *'> char_fv_attribute(?)
+
+                                   { $return = sub {
+                                         my ($fv, $ef);
+                                         if ( ref $item[6][0] eq 'CODE' ) {
+                                             my $value = shift;
+
+                                             # Attach the unit to the measurement.
+                                             unshift( @_, undef );
+                                             my $unit = &{ $item[6][0] };
+
+                                             ($fv, $ef) = $::sdrf->create_factorvalue_measurement(
+                                                 $item[3],
+                                                 $item[4][0],
+                                                 $value,
+                                                 $unit,
+                                             );
+
+                                             # This has to be done after adding the unit.
+                                             $::sdrf->add_factorvalue_to_factor( $fv, $ef ) if $fv;
+                                         }
+                                         else {
+
+                                             # Value
+                                             my @args = shift;
+
+                                             if ( ref $item[6][0] eq 'ARRAY' ) {
+
+                                                 # Term Source
+                                                 push @args, shift;
+
+                                                 # Accession
+                                                 push @args, ($item[6][0][1] eq 'term_accession')
+                                                             ? shift
+                                                             : undef;
+                                             }
+                                             else {
+
+                                                 # No term source given
+                                                 push @args, undef, undef;
+                                             }
+                                             $fv = $::sdrf->create_factorvalue_value(
+                                                 $item[3],
+                                                 $item[4][0],
+                                                 @args,
+                                             );
+                                         }
+                                         return $fv;
+                                     };
+                                   }
+
+    char_fv_attribute:         unit
+                             | termsource
+
+    bracket_term:              /\A \[ [ ]* ([^\x{0}\]]+?) [ ]* \]/xms
+
+                                   { $return = $1 }
+
+    parens_term:               /\A \( [ ]* ([^\x{0}\)]+?) [ ]* \)/xms
+
+                                   { $return = $1 }
+
+    namespace_term:            /\A : ([^\x{0}]+)/xms
+
+                                   { $return = $1 }
+
+    term_source_ref:           /Term *Source *REFs?/i
+
+    termsource:                term_source_ref
+                               <skip:' *'> namespace_term(?)
+                               <skip:' *\x{0} *'> term_accession(?)
+
+                                   { $return = [ $item[3][0], $item[5][0] ] } # FIXME add namespace_term support
+
+    term_accession:            /Term *Accession *Numbers?/i
+
+                                   { $return = 'term_accession'; }
+
+    provider_heading:          /Providers?/i
+
+    provider:                  provider_heading comment(s?)
+
+                                   { $return = sub {
+                                         my $source = shift;
+                                         my @names  = split /\s*;\s*/, shift;
+
+                                         # At least one entry needed to consume @_ correctly.
+                                         @names = q{} unless scalar @names;
+
+                                         # Temporary store for multiple uses of @_
+                                         my @args = @_;
+
+                                         my @obj_list;
+                                         foreach my $name ( @names ) {
+                                             my $obj = $::sdrf->create_provider(
+                                                 $name,
+                                                 $source,
+                                             );
+                                             push @obj_list, $obj;
+                                             @_ = @args;
+                                             foreach my $sub (@{$item[2]}){
+                                                 unshift( @_, $obj ) and
+                                                     &{ $sub } if (ref $sub eq 'CODE');
+                                             }
+                                         }
+                                         return @obj_list;
+                                     };
+                                   }
+
+    materialtype_heading:      /Material *Types?/i
+
+    materialtype:              materialtype_heading termsource(?)
+
+                                   { $return = sub {
+                                         my $material = shift;
+
+                                         # Value
+                                         my @args = shift;
+
+                                         if ( ref $item[2][0] eq 'ARRAY' ) {
+
+                                             # Term Source
+                                             push @args, shift;
+
+                                             # Accession
+                                             push @args, ($item[2][0][1] eq 'term_accession')
+                                                         ? shift
+                                                         : undef;
+                                         }
+                                         else {
+
+                                             # No term source given
+                                             push @args, undef, undef;
+                                         }
+                                         my $type = $::sdrf->create_ontologyentry('MaterialType', @args);
+                                         $::sdrf->set_material_type( $material, $type ) if ($material && $type);
+                                         return $type;
+                                     };
+                                   }
+
+    label_heading:             /Labels?/i
+
+    label:                     label_heading termsource(?)
+
+                                   { $return = sub {
+                                         my $labeled_extract = shift;
+                                         $::channel = shift;
+
+                                         my @args;
+
+                                         if ( ref $item[2][0] eq 'ARRAY' ) {
+
+                                             # Term Source
+                                             push @args, shift;
+
+                                             # Accession
+                                             push @args, ($item[2][0][1] eq 'term_accession')
+                                                         ? shift
+                                                         : undef;
+                                         }
+                                         else {
+
+                                             # No term source given
+                                             push @args, undef, undef;
+                                         }
+                                         my $label = $::sdrf->create_label($::channel, @args, $labeled_extract);
+                                         return $label;
+                                     };
+                                   }
+
+    description:               /Descriptions?/i
+
+                                   { $return = sub {
+                                         my $describable = shift;
+                                         return $::sdrf->create_description(shift, $describable);
+                                     };
+                                   }
+
+    comment_heading:           /Comments?/i
+
+    comment:                   comment_heading <skip:' *'> bracket_term
+
+                                   { $return = sub {
+                                         my $extendable = shift;
+                                         return $::sdrf->create_nvt($item[3], shift, undef, $extendable);
+                                     };
+                                   }
+
+    protocol_ref:              /Protocol *REFs?/i
+
+    protocol:                  protocol_ref
+                               <skip:' *'> namespace_term(?)
+                               <skip:' *\x{0} *'> termsource(?)
+                               protocol_attributes(s?)
+
+                                   { $return = sub{
+
+                                          # Name, namespace_term
+                                          my @args = (shift, $item[3][0]);
+
+                                          if ( ref $item[5][0] eq 'ARRAY' ) {
+
+                                              # Term Source
+                                              push @args, shift;
+
+                                              # Accession
+                                              push @args, ($item[5][0][1] eq 'term_accession')
+                                                          ? shift
+                                                          : undef;
+                                          }
+                                          else {
+
+                                              # No term source given
+                                              push @args, undef, undef;
+                                          }
+
+                                          my $obj  = $::sdrf->create_protocolapplication(@args);
+
+                                          # Add to the global ProtApp list immediately so that
+                                          # parameter units can be sorted out without
+                                          # having to pass $obj through.
+                                          push(@::protocolapp_list, $obj) if $obj;
+
+                                          foreach my $sub (@{$item[6]}){
+                                              unshift( @_, $obj ) and
+                                                  &{ $sub } if (ref $sub eq 'CODE');  
+                                          }
+                                          return $obj; 
+                                     };
+                                   }
+
+    protocol_attributes:       parameter
+                             | performer
+                             | date
+                             | comment
+
+    parameter_heading:         /Parameter *Values?/i
+
+    parameter:                 parameter_heading
+                               <skip:' *'> bracket_term
+                               <skip:' *\x{0} *'> parameter_attributes(s?)
+
+                                   { $return = sub {
+                                         my $protocolapp = shift;
+                                         my $value       = shift;
+                                         my $obj = $::sdrf->create_parametervalue($item[3], $value, $protocolapp);
+                                         foreach my $sub (@{$item[5]}){
+                                             if ( ref $sub eq 'CODE' ) {
+
+                                                  # Measurement, Comment
+                                                  unshift( @_, $obj );
+                                                  my $attr = &{ $sub };
+
+                                                  if ( defined $attr
+                                                      && $attr->isa('Bio::MAGE::Measurement::Unit') ) {
+                                                      $::sdrf->add_unit_to_thing( $attr, $obj );
+                                                  }
+                                             }
+                                             elsif ( ref $sub eq 'ARRAY' ) {
+
+                                                 # Value
+                                                 my @args;
+
+                                                 # Term Source
+                                                 push @args, shift;
+
+                                                 # Accession
+                                                 push @args, ($sub->[1] eq 'term_accession')
+                                                             ? shift
+                                                             : undef;
+
+                                                 $::sdrf->add_value_to_parameter(
+                                                     $obj,
+                                                     @args,
+                                                 );
+                                             }
+                                         }
+                                         return $obj;
+                                     };
+                                   }
+
+    parameter_attributes:      unit
+                             | termsource
+                             | comment
+
+    unit_heading:              /Unit/i
+
+    unit:                      unit_heading
+                               <skip:' *'> bracket_term
+                               <skip:' *\x{0} *'> termsource(?)
+
+                                   { $return = sub {
+                                         my $value_having_unit = shift;
+
+                                         # Unit name
+                                         my @args = shift;
+
+                                         if ( ref $item[5][0] eq 'ARRAY' ) {
+
+                                             # Term Source
+                                             push @args, shift;
+
+                                             # Accession
+                                             push @args, ($item[5][0][1] eq 'term_accession')
+                                                         ? shift
+                                                         : undef;
+                                         }
+                                         else {
+
+                                             # No term source given
+                                             push @args, undef, undef;
+                                         }
+
+                                         my $unit;
+                                         if (   defined($value_having_unit)
+                                             && $value_having_unit->isa('Bio::MAGE::Description::OntologyEntry') ) {
+                                             $unit = $::sdrf->create_nested_oe_unit(
+                                                 $item[3],
+                                                 @args,
+                                                 $value_having_unit,
+                                             );
+                                         }
+                                         else {
+                                             $unit = $::sdrf->create_unit(
+                                                 $item[3],
+                                                 @args,
+                                             );
+                                         }
+                                         return $unit;
+                                     };
+                                   }
+
+    performer_heading:         /Performers?/i
+
+    performer:                 performer_heading comment(s?)
+
+                                   { $return = sub {
+                                         my $protocolapp = shift;
+                                         my @names       = split /\s*;\s*/, shift;
+
+                                         # At least one entry needed to consume @_ correctly.
+                                         @names = q{} unless scalar @names;
+
+                                         # Temporary store for multiple uses of @_
+                                         my @args = @_;
+
+                                         my @obj_list;
+                                         foreach my $name ( @names ) {
+                                             my $obj = $::sdrf->create_performer(
+                                                 $name,
+                                                 $protocolapp,
+                                             );
+                                             push @obj_list, $obj;
+                                             @_ = @args;
+                                             foreach my $sub (@{$item[2]}){
+                                                 unshift( @_, $obj ) and
+                                                     &{ $sub } if (ref $sub eq 'CODE');
+                                             }
+                                         }
+                                         return @obj_list;
+                                     };
+                                   }
+
+    date:                      /Dates?/i
+
+                                   { $return = sub {
+                                         my $protocolapp = shift;
+                                         my $date        = shift;
+                                         $::sdrf->add_date_to_protocolapp( $date, $protocolapp );
+                                         return $date;
+                                     };
+                                   }
+
+    array_design:              array_design_file
+                             | array_design_ref
+
+    array_design_file_heading: /Array *Design *Files?/i
+
+    array_design_file:         array_design_file_heading comment(s?)
+
+                                   { die "ADFs not yet supported" }
+
+    array_design_ref_heading:  /Array *Design *REFs?/i
+
+    array_design_ref:          array_design_ref_heading
+                               <skip:' *'> namespace_term(?)
+                               <skip:' *\x{0} *'> termsource(?)
+                               comment(s?)
+
+                                   { $return = sub {
+                                         my $hybridization  = shift;
+                                         $::array_accession = shift;
+
+                                         # array_accession, namespace_term
+                                         my @args = ( $::array_accession, $item[3][0] );
+
+                                         if ( ref $item[5][0] eq 'ARRAY' ) {
+
+                                             # Term Source
+                                             push @args, shift;
+
+                                             # Accession
+                                             push @args, ($item[5][0][1] eq 'term_accession')
+                                                         ? shift
+                                                         : undef;
+                                         }
+                                         else {
+
+                                             # No term source given
+                                             push @args, undef, undef;
+                                         }
+
+                                         my $obj = $::sdrf->create_array(@args, $hybridization);
+                                         foreach my $sub (@{$item[6]}){
+                                             unshift( @_, $obj ) and
+                                                 &{ $sub } if (ref $sub eq 'CODE');  
+                                         }
+                                         return $obj;
+                                     };
+                                   }
+
+    assay_or_hyb:              assay
+                             | hybridization
+
+    hybridization_name:        /Hybridi[sz]ation *Names?/i
+
+    hybridization:             hybridization_name hybrid_attribute(s?)
+
+                                   { $return = sub {
+                                         my $name = shift;
+                                         my $obj  = $::sdrf->create_hybridization(
+                                             $name,
+                                             $::previous_material,
+                                             \@::protocolapp_list,
+                                             $::channel,
+                                         );
+                                         @::protocolapp_list = () if $obj;
+                                         foreach my $sub (@{$item[2]}){
+                                             unshift( @_, $obj ) and
+                                                 &{ $sub } if (ref $sub eq 'CODE');  
+                                         }
+                                         $::previous_event = $obj if $obj;
+                                         return $obj;
+                                      };
+                                    }
+
+    hybrid_attribute:          array_design
+                             | technology_type
+                             | comment
+
+    assay_name:                /Assay *Names?/i
+
+    assay:                     assay_name technology_type comment(?)
+
+                                   { $return = sub {
+                                         my $name = shift;
+                                         my $obj  = $::sdrf->create_assay(
+                                             $name,
+                                             $::previous_material,
+                                             \@::protocolapp_list,
+                                             $::channel,
+                                         );
+                                         @::protocolapp_list = () if $obj;
+                                         foreach my $sub ($item[2], $item[3][0]){
+                                             unshift( @_, $obj ) and
+                                                 &{ $sub } if (ref $sub eq 'CODE');  
+                                         }
+
+                                         $::previous_event = $obj if $obj;
+                                         return $obj;
+                                      };
+                                    }
+
+    technol_type_heading:      /Technology *Types?/i
+
+    technology_type:           technol_type_heading termsource(?)
+
+                                   { $return = sub {
+                                         my $assay = shift;
+
+                                         # Value
+                                         my @args = shift;
+
+                                         if ( ref $item[2][0] eq 'ARRAY' ) {
+
+                                             # Term Source
+                                             push @args, shift;
+
+                                             # Accession
+                                             push @args, ($item[2][0][1] eq 'term_accession')
+                                                         ? shift
+                                                         : undef;
+                                         }
+                                         else {
+
+                                             # No term source given
+                                             push @args, undef, undef;
+                                         }
+                                         my $type = $::sdrf->create_ontologyentry('TechnologyType', @args);
+                                         $::sdrf->set_technology_type($assay, $type) if ($assay && $type);
+                                         return $type;
+                                     };
+                                   }
+
+    scan_name:                 /Scan *Names?/i
+
+    scan:                      scan_name scan_attribute(s?)
+
+                                   { $return = sub {
+                                         my $name = shift;
+                                         my ($obj, $channel_obj, @ret_obj) = $::sdrf->create_scan(
+                                             $name,
+                                             $::previous_event,
+                                             \@::protocolapp_list,
+                                             $::channel,
+                                             $::previous_material,
+                                         );
+                                         @::protocolapp_list = () if $obj;
+                                         $::previous_event = $obj if $obj;
+                                         push @ret_obj, $obj if $obj;
+                                         push @ret_obj, $channel_obj if $channel_obj;
+                                         foreach my $sub (@{$item[2]}){
+                                             unshift( @_, $obj ) and
+                                                 push (@ret_obj, &{ $sub }) if (ref $sub eq 'CODE');  
+                                         }
+                                         return @ret_obj; # Includes channel and merged PBAs; hybs where autogenerated.
+                                      };
+                                    }
+
+    scan_attribute:            comment
+
+    normalization_name:        /Normali[sz]ation *Names?/i
+
+    normalization:             normalization_name norm_attribute(s?)
+
+                                   { $return = sub {
+                                         my ( $obj, $apps, @old_bioassays ) = $::sdrf->create_normalization(
+                                             shift,
+                                             $::previous_event,
+                                             \@::protocolapp_list,
+                                             $::channel,
+                                             $::previous_material,
+                                         );
+                                         @::protocolapp_list = @{ $apps || [] } if $obj;
+                                         foreach my $sub (@{$item[2]}){
+                                             unshift( @_, $obj ) and
+                                                 &{ $sub } if (ref $sub eq 'CODE');  
+                                         }
+                                         $::previous_event = $obj if $obj;
+                                         return ( $obj, @old_bioassays );    # DBA; MBA, PBAs if autogenerated.
+                                     };
+                                   }
+
+    norm_attribute:            comment
+
+    raw_data:                  image
+                             | array_data
+                             | array_data_matrix
+
+    derived_data:              derived_array_data
+                             | derived_array_data_matrix
+
+    array_data_file:           /Array *Data *Files?/i
+
+    array_data:                array_data_file comment(s?)
+
+                                   { $return = sub {
+                                         my ($obj, $data, @oldpbas) = $::sdrf->create_raw_data_file(
+                                             shift,
+                                             $::previous_event,
+                                             \@::protocolapp_list,
+                                             $::array_accession,
+                                             $::channel,
+                                             $::previous_material,
+                                         );
+                                         @::protocolapp_list = () if $obj;
+                                         foreach my $sub (@{$item[2]}){
+                                             unshift( @_, $obj ) and
+                                                 &{ $sub } if (ref $sub eq 'CODE');  
+                                         }
+                                         $::previous_event = $obj  if $obj;
+                                         $::previous_data  = $data if $data;
+                                         return ($obj, @oldpbas);    # MBA; PBAs if autogenerated.
+                                     };
+                                   }
+
+    derived_array_data_file:   /Derived *Array *Data *Files?/i
+
+    derived_array_data:        derived_array_data_file comment(s?)
+
+                                   { $return = sub {
+                                         my ($obj, @old_bioassays) = $::sdrf->create_normalized_data(
+                                             shift,
+                                             $::previous_event,
+                                             \@::protocolapp_list,
+                                             $::previous_data,
+                                             $::array_accession,
+                                             $::channel,
+                                             $::previous_material,
+                                         );
+                                         @::protocolapp_list = () if $obj;
+                                         foreach my $sub (@{$item[2]}){
+                                             unshift( @_, $obj ) and
+                                                 &{ $sub } if (ref $sub eq 'CODE');  
+                                         }
+                                         $::previous_data = $obj if $obj;
+                                         return ($obj, @old_bioassays);    # DBAData
+                                     };
+                                   }
+
+    array_data_matrix_file:    /Array *Data *Matrix *Files?/i
+
+    array_data_matrix:         array_data_matrix_file comment(s?)
+
+                                   { $return = sub {
+                                         my ($obj, @old_pbas) = $::sdrf->create_raw_data_matrix(
+                                             shift,
+                                             \@::protocolapp_list,
+                                             $::array_accession,
+                                         );
+                                         @::protocolapp_list = () if $obj;
+                                         foreach my $sub (@{$item[2]}){
+                                             unshift( @_, $obj ) and
+                                                 &{ $sub } if (ref $sub eq 'CODE');  
+                                         }
+                                         $::previous_data = $obj if $obj;
+                                         return ($obj, @old_pbas);    # MBAData; PBAs if autogenerated.
+                                     };
+                                   }
+
+    derived_array_data_matrix_file: /Derived *Array *Data *Matrix *Files?/i
+
+    derived_array_data_matrix: derived_array_data_matrix_file comment(s?)
+
+                                   { $return = sub {
+                                         my ($obj, @old_bioassays) = $::sdrf->create_norm_data_matrix(
+                                             shift,
+                                             \@::protocolapp_list,
+                                             $::array_accession,
+                                             $::previous_event,
+                                             $::channel,
+                                             $::previous_material,
+                                         );
+                                         @::protocolapp_list = () if $obj;
+                                         foreach my $sub (@{$item[2]}){
+                                             unshift( @_, $obj ) and
+                                                 &{ $sub } if (ref $sub eq 'CODE');  
+                                         }
+                                         $::previous_data = $obj if $obj;
+                                         return ($obj, @old_bioassays);    # DBAData
+                                     };
+                                   }
+
+    image_file:                /Image *Files?/i
+
+    image:                     image_file comment(s?)
+
+                                   { $return = sub {
+                                         my ( $obj, @old_pbas ) = $::sdrf->create_image(
+                                             shift,
+                                             $::previous_event,
+                                             \@::protocolapp_list,
+                                             $::channel,
+                                             $::previous_material,
+                                         );
+                                         foreach my $sub (@{$item[2]}){
+                                             unshift( @_, $obj ) and
+                                                 &{ $sub } if (ref $sub eq 'CODE');  
+                                         }
+                                         return ( $obj, @old_pbas );    # Image; hyb PBA if autogenerated.
+                                     };
+                                   }
+
diff --git a/lib/ArrayExpress/MAGETAB/TabFile.pm b/lib/ArrayExpress/MAGETAB/TabFile.pm
new file mode 100644
index 0000000..b55ffa3
--- /dev/null
+++ b/lib/ArrayExpress/MAGETAB/TabFile.pm
@@ -0,0 +1,926 @@
+#!/usr/bin/env perl
+#
+# Module providing IDF parsing functions to the ArrayExpress MAGE-TAB
+# implementation.
+#
+# Tim Rayner, 2007, EMBL-EBI Microarray Informatics Team
+#
+# $Id: TabFile.pm 2017 2008-04-01 21:24:23Z tfrayner $
+
+package ArrayExpress::MAGETAB::TabFile;
+
+use strict;
+use warnings;
+
+use Class::Std;
+use Carp;
+use List::Util qw(first);
+use Bio::MAGE qw(:ALL);
+use Text::CSV_XS;
+use Readonly;
+
+use ArrayExpress::Curator::Common qw(check_linebreaks);
+use ArrayExpress::Curator::MAGE qw(
+    make_container
+    unique_identifier
+
+    new_armanuf
+    new_array
+    new_image
+    new_biosource
+    new_biosample
+    new_extract
+    new_labeledextract
+    new_labelcompound
+    new_measuredbioassay
+    new_derivedbioassay
+    new_measuredbioassaydata
+    new_derivedbioassaydata
+    new_bioassaymap
+    new_quantitationtypemap
+    new_quantitationtype
+    new_qtd
+);
+
+use ArrayExpress::Curator::MAGE::Definitions qw(
+    $AE_LABELCOMPOUND_PREFIX
+    $AE_CHANNEL_PREFIX
+);
+
+Readonly my $BLANK => qr/\A (->)? \z/xms;
+
+## Accessor attributes.
+my %bags               : ATTR( :name<bags>,               :default<undef> );
+my %namespace          : ATTR( :name<namespace>,          :default<MAGETabulator> );
+my %authority          : ATTR( :name<authority>,          :default<ebi.ac.uk> );
+my %expt_accession     : ATTR( :name<expt_accession>,     :default<undef> );
+my %in_relaxed_mode    : ATTR( :name<in_relaxed_mode>,    :default<undef> );
+my %tabfile_eol_char   : ATTR( :name<eol_char>,           :default<undef> );
+my %source_directory   : ATTR( :name<source_directory>,   :default<undef> );
+
+#########################
+# Object initialization #
+#########################
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    # Constructor calls may pass in previously-used bags (e.g. when
+    # transferring IDF MAGE to the SDRF parser).
+    if ( defined $self->get_bags() ) {
+	$self->generate_bag_methods();
+    }
+    else{
+	$self->initialize_bags();
+    }
+
+    return;
+}
+
+##################
+# Public methods #
+##################
+
+###################
+# Private methods #
+###################
+
+sub initialize_bags : RESTRICTED {
+
+    my ( $self ) = @_;
+
+    my %bag_of;
+
+    # These differ from the tab2mage versions of the same, implemented
+    # here as OO with an eye to migrating everything to OO design in
+    # the future. Currently these methods must comply with the
+    # conventions laid down in ArrayExpress::Curator::MAGE.
+
+    # These are used in both IDF and SDRF.
+    $bag_of{termsource} = make_container( sub{ $self->new_termsource(@_)    } );
+    $bag_of{factor}     = make_container( sub{ $self->new_factor(@_)        } );
+    $bag_of{people}     = make_container( sub{ $self->new_person(@_)        } );
+    $bag_of{places}     = make_container( sub{ $self->new_organization(@_)  } );
+    $bag_of{protocol}   = make_container( sub{ $self->new_protocol(@_)      } );
+    $bag_of{parameter}  = make_container( sub{ $self->new_parameter(@_)     } );
+    $bag_of{software}   = make_container( sub{ $self->new_protocol_ware(@_) } );
+    $bag_of{hardware}   = make_container( sub{ $self->new_protocol_ware(@_) } );
+
+    # These are SDRF only.
+    $bag_of{armanuf}           = make_container( \&new_armanuf );
+    $bag_of{array}             = make_container( \&new_array );
+    $bag_of{arraydesign}       = make_container( sub{ $self->new_arraydesign(@_) } );
+    $bag_of{factorvalue}       = make_container( sub{ $self->new_factorvalue(@_) } );
+    $bag_of{source}            = make_container( \&new_biosource );
+    $bag_of{sample}            = make_container( \&new_biosample, 1 );
+    $bag_of{extract}           = make_container( \&new_extract, 1 );
+    $bag_of{labeledextract}    = make_container( \&new_labeledextract, 1 );
+    $bag_of{label}             = make_container( \&new_labelcompound );
+    $bag_of{image}             = make_container( \&new_image );
+    $bag_of{pba}               = make_container( sub{ $self->new_physicalbioassay(@_) } );
+    $bag_of{mba}               = make_container( \&new_measuredbioassay, 1 );
+    $bag_of{dba}               = make_container( \&new_derivedbioassay, 1 );
+    $bag_of{extended_pba}      = make_container( sub{ $self->new_physicalbioassay(@_) } );
+    $bag_of{datamatrix_mba}    = make_container( \&new_measuredbioassay, 1 );
+    $bag_of{datamatrix_dba}    = make_container( \&new_derivedbioassay, 1 );
+    $bag_of{mbad}              = make_container( \&new_measuredbioassaydata );
+    $bag_of{dbad}              = make_container( \&new_derivedbioassaydata );
+    $bag_of{datamatrix_mbad}   = make_container( \&new_measuredbioassaydata );
+    $bag_of{datamatrix_dbad}   = make_container( \&new_derivedbioassaydata );
+    $bag_of{bam}               = make_container( \&new_bioassaymap );
+    $bag_of{qtm}               = make_container( \&new_quantitationtypemap );
+    $bag_of{quantitationtype}  = make_container( \&new_quantitationtype );
+    $bag_of{qtd}               = make_container( \&new_qtd );
+    
+    $self->set_bags( \%bag_of );
+
+    $self->generate_bag_methods();
+
+    return \%bag_of;
+}
+
+sub generate_bag_methods : RESTRICTED {
+
+    my ( $self ) = @_;
+
+    my $bag = $self->get_bags();
+
+    {   # Create convenience methods for bag access.
+	## no critic ProhibitNoStrict ProhibitNoWarnings
+	no strict qw(refs);
+	no warnings qw(redefine);
+	## use critic ProhibitNoStrict ProhibitNoWarnings
+	while ( my ( $bagname, $sub ) = each %$bag ) {
+	    my $method = "${bagname}_bag";
+
+	    # The shift here corrects the @_ argument array.
+	    *{$method} = sub{ my $id = shift; $sub->(@_) };
+	}
+    }
+
+    return;
+}
+
+sub get_id_prefix : RESTRICTED {
+
+    my ( $self ) = @_;
+
+    return sprintf( "%s:%s", $self->get_authority(), $self->get_namespace() );
+}
+
+sub get_all_bioassays {
+
+    # Method to retrieve all bioassays of any type.
+    my ( $self ) = @_;
+
+    my @bioassays;
+
+    # Add references to all BioAssays
+    foreach my $type ( qw(pba
+			  mba
+			  dba
+			  extended_pba
+			  datamatrix_mba
+			  datamatrix_dba) ) {
+
+	push @bioassays, @{ $self->get_bags->{ $type }->() };
+    }
+
+    return(\@bioassays);
+}
+
+sub get_all_bioassaydata {
+
+    # Method to retrieve all bioassaydata of any type.
+    my ( $self ) = @_;
+
+    my @bioassaydata;
+
+    # Add references to all BioAssayData
+    foreach my $type ( qw(mbad
+			  dbad
+			  datamatrix_mbad
+			  datamatrix_dbad) ) {
+
+	push @bioassaydata, @{ $self->get_bags->{ $type }->() };
+    }
+
+    return(\@bioassaydata);
+}
+
+sub create_ontologyentry {
+
+    # Method takes two mandatory arguments, $category and $value, and
+    # four optional arguments ($termsource, $accession, $uri and
+    # $db). Returns an OE object. If termsource defined but db is not,
+    # termsource is used to look up a suitable db.
+    my ( $self, $category, $value, $termsource, $accession, $uri, $db ) = @_;
+
+    return if ( $value =~ $BLANK );
+
+    my $oe = Bio::MAGE::Description::OntologyEntry->new(
+	category          => $category,
+	value             => $value,
+    );
+
+    my $database;
+    if ( $db ) {
+	$database = $db;
+    }
+    elsif ( $termsource ) {
+
+	# We need an accession in some form for the OE to be valid. We
+	# don't just want to drop the termsource so we fake it
+	# here. N.B. this line actually holds for MO in its current form.
+	$accession ||= $value;
+
+	# If the Term Source is detectably MO, use some defaults.
+	if ( lc($termsource) eq 'mo' || $termsource =~ m/MGED ?Ontology/i ) {
+	    $uri       ||= sprintf(
+		"http://mged.sourceforge.net/ontologies/MGEDontology.php#%s",
+		$value
+# FIXME use the following if the OWLdocs ever go production-ready.
+#		"http://mged.sourceforge.net/ontologies/MOhtml/%s.html",
+#		lc($value)
+	    );
+	}
+
+	my $args;
+	if ( $self->get_in_relaxed_mode() ) {
+	    $args = {name       => $termsource,
+		     identifier => "ebi.ac.uk:Database:$termsource"};
+	}
+
+	$database = $self->termsource_bag( $termsource, $args )
+	    or croak(qq{Error: cannot find Term Source Name "$termsource"\n});
+    }
+
+    if ( $database ) {
+
+	# Create the actual DB entry.
+	my $dbentry = Bio::MAGE::Description::DatabaseEntry->new(
+	    accession => $accession,
+	    URI       => $uri,
+	    database  => $database,
+	);
+	$oe->setOntologyReference( $dbentry );
+    }
+
+    return $oe;
+}
+    
+sub new_termsource : RESTRICTED {
+
+    my ( $self, $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::Description::Database->new(
+	identifier => $args->{identifier},
+    ) unless $obj;
+
+    if ( $args->{name}    && ! $obj->getName() ) {
+	$obj->setName(    $args->{name}    );
+    }
+
+    if ( $args->{version} && ! $obj->getVersion() ) {
+	$obj->setVersion( $args->{version} );
+    }
+
+    if ( $args->{uri}     && ! $obj->getURI() ) {
+	$obj->setURI(     $args->{uri}     );
+    }
+
+    return $obj;
+}
+
+sub new_factor : RESTRICTED {
+    
+    my ( $self, $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::Experiment::ExperimentalFactor->new() unless $obj;
+
+    # Identifier.
+    unless ( $obj->getIdentifier() ) {
+	my $identifier
+	    = sprintf("%s:%s.%s.%s.ExperimentalFactor",
+		      $self->get_id_prefix(),
+		      $self->get_expt_accession(),
+		      unique_identifier(),
+		      $args->{type});
+	$obj->setIdentifier($identifier);
+    }
+
+    # Name.
+    $obj->setName( $args->{name} ) unless $obj->getName();
+
+    # Category OE.
+    my $oe = $self->create_ontologyentry(
+	'ExperimentalFactorCategory',
+	$args->{type},
+	$args->{termsource},
+	$args->{termaccession},
+    );
+    $obj->setCategory($oe) unless $obj->getCategory;
+
+    return $obj;
+}
+
+sub new_person : RESTRICTED {
+
+    my ( $self, $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::AuditAndSecurity::Person->new() unless $obj;
+
+    $obj->setIdentifier(  $args->{identifier}  ) if $args->{identifier};
+    $obj->setName(        $args->{name}        ) if $args->{name};
+    $obj->setFirstName(   $args->{firstname}   ) if $args->{firstname};
+    $obj->setMidInitials( $args->{midinitials} ) if $args->{midinitials};
+    $obj->setLastName(    $args->{lastname}    ) if $args->{lastname};
+    $obj->setPhone(       $args->{phone}       ) if $args->{phone};
+    $obj->setEmail(       $args->{email}       ) if $args->{email};
+    $obj->setFax(         $args->{fax}         ) if $args->{fax};
+    $obj->setAddress(     $args->{address}     ) if $args->{address};
+
+    if ( $args->{roles} ) {
+	my @newroles;
+	my @oldroles = @{ $obj->getRoles() || [] };
+	foreach my $value (split /\s*;\s*/, $args->{roles} ) {
+
+	    my $found = first {
+		$_->getValue() eq $value;
+	    }   @oldroles;
+	    unless ( $found ) {
+		my $role = $self->create_ontologyentry(
+		    'Roles',
+		    $value,
+		    $args->{termsource},
+		    $args->{termaccession},
+		);
+		push @newroles, $role;
+	    }
+	}
+	$obj->addRoles( @newroles ) if scalar(@newroles);
+    }
+
+    my $affiliation;
+    if ( $args->{affiliation} ) {
+	$affiliation = $self->places_bag(
+	    $args->{affiliation},
+	    {
+		name => $args->{affiliation},
+	    },
+	);
+	$obj->setAffiliation( $affiliation );
+    }
+
+    return $obj;
+}
+
+sub new_organization : RESTRICTED {
+
+    my ( $self, $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::AuditAndSecurity::Organization->new() unless $obj;
+
+    unless ( $obj->getIdentifier() ) {
+
+	# Only first 50 characters used in identifier creation to
+	# avoid ArrayExpress schema problems with over-long ids.
+	my $identifier = sprintf(
+	    "%s:%s.Organization",
+	    $self->get_id_prefix(),
+	    substr( $args->{name}, 0, 50 ),
+	);
+
+	$obj->setIdentifier($identifier);
+    }
+
+    $obj->setName($args->{name}) unless $obj->getName();
+
+    return $obj;
+}
+
+sub new_protocol : RESTRICTED {
+
+    my ( $self, $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::Protocol::Protocol->new unless $obj;
+
+    # We allow the SDRF to re-identify protocols if Term Source and
+    # Accession are used.
+    $obj->setIdentifier($args->{accession}) if $args->{accession};
+
+    if ( $args->{type} && ! $obj->getType() ) {
+        my $type = $self->create_ontologyentry(
+            'ProtocolType',
+	    $args->{type},
+	    $args->{termsource},
+	    $args->{termaccession},
+        );
+        $obj->setType($type);
+    }
+    if ( $args->{name} && ! $obj->getName() ) {
+        $obj->setName( $args->{name} );
+    }
+    if ( $args->{description} && ! $obj->getText() ) {
+        $obj->setText( $args->{description} );
+    }
+
+    # FIXME more granular update for parameters here?
+    if ( $args->{parameters} && ! $obj->getParameterTypes() ) {
+
+        my $parameters = $self->create_parameters(
+	    $args->{parameters},
+	    $args->{accession},
+	    $args->{name},
+	);
+
+        $obj->setParameterTypes( $parameters );
+    }
+
+    # Create software, hardware, attach to protocol and each other.
+    my ( $software, $hardware ) = $self->create_and_add_protocol_wares(
+	$obj,
+	$args->{software},
+	$args->{hardware},
+    );
+
+    # FIXME contact is implemented as a NVT association for now.
+    if ( $args->{contact} ) {
+	my $contact = Bio::MAGE::NameValueType->new(
+	    name  => 'Protocol Contact',
+	    value => $args->{contact},
+	);
+	$obj->addPropertySets($contact);
+    }
+
+    return $obj;
+
+}
+
+sub create_parameters : RESTRICTED {
+
+    # We need to control protocol accession, authority and namespace
+    # at this point, use them in the parameter identifiers. Note that
+    # we don't have parameter units at this point; we need to modify
+    # these later at the SDRF parsing stage.
+    my ( $self, $parameters, $prot_accession, $prot_name ) = @_;
+
+    my $param_namespace =
+	sprintf("%s:%s",
+		$self->get_id_prefix(),
+		$prot_accession);
+    my @parameters;
+    foreach my $param_name ( split /\s*;\s*/, $parameters ) {
+	my $param_id = sprintf("%s.%s.Parameter",
+			       $param_namespace,
+			       $param_name);
+	push(
+	    @parameters,
+	    $self->parameter_bag(
+		"$param_name.$prot_name",
+		{
+		    identifier => $param_id,
+		    name       => $param_name,
+		}
+	    )
+	);
+    }
+
+    return \@parameters;
+}
+
+sub new_parameter : RESTRICTED {
+
+    my ( $self, $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::Protocol::Parameter->new unless $obj;
+
+    $obj->setIdentifier($args->{identifier}) unless $obj->getIdentifier();
+
+    if ( $args->{name} && ! $obj->getName() ) {
+	$obj->setName( $args->{name} );
+    }
+
+    # Units are also added at the SDRF parsing stage.
+    if ( $args->{unit} ) {
+	my $defaultval = Bio::MAGE::Measurement::Measurement->new(
+	    unit => $args->{unit},
+	);
+	$obj->setDefaultValue( $defaultval );
+    }
+
+    return $obj;
+}
+
+sub new_protocol_ware : RESTRICTED {
+
+    # Create a Software or Hardware object; $class is set during the
+    # creation of the %bags attribute hash (the bag knows what it contains).
+    my ( $self, $args, $obj ) = @_;
+
+    croak("Error: must supply a 'class' attribute to new_protocol_ware.")
+	unless $args->{class};
+
+    my $mageclass = "Bio::MAGE::Protocol::$args->{class}";
+
+    $obj = $mageclass->new unless $obj;
+
+    # Identifier
+    $obj->setIdentifier( $args->{identifier} ) unless $obj->getIdentifier();
+
+    # Type (not actually supported by IDF yet; FIXME maybe by
+    # inferring from ProtocolType (questionable). This is included for later.
+    if ( $args->{type} && ! $obj->getType() ) {
+	my $type = $self->create_ontologyentry(
+	    $args->{category} || "$args->{class}Type",
+	    $args->{type},
+	);
+	$obj->setType($type);
+    }
+
+    # Name
+    $obj->setName( $args->{name} ) unless $obj->getName;
+
+    return $obj;
+}
+
+sub create_and_add_protocol_wares : RESTRICTED {
+
+    my ( $self, $protocol, $sw_name, $hw_name) = @_;
+
+    my ( $software, $hardware );
+
+    my $ware_namespace =
+	sprintf("%s:%s.%s",
+		$self->get_id_prefix(),
+		$self->get_expt_accession(),
+		unique_identifier());
+
+    # FIXME software and hardware type not currently captured by
+    # MAGE-TAB specification (as of v1.0).
+    if ( $sw_name ) {
+	$software = $self->software_bag(
+	    $sw_name,
+	    {
+		identifier => "$ware_namespace.Software",
+		name       => $sw_name,
+		class      => 'Software',
+	    }
+	);
+	$self->update_object_wares_assnlist( $protocol, $software );
+    }
+    if ( $hw_name ) {
+	$hardware = $self->hardware_bag(
+	    $hw_name,
+	    {
+		identifier => "$ware_namespace.Hardware",
+		name       => $hw_name,
+		class      => 'Hardware',
+	    }
+	);
+	$self->update_object_wares_assnlist( $protocol, $hardware );
+    }
+
+    # Link sw and hw here (checking that they're not already linked).
+    if ( $hardware && $software ) {
+
+	# HW to SW is 1:n
+	$software->setHardware($hardware) unless $software->getHardware();
+	$self->update_object_wares_assnlist( $hardware, $software );
+    }
+
+    return ( $software, $hardware );
+}
+
+sub update_object_wares_assnlist : RESTRICTED {
+
+    # Update the list of hardware or software associated with an
+    # object (protocol or hardware, or conceivably software (to other
+    # softwares)) to contain a new entry.
+    my ( $self, $object, $ware ) = @_;
+
+    # Figure out which list we're updating from the $ware object
+    # passed in.
+    my $attribute = ref $ware;
+    $attribute =~ s/^.*:://;
+
+    my $getter = "get${attribute}s";
+    my $adder  = "add${attribute}s";
+
+    my $found;
+    if ( my $preexisting_wares = $object->$getter ) {
+        my $new_identifier = $ware->getIdentifier();
+        $found = first { $_->getIdentifier() eq $new_identifier }
+            @$preexisting_wares;
+    }
+
+    $object->$adder($ware) unless $found;
+
+    return;
+}
+
+sub new_arraydesign : RESTRICTED {
+
+    my ( $self, $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::ArrayDesign::PhysicalArrayDesign->new()
+	unless $obj;
+
+    if ( defined( $args->{accession} ) ) {
+	$obj->setIdentifier( $args->{accession} )
+	    unless $obj->getIdentifier();
+    }
+
+    if ( defined( $args->{termsource} ) ) {
+
+	my $ts_args;
+	if ( $self->get_in_relaxed_mode() ) {
+	    $ts_args = {name       => $args->{termsource},
+		        identifier => "ebi.ac.uk:Database:$args->{termsource}"};
+	}
+
+	my $database = $self->termsource_bag( $args->{termsource}, $ts_args )
+	    or croak(qq{Error: cannot find Term Source Name "$args->{termsource}"\n});
+
+	my $dbentry = Bio::MAGE::Description::DatabaseEntry->new(
+	    accession => $args->{accession},
+	    database  => $database,
+	);
+	my $desc = Bio::MAGE::Description::Description->new(
+	    databaseReferences => [ $dbentry ],
+	);
+	$obj->setDescriptions( [ $desc ] );
+    }
+
+    return $obj;
+}
+
+sub new_physicalbioassay : RESTRICTED {
+
+    my ( $self, $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::BioAssay::PhysicalBioAssay->new unless $obj;
+
+    # Identifier
+    $args->{identifier_template} && do {
+        my $identifier = "$args->{identifier_template}.PhysicalBioAssay";
+        $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+    };
+
+    # Name
+    $obj->setName($args->{name}) unless $obj->getName();
+
+    # Channels
+    # Construct hash listing of old label ids
+    my $old_label_ids;
+    if ( my $old_channels = $obj->getChannels() ) {
+        foreach my $old_ch ( @{ $old_channels } ) {
+	    if ( my $old_labels = $old_ch->getLabels() ) {
+		foreach my $old_l ( @{ $old_labels } ) {
+		    $old_label_ids->{ $old_l->getIdentifier() }++;
+		}
+            }
+        }
+    }
+
+    # N.B. only one label per LE supported here (although LEs can have
+    # more than one label, and new_labeledextract does support
+    # this).
+    if ( $args->{label} ) {
+	my $dyename;
+
+        # Convert LabelCompound into Channel
+        my $dye_id = $args->{label}->getIdentifier();
+        unless ( ( $dyename )
+		     = ( $dye_id =~ m/^$AE_LABELCOMPOUND_PREFIX(.*)$/ ) ) {
+	    $dyename = 'Unknown';
+        }
+
+	# Don't add the channel if it's already present.
+	unless ( $old_label_ids->{ $dye_id } ) {
+	    my $channel = Bio::MAGE::BioAssay::Channel->new(
+		identifier => $AE_CHANNEL_PREFIX . $dyename,
+		name       => $dyename,
+		labels     => [ $args->{label} ],
+	    );
+	    $obj->addChannels($channel);
+	}
+    }
+
+    # This is only the case for hyb-level PBAs.
+    if ( my $new_bm = $args->{derived_from} ) {
+
+	my $hyb;
+	unless ( $hyb = $obj->getBioAssayCreation() ) {
+	    $hyb = $self->new_hybridization(
+		{
+		    identifier_template => $args->{identifier_template},
+		    name                => $args->{name},
+		    array               => $args->{array},
+		    target              => $obj,
+		    protocolapps        => $args->{hyb_protocolapps},
+		    is_assay            => $args->{is_assay},
+		},
+	    );
+	    $obj->setBioAssayCreation( $hyb );
+	}
+
+	# SourceBioMaterialMeasurements - added where the BioMaterials
+	# differ (e.g. Cy3, Cy5 labeled extracts).
+	my $preexisting_bmms = $hyb->getSourceBioMaterialMeasurements();
+	my $new_bm_id        = $new_bm->getIdentifier;
+	my $found = first {
+	    $_->getBioMaterial()->getIdentifier() eq $new_bm_id
+	} @$preexisting_bmms;
+
+	unless ($found) {
+	    my $bmm = Bio::MAGE::BioMaterial::BioMaterialMeasurement->new(
+		bioMaterial => $new_bm,
+	    );
+	    $hyb->addSourceBioMaterialMeasurements($bmm);
+	}
+    }
+
+    if ( my $factorvals = $args->{factor_values} ) {
+	foreach my $fv ( @$factorvals ) {
+	    $self->add_factorval_to_bioassay( $fv, $obj );
+	}
+    }
+
+    # BioAssayTreatments; initial PBA creation just creates a dummy
+    # BAT (see scan, data for per-channel BATs).  *Don't* update here
+    # if there are preexisting BATs.
+    unless ( $obj->getBioAssayTreatments() ) {
+	$obj->setBioAssayTreatments(
+	    [
+		$self->new_scan(
+		    {
+			identifier_template => $args->{identifier_template},
+			name                => $args->{name},
+			pba                 => $obj,
+			target              => $obj,
+			protocolapps        => $args->{scan_protocolapps},
+		    }
+		)
+	    ]
+	);
+    }
+
+    return $obj;
+}
+
+sub new_factorvalue : RESTRICTED {
+
+    my ( $self, $args, $obj ) = @_;
+
+    # Generate an empty FV object; Value_assn, Measurement_assn will
+    # be added elsewhere.
+    unless ( $obj ) {
+        $obj = Bio::MAGE::Experiment::FactorValue->new();
+    }
+
+    if ( $args->{identifier_template} ) {
+	$obj->setIdentifier("$args->{identifier_template}.FactorValue")
+	    unless $obj->getIdentifier();
+    }
+    $obj->setName( $args->{name} ) unless $obj->getName;
+
+    return $obj;
+}
+    
+sub add_factorval_to_bioassay : RESTRICTED {
+
+    my ( $self, $factorval, $bioassay ) = @_;
+
+    # FIXME we should probably be comparing category, value, unit
+    # etc. here.
+    my $found = first {
+	$_->getIdentifier() eq $factorval->getIdentifier()
+    } @{ $bioassay->getBioAssayFactorValues() || [] };
+    $bioassay->addBioAssayFactorValues( $factorval ) unless $found;
+
+    return;
+}
+
+sub new_hybridization : RESTRICTED {
+
+    my ( $self, $args, $obj ) = @_;
+
+    my $identifier;
+    unless ( $obj ) {
+	if ( $args->{is_assay} ) {
+	    $obj = Bio::MAGE::BioAssay::BioAssayCreation->new();
+	    $identifier = "$args->{identifier_template}.Assay";
+	}
+	else {
+	    $obj = Bio::MAGE::BioAssay::Hybridization->new();
+	    $identifier = "$args->{identifier_template}.Hybridization";
+	}
+    }
+
+    if ( $args->{identifier_template} ) {
+	$obj->setIdentifier( $identifier )
+	    unless $obj->getIdentifier();
+    }
+
+    $obj->setName(  $args->{name} )                    if $args->{name};
+    $obj->setArray( $args->{array} )                   if $args->{array};
+    $obj->setPhysicalBioAssayTarget( $args->{target} ) if $args->{target};
+
+    if ( $args->{protocolapps} && scalar( @{ $args->{protocolapps} } ) ) {
+	$obj->setProtocolApplications( $args->{protocolapps} );
+    }
+
+    return $obj;
+}
+
+sub new_scan : RESTRICTED {
+
+    my ( $self, $args, $obj ) = @_;
+
+    $obj = Bio::MAGE::BioAssay::ImageAcquisition->new() unless $obj;
+
+    if ( $args->{identifier_template} ) {
+	$obj->setIdentifier("$args->{identifier_template}.ImageAcquisition")
+	    unless $obj->getIdentifier();
+    }
+
+    $obj->setName(  $args->{name} )           if $args->{name};
+    $obj->setPhysicalBioAssay( $args->{pba} ) if $args->{pba};
+    $obj->setTarget( $args->{target} )        if $args->{target};
+
+    if ( $args->{protocolapps} && scalar( @{ $args->{protocolapps} } ) ) {
+	$obj->setProtocolApplications( $args->{protocolapps} );
+    }
+
+    return $obj;
+}
+
+sub calculate_eol_char : RESTRICTED {
+
+    my ( $self, $file ) = @_;
+
+    unless ( $self->get_eol_char() ) {
+	my ($eols, $eol_char) = check_linebreaks( $self->get_filename() );
+	unless ( $eol_char ) {
+	    croak(
+		sprintf(
+		    "Error: Cannot correctly parse linebreaks in file %s"
+			. " (%s unix, %s dos, %s mac)\n",
+		    $self->get_filename(),
+		    $eols->{unix},
+		    $eols->{dos},
+		    $eols->{mac},
+		)
+	    );
+	}
+	$self->set_eol_char( $eol_char );
+    }
+
+    if (    ( $self->get_eol_char eq "\015" )
+         && ( $Text::CSV_XS::VERSION < 0.27 ) ) {
+
+	# Mac linebreaks not supported by older versions of Text::CSV_XS.
+	croak("Error: Mac linebreaks not supported by this version"
+	    . " of Text::CSV_XS. Please upgrade to version 0.27 or higher.\n");
+    }
+
+    return $self->get_eol_char();
+}
+
+sub get_csv_parser {
+
+    my ( $self ) = @_;
+
+    # FIXME consider caching this in a private attribute?
+    my $csv_parser = Text::CSV_XS->new(
+        {   sep_char    => qq{\t},
+            quote_char  => qq{"},                   # default
+            escape_char => qq{"},                   # default
+            binary      => 1,
+            eol         => ( $self->calculate_eol_char() || "\n" ),
+	    allow_loose_quotes => 1,
+        }
+    );
+
+    return $csv_parser;
+}
+
+sub raise_error : RESTRICTED {
+
+    my ( $self, @messages ) = @_;
+
+    croak(@messages);
+}
+
+sub raise_warning : RESTRICTED {
+
+    my ( $self, @messages ) = @_;
+
+    carp(@messages);
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Tracking/CelQC.pm b/lib/ArrayExpress/Tracking/CelQC.pm
new file mode 100644
index 0000000..8a06b09
--- /dev/null
+++ b/lib/ArrayExpress/Tracking/CelQC.pm
@@ -0,0 +1,171 @@
+#!/usr/bin/env perl
+#
+# $Id: CelQC.pm 1960 2008-02-21 12:03:19Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::Tracking::CelQC;
+
+use Class::Std;
+use File::Temp qw(tempfile);
+use Carp;
+use POSIX;
+
+my %input  : ATTR( :get<input>,  :init_arg<input>,  :default<undef> );
+my %quiet  : ATTR( :get<quiet>,  :init_arg<quiet>,  :default<undef> );
+my %stdout : ATTR( :name<stdout>, :default<undef> );
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    unless ( defined $self->get_input() ) {
+	confess(qq{Error: Input file not set.\n});
+    }
+    unless ( -r $self->get_input() ) {
+	croak(qq{Error: Input file does not exist or is unreadable.\n});
+    }
+
+    if ( $self->get_quiet() ) {
+	$self->set_stdout('/dev/null');
+    }
+    else {
+	$self->set_stdout(POSIX::ctermid());
+    }
+
+    return;
+}
+
+sub create_r_script : PRIVATE {
+
+    my ( $self, $reference_fh ) = @_;
+
+    # Set up our temporary R script.
+    my ( $r_fh, $r_script ) = tempfile();
+    unless ( $r_fh && $r_script ) {
+	croak(qq{Error: Unable to open temporary R script file "$r_script": $!\n});
+    }
+    while( <$reference_fh> ) {
+	print $r_fh $_;
+    }
+    close( $r_fh )
+	or croak(qq{Error: Unable to close temporary R script filehandle: $!\n});
+
+    return $r_script;
+}
+
+sub call_r_script : PRIVATE {
+
+    my ( $self, $output ) = @_;
+
+    # Set up our temporary R script.
+    my $r_script = $self->create_r_script( \*DATA );
+
+    my $input  = $self->get_input();
+    my $stdout = $self->get_stdout();
+
+    # Call the R script and do the interesting stuff.
+    my $syscall = "R CMD BATCH --slave --no-save --no-restore"
+	. " -input=$input -output=$output $r_script $stdout";
+
+    # Quote some legal characters which would otherwise confuse the shell.
+    $syscall =~ s/([(){}\[\]])/\\$1/g;
+
+    system($syscall) == 0
+	or croak("Error executing R code: $?");
+
+    unlink( $r_script )
+	or croak(qq{Error: Unable to delete temporary R script file "$r_script": $!\n});
+
+    return;
+}
+
+sub run_metrics {
+
+    my ( $self ) = @_;
+
+    # Set up our temporary output file.
+    my $result_file;
+    (undef, $result_file) = tempfile();
+
+    $self->call_r_script( $result_file );
+
+    # Parse the results file into a hash.
+    open( my $result_fh, '<', $result_file )
+	or croak(qq{Error: Unable to open results file "$result_file": $!\n});
+    my %result;
+    while ( my $line = <$result_fh> ) {
+	my ( $key, $value ) = ( $line =~ m/"([^\"]+)" \s* "([^\"]+)" \s* \z/xms );
+	$result{$key} = $value;
+    }
+
+    close( $result_fh )
+	or croak(qq{Error closing results filehandle: $!\n});
+
+    unlink( $result_file )
+	or croak(qq{Error deleting temporary results file: $!\n});
+
+    return \%result;
+}
+
+1;
+
+__DATA__
+
+# Actual R code follows; this now works, and is used to generate a
+# temporary R script which is run on the input CEL file.
+#
+# Run using:
+# DYLD_LIBRARY_PATH='' R CMD BATCH -input=<cel file> -output=<output file> <this script file>.
+input<-FALSE;
+output<-FALSE;
+
+# This is a fairly generic options parsing loop, modified to store
+# -input and -output values. Taken from
+# https://sws.stat.iastate.edu/resources/programmingExamples/R/R-Command-Line.html
+for (e in commandArgs()) {
+    ta = strsplit(e,"=",fixed=TRUE);
+    if(! is.na(ta[[1]][2])) {
+        temp = ta[[1]][2];
+        if(substr(ta[[1]][1],nchar(ta[[1]][1]),nchar(ta[[1]][1])) == "I") {
+            temp = as.integer(temp);
+        }
+        if(substr(ta[[1]][1],nchar(ta[[1]][1]),nchar(ta[[1]][1])) == "N") {
+            temp = as.numeric(temp);
+        }
+        assign(ta[[1]][1],temp);
+        if ( ta[[1]][1] == "-input" ) {
+            input = temp;
+        }
+        if ( ta[[1]][1] == "-output" ) {
+            output = temp;
+        }
+#        cat("assigned ",ta[[1]][1]," the value of |",temp,"|\n");
+    } else {
+        assign(ta[[1]][1],TRUE);
+#        cat("assigned ",ta[[1]][1]," the value of TRUE\n");
+    }
+}
+
+library('simpleaffy');
+scorecel <- function(infile, outfile) {
+    celdata<-ReadAffy(filenames=infile);
+    celqc<-qc(celdata);
+    RNAdeg=AffyRNAdeg(celdata);
+    celavgbg=avbg(celqc);
+    perc=percent.present(celqc);
+# Each tag here will correspond to a qc type in the tracking DB. The
+# exception is "platform" which has its own table.
+    capture.output(c("platform",cdfName(celdata)),
+                      file=outfile, append=FALSE);
+    capture.output(c("affy_average_background",prettyNum(as.double(celavgbg))),
+                      file=outfile, append=TRUE);
+    capture.output(c("affy_scale_factor",prettyNum(sfs(celqc))),
+                      file=outfile, append=TRUE);
+    capture.output(c("affy_percent_present",prettyNum(as.double(perc))),
+                      file=outfile, append=TRUE);
+    capture.output(c("affy_RNAdeg_slope",prettyNum(RNAdeg$slope)),
+                      file=outfile, append=TRUE);
+}
+scorecel(infile=input, outfile=output);
diff --git a/lib/ArrayExpress/Tracking/Event.pm b/lib/ArrayExpress/Tracking/Event.pm
new file mode 100644
index 0000000..f641906
--- /dev/null
+++ b/lib/ArrayExpress/Tracking/Event.pm
@@ -0,0 +1,27 @@
+#!/usr/bin/env perl
+#
+# $Id: Event.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::Tracking::Event;
+
+# Class used as a bridge between the AE database query objects and the
+# tracking database.
+
+use Class::Std;
+
+my %event_type       : ATTR( :name<event_type>,       :default<undef> );
+my %success          : ATTR( :name<success>,          :default<undef> );
+my %source_db        : ATTR( :name<source_db>,        :default<undef> );
+my %target_db        : ATTR( :name<target_db>,        :default<undef> );
+my %starttime        : ATTR( :name<starttime>,        :default<undef> );
+my %endtime          : ATTR( :name<endtime>,          :default<undef> );
+my %machine          : ATTR( :name<machine>,          :default<undef> );
+my %operator         : ATTR( :name<operator>,         :default<undef> );
+my %log_file         : ATTR( :name<log_file>,         :default<undef> );
+my %jobregister_dbid : ATTR( :name<jobregister_dbid>, :default<undef> );
+my %comment          : ATTR( :name<comment>,          :default<undef> );
+
+1;
diff --git a/lib/ArrayExpress/Tracking/QCJobManager.pm b/lib/ArrayExpress/Tracking/QCJobManager.pm
new file mode 100644
index 0000000..d911ed6
--- /dev/null
+++ b/lib/ArrayExpress/Tracking/QCJobManager.pm
@@ -0,0 +1,180 @@
+# $Id: QCJobManager.pm 1978 2008-02-28 12:14:14Z tfrayner $
+
+package ArrayExpress::Tracking::QCJobManager;
+
+use strict;
+use warnings;
+
+use Class::Std;
+use Carp;
+
+use ArrayExpress::AutoSubmission::DB;
+
+# Attributes.
+my %ae_sth           : ATTR( :name<ae_sth>,          :default<undef>   );
+my %ae_dbh           : ATTR( :name<ae_dbh>,          :default<undef>   );
+my %num_procs        : ATTR( :name<num_procs>,       :default<1>       );
+
+my $CHILD_SCRIPT = 'cel_qcstats.pl';
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    unless ( $self->get_ae_sth() ) {
+	confess("Error: no ae_sth set in " . __PACKAGE__);
+    }
+    unless ( $self->get_ae_dbh() ) {
+	confess("Error: no ae_dbh set in " . __PACKAGE__);
+    }
+
+    return;
+}
+
+sub run {
+
+    # Main bjob management event loop.
+
+    my ( $self ) = @_;
+
+    # Start an initial run of $self->get_num_procs();
+    $self->start_new_set();
+
+    # Approx 30 seconds per process.
+    my $sleep_period = 30;
+    sleep( $sleep_period );
+
+    while ( 1 ) {
+
+	# Loop ends when there are no more rows to process.
+	$self->start_new_set() or return;
+	sleep( $sleep_period );
+    }
+
+    return;
+}
+
+sub count_submitted_jobs : PRIVATE {
+
+    my ( $self ) = @_;
+
+    open ( my $bjobs, '-|', 'bjobs -w 2>&1' )
+	or croak("Error setting up bjobs handle: $!");
+
+    my $count = 0;
+    while ( my $out = <$bjobs> ) {
+	$count++ if ( $out =~ m/$CHILD_SCRIPT/ );
+    }
+
+    close( $bjobs ) or carp( $! ? "Error closing bjobs pipe: $!"
+			        : "Exit status $? from bjobs." );
+
+    return $count;
+}
+
+sub get_old_hashed_data {
+
+    print( q{Caching data identifiers...} );
+
+    my $hashed_data = {};
+
+    my $dbh = ArrayExpress::AutoSubmission::DB->db_Main();
+
+    my $sth = $dbh->prepare(<<'QUERY');
+select d.identifier, d.needs_metrics_calculation
+from loaded_data d
+where is_deleted=0
+QUERY
+
+    $sth->execute() or die( $sth->errstr() );
+
+    while ( my $row = $sth->fetchrow_hashref( 'NAME_lc' ) ) {
+	$hashed_data->{ $row->{'identifier'} }
+	    = $row->{'needs_metrics_calculation'};
+    }
+
+    $sth->finish();
+
+    print( qq{ done.\n} );
+
+    return $hashed_data;
+}
+
+{
+    # Hash of identifier => needs_metrics_calculation
+    my $cached_query;
+
+sub start_new_set : PRIVATE {
+
+    my ( $self ) = @_;
+
+    # Quick look to see if the data has been already hashed.
+    $cached_query ||= get_old_hashed_data();
+
+    my $sth = $self->get_ae_sth();
+
+    # Double-check that the statement handle has been executed.
+    unless ( $sth->{'Executed'} ) {
+	$sth->execute() or die( $sth->errstr() );
+    }
+
+    my $prev = $self->count_submitted_jobs();
+
+    PROC:
+    for ( my $i = $prev; $i < $self->get_num_procs(); $i++ ) {
+	my $hashref = $sth->fetchrow_hashref('NAME_lc');
+
+	# This would be the endpoint.
+	return unless $hashref;
+
+	# Skip those rows which don't need calculations.
+	redo PROC unless $cached_query->{ $hashref->{'identifier'} };
+
+	$self->process_row( $hashref );
+    }
+
+    return 1;
+}
+
+}
+
+sub process_row : PRIVATE {
+
+    my ( $self, $hashref ) = @_;
+
+    my $ae_dbh = $self->get_ae_dbh();
+
+    # Get the CEL file, dispatch the subprocess FIXME.
+    my $lob_locator = $hashref->{'lob'};
+    my $length      = $ae_dbh->ora_lob_length( $lob_locator );
+    my $chunksize   = 65536;
+
+    # Read in the LOB and write it to a file.
+    my $filename = $hashref->{'dbid'} . '.cel';
+    open( my $fh, '>', $filename )
+	or croak("Error: unable to open file for writing: $!");
+    for ( my $i = 1; $i <= $length; $i += $chunksize ) {
+	my $chunk = $ae_dbh->ora_lob_read( $lob_locator, $i, $chunksize );
+	syswrite( $fh, $chunk ) or croak("Error writing to file: $!");
+    }
+    close( $fh ) or croak("Error closing filehandle: $!");
+
+    # Make sure our log file at least exists, otherwise we get a whole
+    # lot of silent failure from the oh-so-wonderful LSF system.
+    my $logfile = 'legacy_qc.log';
+    unless ( -e $logfile ) {
+	open( my $fh, '>', $logfile )
+	    or croak("Error initialising log file: $!\n");
+	close( $fh );
+    }
+
+    # The subprocess will need to delete the file when done, hence the -d option.
+    system(
+	qq{bsub -q production -o "$logfile" }
+      . qq{'$CHILD_SCRIPT -i "$hashref->{identifier}" -f $filename -q -d'}
+    ) == 0 or croak("Error submitting LSF process.");
+
+    return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Tracking/QueryHandler.pm b/lib/ArrayExpress/Tracking/QueryHandler.pm
new file mode 100644
index 0000000..e58ebfa
--- /dev/null
+++ b/lib/ArrayExpress/Tracking/QueryHandler.pm
@@ -0,0 +1,769 @@
+#!/usr/bin/env perl
+#
+# $Id: QueryHandler.pm 2077 2008-06-10 09:39:46Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::Tracking::QueryHandler;
+
+use Class::Std;
+use Carp;
+use Date::Manip qw(ParseDate UnixDate);
+use Readonly;
+
+use ArrayExpress::Curator::Database qw(
+    get_ae_dbh
+    get_aedw_dbh
+);
+
+my %dbhandle        : ATTR( :name<dbhandle>,        :default<{}>        );
+my %event_cache     : ATTR( :name<event_cache>,     :default<undef>     );
+my %last_jobid      : ATTR( :name<last_jobid>,      :default<{}>        );
+my %cached_sth      : ATTR( :name<cached_sth>,      :default<{}>        );
+
+Readonly my $AEREP_DB  => 'AEPUB1';
+Readonly my $AEDW_DB   => 'AEDWDEV';
+
+sub BUILD {
+
+    my ( $self, $id, $args ) = @_;
+
+    $dbhandle{ident $self}{ $AEREP_DB } = get_ae_dbh()
+	or croak("Error: Unable to connect to AE repository DB.");
+
+    $dbhandle{ident $self}{ $AEDW_DB }  = get_aedw_dbh()
+	or croak("Error: Unable to connect to AE warehouse DB.");
+
+    # Long values are trimmed at 1000 chars.
+    $dbhandle{ident $self}->{ $AEREP_DB }{LongReadLen} = 1000;
+    $dbhandle{ident $self}->{ $AEREP_DB }{LongTruncOk} = 1;
+    $dbhandle{ident $self}->{ $AEDW_DB }{LongReadLen}  = 1000;
+    $dbhandle{ident $self}->{ $AEDW_DB }{LongTruncOk}  = 1;
+
+    return;
+}
+
+sub START {
+
+    my ( $self, $id, $args ) = @_;
+
+    # Set the default last job id to zero for each DB instance.
+    foreach my $instance ( @{ $self->get_instances() } ) {
+	$self->get_last_jobid()->{ $instance } ||= 0;
+    }
+
+    # Create our cached statement handles.
+    $self->cache_statement_handles();
+
+    return;
+}
+
+sub cache_statement_handles : PRIVATE {
+
+    my ( $self ) = @_;
+
+    print STDOUT "Caching statement handles...\n";
+
+    my $dbh = $self->get_dbhandle()->{ $AEREP_DB };
+    
+    $cached_sth{ident $self}{expt_species} = $dbh->prepare(<<"QUERY");
+select unique o.value
+from tt_experiment e,
+tt_biomaterials_experiments eb,
+tt_poly_biomaterial pb,
+tt_characteris_t_biomateri bso,
+tt_ontologyentry o,
+tt_identifiable i
+where i.identifier = ?
+and e.id = eb.experiments_id
+and i.id = eb.experiments_id
+and eb.biomaterials_id = pb.id
+and pb.t_biosource_id is not null
+and pb.t_biosource_id = bso.t_biomaterial_id
+and bso.characteristics_id = o.id
+and lower(o.category) = 'organism'
+QUERY
+
+    $cached_sth{ident $self}{array_species} = $dbh->prepare(<<"QUERY");
+select unique oe.value
+from tt_arraydesign ad,
+tt_reportergro_t_arraydesi rg,
+tt_designelementgroup de,
+tt_ontologyentry oe,
+tt_identifiable i
+where i.identifier = ?
+and ad.id = i.id
+and rg.t_arraydesign_id = ad.id
+and de.id = rg.reportergroups_id
+and de.species_id = oe.id
+and lower(oe.category) = 'organism'
+QUERY
+
+    $cached_sth{ident $self}{expt_arrays} = $dbh->prepare(<<"QUERY");
+select unique iden.identifier
+from tt_bioassays_t_experiment bt,
+tt_poly_bioassay poly_b,
+tt_physicalbioassay pba,
+tt_bioassaycreation bc,
+tt_array ar,
+tt_physicalarraydesign ard,
+tt_identifiable iden,
+tt_identifiable i
+where i.identifier = ?
+and i.id = bt.t_experiment_id
+and poly_b.t_physicalbioassay_id = bt.bioassays_id
+and pba.id = poly_b.t_physicalbioassay_id
+and pba.bioassaycreation_id = bc.id
+and bc.array_id = ar.id
+and ar.arraydesign_id = ard.id
+and ard.id = iden.id
+QUERY
+
+    $cached_sth{ident $self}{num_samples} = $dbh->prepare(<<"QUERY");
+select count (unique pb.t_biosource_id) as samples
+from tt_biomaterials_experiments be,
+tt_biomaterial bm,
+tt_poly_biomaterial pb,
+tt_biosample bs,
+tt_identifiable i
+where i.identifier = ?
+and be.experiments_id = i.id
+and be.biomaterials_id = bm.id
+and bm.id = pb.t_biosource_id
+QUERY
+
+    $cached_sth{ident $self}{num_hybridizations} = $dbh->prepare(<<"QUERY");
+select count( cp.t_hybridization_id ) as hybs
+from tt_experiment e,
+tt_bioassays_t_experiment eb,
+tt_poly_bioassay bp,
+tt_physicalbioassay pba,
+tt_poly_bioassaycreation cp,
+tt_identifiable i
+where i.identifier = ?
+and e.id = i.id
+and e.id = eb.t_experiment_id
+and eb.bioassays_id = bp.t_physicalbioassay_id
+and bp.t_physicalbioassay_id = pba.id
+and pba.bioassaycreation_id = cp.id
+and cp.t_hybridization_id is not null
+QUERY
+
+    $cached_sth{ident $self}{has_raw_data} = $dbh->prepare(<<"QUERY");
+select count(*)
+from tt_identifiable i,
+tt_bioassaydatagroup bg,
+tt_bioassaydat_bioassaydat bb,
+tt_poly_bioassaydata pb
+where i.identifier = ?
+and i.id = bg.experiment_id
+and bg.id=bb.bioassaydatagroups_id
+and bb.bioassaydatas_id = pb.t_measuredbioassaydata_id
+QUERY
+
+    $cached_sth{ident $self}{has_processed_data} = $dbh->prepare(<<"QUERY");
+select count(*)
+from tt_identifiable i,
+tt_bioassaydatagroup bg,
+tt_bioassaydat_bioassaydat bb,
+tt_poly_bioassaydata pb
+where i.identifier = ?
+and i.id = bg.experiment_id
+and bg.id=bb.bioassaydatagroups_id
+and bb.bioassaydatas_id = pb.t_derivedbioassaydata_id
+QUERY
+
+    $cached_sth{ident $self}{expt_factors} = $dbh->prepare(<<"QUERY");
+select oe.value as value
+from tt_experimentdesign ed,
+tt_experimentalfactor ef,
+tt_ontologyentry oe,
+tt_identifiable i
+where i.identifier = ?
+and ed.t_experiment_id = i.id
+and ef.t_experimentdesign_id = ed.id
+and oe.id = ef.category_id
+QUERY
+
+    $cached_sth{ident $self}{expt_qts} = $dbh->prepare(<<"QUERY");
+select unique iden.name as name
+from tt_bioassaydatagroup bg,
+tt_bioassaydat_bioassaydat bb,
+tt_bioassaydata ba,
+tt_quantitationtypedimension qtd,
+tt_quantitatio_t_quantitat qtq,
+tt_quantitationtype qt,
+tt_identifiable iden,
+tt_identifiable i
+where i.identifier = ?
+and bg.experiment_id = i.id
+and bg.id=bb.bioassaydatagroups_id
+and ba.id = bb.bioassaydatas_id
+and qtd.id = ba.quantitationtypedimension_id
+and qtq.t_quantitationtypedimension_id = ba.quantitationtypedimension_id
+and qt.id = qtq.quantitationtypes_id
+and iden.id = qt.id
+QUERY
+
+    $cached_sth{ident $self}{is_released} = $dbh->prepare(<<"QUERY");
+select usr.name
+from pl_visibility vi,
+pl_label la,
+tt_identifiable i,
+pl_user usr
+where i.identifier = ?
+and i.identifier = la.mainobj_name
+and la.id = vi.label_id
+and vi.user_id = usr.id
+and usr.name = 'guest'
+QUERY
+
+    $cached_sth{ident $self}{is_loaded} = $dbh->prepare(<<"QUERY");
+select count(*)
+from pl_label la,
+tt_identifiable i
+where i.identifier = ?
+and i.identifier = la.mainobj_name
+QUERY
+
+    $cached_sth{ident $self}{release_date} = $dbh->prepare(<<"QUERY");
+select nvt.value
+from tt_namevaluetype nvt,
+tt_identifiable i
+where i.identifier = ?
+and nvt.t_extendable_id = i.id
+and nvt.name ='ArrayExpressReleaseDate'
+QUERY
+
+    $cached_sth{ident $self}{ae_miame_score} = $dbh->prepare(<<"QUERY");
+select nvt.value
+from tt_namevaluetype nvt,
+tt_identifiable i
+where i.identifier = ?
+and nvt.t_extendable_id = i.id
+and nvt.name ='AEMIAMESCORE'
+QUERY
+
+    $cached_sth{ident $self}{curated_name} = $dbh->prepare(<<"QUERY");
+select nvt.value
+from tt_namevaluetype nvt,
+tt_identifiable i
+where i.identifier = ?
+and nvt.t_extendable_id = i.id
+and nvt.name ='AEExperimentDisplayName'
+and nvt.value != i.name
+QUERY
+
+    $cached_sth{ident $self}{submitter_description} = $dbh->prepare(<<"QUERY");
+select d.text
+from tt_description d,
+tt_identifiable i
+where i.identifier = ?
+and d.t_describable_id = i.id
+and d.text not like '(Generated description)%'
+and length(d.text) > 0
+QUERY
+    
+    return;
+}
+
+sub get_instances {
+
+    my ( $self ) = @_;
+
+    return [ keys %{ $self->get_dbhandle() } ];
+}
+
+sub get_experiments {
+
+    my ( $self ) = @_;
+
+    # FIXME we might want to extend this to include the AEDW.
+    my $dbh = $self->get_dbhandle()->{ $AEREP_DB };
+
+    my $sth = $dbh->prepare(<<"QUERY");
+select i.identifier
+from tt_identifiable i,
+tt_experiment e
+where e.id = i.id
+QUERY
+
+    $sth->execute() or die( $sth->errstr() );
+    
+    my $results = $sth->fetchall_arrayref();
+
+    return scalar @{ $results }
+           ? [ map { $_->[0] } @{ $results } ]
+	   : [];
+}
+
+sub get_array_designs {
+
+    # FIXME we might want to extend this to include the AEDW.
+    my ( $self ) = @_;
+
+    my $dbh = $self->get_dbhandle()->{ $AEREP_DB };
+
+    my $sth = $dbh->prepare(<<"QUERY");
+select i.identifier
+from tt_identifiable i,
+tt_physicalarraydesign a
+where a.id = i.id
+QUERY
+
+    $sth->execute() or die( $sth->errstr() );
+    
+    my $results = $sth->fetchall_arrayref();
+
+    return scalar @{ $results }
+           ? [ map { $_->[0] } @{ $results } ]
+	   : [];
+}
+
+sub get_array_species {
+
+    my ( $self, $accession ) = @_;
+
+    # Query returns Organism OE values.
+    my $sth = $self->get_cached_sth()->{array_species}
+	or die("Error: Undefined statement handle.");
+
+    $sth->execute( $accession ) or die( $sth->errstr() );
+
+    my $results = $sth->fetchall_arrayref();
+
+    return scalar @{ $results }
+           ? [ map { $_->[0] } @{ $results } ]
+	   : [];
+}
+
+sub get_expt_species {
+
+    my ( $self, $accession ) = @_;
+
+    # Query returns Organism OE values.
+    my $sth = $self->get_cached_sth()->{expt_species}
+	or die("Error: Undefined statement handle.");
+
+    $sth->execute( $accession ) or die( $sth->errstr() );
+
+    my $results = $sth->fetchall_arrayref();
+
+    return scalar @{ $results }
+           ? [ map { $_->[0] } @{ $results } ]
+	   : [];
+}
+
+sub get_expt_arrays {
+
+    my ( $self, $accession ) = @_;
+
+    # Query returns array accessions.
+    my $sth = $self->get_cached_sth()->{expt_arrays}
+	or die("Error: Undefined statement handle.");
+
+    $sth->execute( $accession ) or die( $sth->errstr() );
+    
+    my $results = $sth->fetchall_arrayref();
+
+    return scalar @{ $results }
+           ? [ map { $_->[0] } @{ $results } ]
+	   : [];
+}
+
+sub get_expt_factors {
+
+    my ( $self, $accession ) = @_;
+
+    # Query returns ExperimentalFactorCategory OE values.
+    my $sth = $self->get_cached_sth()->{expt_factors}
+	or die("Error: Undefined statement handle.");
+
+    $sth->execute( $accession ) or die( $sth->errstr() );
+    
+    my $results = $sth->fetchall_arrayref();
+
+    return scalar @{ $results }
+           ? [ map { $_->[0] } @{ $results } ]
+	   : [];
+}
+
+sub get_expt_qts {
+
+    my ( $self, $accession ) = @_;
+
+    # Query returns QT names.
+    my $sth = $self->get_cached_sth()->{expt_qts}
+	or die("Error: Undefined statement handle.");
+
+    $sth->execute( $accession ) or die( $sth->errstr() );
+    
+    my $results = $sth->fetchall_arrayref();
+
+    return scalar @{ $results }
+           ? [ map { $_->[0] } @{ $results } ]
+	   : [];
+}
+
+sub get_submitter_description {
+
+    my ( $self, $accession ) = @_;
+
+    # Query returns Description texts.
+    my $sth = $self->get_cached_sth()->{submitter_description}
+	or die("Error: Undefined statement handle.");
+
+    $sth->execute( $accession ) or die( $sth->errstr() );
+    
+    my $results = $sth->fetchall_arrayref();
+
+    return $results->[0][0];
+}
+
+sub get_curated_name {
+
+    my ( $self, $accession ) = @_;
+
+    # Query returns the curated name (AEExperimentDisplayName NVT).
+    my $sth = $self->get_cached_sth()->{curated_name}
+	or die("Error: Undefined statement handle.");
+
+    $sth->execute( $accession ) or die( $sth->errstr() );
+    
+    my $results = $sth->fetchall_arrayref();
+
+    return $results->[0][0];
+}
+
+sub get_num_samples {
+
+    my ( $self, $accession ) = @_;
+
+    # Query returns count of samples.
+    my $sth = $self->get_cached_sth()->{num_samples}
+	or die("Error: Undefined statement handle.");
+
+    $sth->execute( $accession ) or die( $sth->errstr() );
+    
+    my $results = $sth->fetchall_arrayref();
+
+    return $results->[0][0];
+}
+
+sub get_num_hybridizations {
+
+    my ( $self, $accession ) = @_;
+
+    # Query returns count of samples.
+    my $sth = $self->get_cached_sth()->{num_hybridizations}
+	or die("Error: Undefined statement handle.");
+
+    $sth->execute( $accession ) or die( $sth->errstr() );
+    
+    my $results = $sth->fetchall_arrayref();
+
+    return $results->[0][0];
+}
+
+sub get_has_raw_data {
+
+    my ( $self, $accession ) = @_;
+
+    # Query returns count of MBADatas.
+    my $sth = $self->get_cached_sth()->{has_raw_data}
+	or die("Error: Undefined statement handle.");
+
+    $sth->execute( $accession ) or die( $sth->errstr() );
+    
+    my $results = $sth->fetchall_arrayref();
+
+    return $results->[0][0] ? 1 : 0;
+}
+
+sub get_has_processed_data {
+
+    my ( $self, $accession ) = @_;
+
+    # Query returns count of DBADatas.
+    my $sth = $self->get_cached_sth()->{has_processed_data}
+	or die("Error: Undefined statement handle.");
+
+    $sth->execute( $accession ) or die( $sth->errstr() );
+    
+    my $results = $sth->fetchall_arrayref();
+
+    return $results->[0][0] ? 1 : 0;
+}
+
+sub get_release_date {
+
+    my ( $self, $accession ) = @_;
+
+    # Query returns the release date (ArrayExpressReleaseDate NVT).
+    my $sth = $self->get_cached_sth()->{release_date}
+	or die("Error: Undefined statement handle.");
+
+    $sth->execute( $accession ) or die( $sth->errstr() );
+    
+    my $results = $sth->fetchall_arrayref();
+
+    return $results->[0][0];
+}
+
+sub get_is_released {
+
+    my ( $self, $accession ) = @_;
+
+    # If query returns anything, object is public.
+    my $sth = $self->get_cached_sth()->{is_released}
+	or die("Error: Undefined statement handle.");
+
+    $sth->execute( $accession ) or die( $sth->errstr() );
+    
+    my $results = $sth->fetchall_arrayref();
+
+    return scalar @{ $results } ? 1 : 0;
+}
+
+sub get_is_loaded {
+
+    my ( $self, $accession ) = @_;
+
+    # If query returns > 0, object is loaded.
+    my $sth = $self->get_cached_sth()->{is_loaded}
+	or die("Error: Undefined statement handle.");
+
+    $sth->execute( $accession ) or die( $sth->errstr() );
+    
+    my $results = $sth->fetchall_arrayref();
+
+    return $results->[0][0];
+}
+
+sub get_expt_in_data_warehouse {
+
+    my ( $self, $accession ) = @_;
+
+    my $dbh = $self->get_dbhandle()->{ $AEDW_DB };
+
+    # If query returns > 0, object is loaded.
+    my $sth = $dbh->prepare(<<"QUERY");
+select experiment_identifier
+from ae1__experiment__main
+where experiment_identifier = ?
+QUERY
+
+    $sth->execute( $accession ) or die( $sth->errstr() );
+    
+    my $results = $sth->fetchall_arrayref();
+
+    return scalar( @$results );
+}
+
+sub get_array_in_data_warehouse {
+
+    my ( $self, $accession ) = @_;
+
+    my $dbh = $self->get_dbhandle()->{ $AEDW_DB };
+
+    # If query returns > 0, object is loaded.
+    my $sth = $dbh->prepare(<<"QUERY");
+select arraydesign_identifier
+from ae2__arraydesign
+where arraydesign_identifier = ?
+QUERY
+
+    $sth->execute( $accession ) or die( $sth->errstr() );
+    
+    my $results = $sth->fetchall_arrayref();
+
+    return scalar( @$results );
+}
+
+sub get_ae_miame_score {
+
+    my ( $self, $accession ) = @_;
+
+    # Query returns the AE-computed MIAME score (AEMIAMESCORE NVT).
+    my $sth = $self->get_cached_sth()->{ae_miame_score}
+	or die("Error: Undefined statement handle.");
+
+    $sth->execute( $accession ) or die( $sth->errstr() );
+    
+    my $results = $sth->fetchall_arrayref();
+
+    return $results->[0][0];
+}
+
+sub get_ae_data_warehouse_score {
+
+    my ( $self, $accession ) = @_;
+
+    confess("Not implemented yet"); # FIXME once this has been implemented in AE.
+}
+
+sub get_events {
+
+    my ( $self, $accession ) = @_;
+
+    my $events = $self->get_event_cache();
+
+    unless ( $events ) {
+
+	# Cache our jobregister events.
+	$self->cache_aerep_events();
+	$self->cache_aedw_events();
+    }
+
+    return $self->get_event_cache()->{ $accession } || [];
+}
+
+sub get_updated_event_data {
+
+    my ( $self, $dbid, $instance ) = @_;
+
+    my $dbh = $self->get_dbhandle()->{ $instance }
+	or confess("Error: unrecognized database instance $instance.");
+
+    my $sth = $dbh->prepare(<<"QUERY");
+select ENDTIME, PHASE
+from jobregister
+where id = ?
+and starttime is not null
+QUERY
+
+    $sth->execute( $dbid ) or die( $sth->errstr );
+
+    # This query is by primary key, so we can rely on only one row
+    # returned.
+    my $results = $sth->fetchrow_hashref();
+    my $success = $self->parse_job_phase( $results->{'PHASE'} );
+    my $endtime;
+    if ( defined( $results->{'ENDTIME'} ) ) {
+	my $date = ParseDate($results->{'ENDTIME'});
+	$endtime = UnixDate($date, "%Y-%m-%d %T");
+    }
+
+    return ( $endtime, $success );
+}
+
+sub cache_aerep_events : PRIVATE {
+
+    my ( $self ) = @_;
+
+    print STDOUT "Caching AE repository events...\n";
+
+    $self->cache_events( $AEREP_DB );
+
+    return;
+}
+
+sub cache_aedw_events : PRIVATE {
+
+    my ( $self ) = @_;
+
+    print STDOUT "Caching AE warehouse events...\n";
+
+    $self->cache_events( $AEDW_DB );
+
+    return;
+}
+
+sub cache_events : PRIVATE {
+
+    my ( $self, $instance ) = @_;
+
+    my $dbh = $self->get_dbhandle()->{ $instance }
+	or confess("Error: Undefined database handle ($instance).");
+
+    # Note that currently the AEDW and AEREP jobregister schemas are
+    # identical, so we use this code for both. We may need to change
+    # this if these schemas change.
+
+    # Populate the event_cache with ArrayExpress::Tracking::Event
+    # objects here.
+    my $sth = $dbh->prepare(<<"QUERY");
+select distinct ID, USERNAME, DIRNAME, JOBTYPE, STARTTIME, ENDTIME, PHASE
+from jobregister
+where id > ?
+QUERY
+
+    $sth->execute( $self->get_last_jobid()->{ $instance } || 0 )
+	or die( $sth->errstr() );
+
+    my $event = $self->get_event_cache() || {};
+
+    JOB:
+    while ( my $hashref = $sth->fetchrow_hashref() ) {
+
+	# FIXME this regexp will miss some accessions (e.g. E-TABM-145b).
+	my ( $accession )
+	    = ( $hashref->{'DIRNAME'} =~ m/\/([AE]-[A-Z]{4}-[0-9]+)/ );
+	next JOB unless $accession;
+
+	my $obj = $self->make_event_object( $hashref, $instance );
+	push( @{ $event->{$accession} }, $obj );
+    }
+
+    $self->set_event_cache($event);
+
+    return;
+}
+
+sub make_event_object : PRIVATE {
+
+    my ( $self, $hashref, $instance ) = @_;
+
+    # N.B. currently the AEREP and AEDW jobregister tables have
+    # identical columns; we may need to make database-specific
+    # versions of this method if that ever changes.
+
+    my $success = $self->parse_job_phase( $hashref->{'PHASE'} );
+
+    # Here we map the AEREP column names to our events. Attributes not
+    # set: source_db, machine, log_file, comment. Note that we need to
+    # parse the returned date format here.
+    foreach my $key qw(STARTTIME ENDTIME) {
+	my $date = ParseDate($hashref->{$key});
+	$hashref->{$key} = UnixDate($date, "%Y-%m-%d %T");
+    }
+
+    my $obj = ArrayExpress::Tracking::Event->new({
+	event_type       => $hashref->{'JOBTYPE'},
+	success          => $success,
+	target_db        => $instance,
+	starttime        => $hashref->{'STARTTIME'},
+	endtime          => $hashref->{'ENDTIME'},
+	operator         => $hashref->{'USERNAME'},
+	jobregister_dbid => $hashref->{'ID'},
+    });
+
+    return $obj;
+}
+
+sub parse_job_phase : PRIVATE {
+
+    my ( $self, $phase ) = @_;
+
+    # Return 1 if finished, 0 if crashed, or undef if unknown (assumed
+    # elsewhere to mean unfinished).
+
+    if    ( $phase && $phase =~ /\b (?: crashed | error | dead ) \b/ixms ) {
+
+	# Failure.
+	return 0;
+    }
+    elsif ( $phase && $phase =~ /\b (?: finish | finished ) \b/ixms ) {
+
+	# Success.
+	return 1;
+    }
+    else{
+
+	# Unfinished?
+	return;
+    }
+}
+
+1;
diff --git a/t/affy_calvin.t b/t/affy_calvin.t
new file mode 100644
index 0000000..bb9ef83
--- /dev/null
+++ b/t/affy_calvin.t
@@ -0,0 +1,205 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: affy_cel.t 1811 2007-11-02 10:34:42Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 22;
+
+use File::Spec;
+
+BEGIN {
+    use_ok ('ArrayExpress::Datafile::Affymetrix');
+    use_ok ('ArrayExpress::Datafile::Affymetrix::Calvin::Binary');
+    use_ok ('ArrayExpress::Datafile::Affymetrix::Calvin::Parameter');
+    use_ok ('ArrayExpress::Datafile::Affymetrix::Calvin::DataColumn');
+    use_ok ('ArrayExpress::Datafile::Affymetrix::Calvin::Component');
+    use_ok ('ArrayExpress::Datafile::Affymetrix::Calvin::DataHeader');
+    use_ok ('ArrayExpress::Datafile::Affymetrix::Calvin::DataSet');
+    use_ok ('ArrayExpress::Datafile::Affymetrix::Calvin::DataGroup');
+    use_ok ('ArrayExpress::Datafile::Affymetrix::Calvin::Generic');
+    use_ok ('ArrayExpress::Datafile::Affymetrix::Calvin::Facade');
+    use_ok ('ArrayExpress::Datafile::Affymetrix::Calvin::CHP');
+    use_ok ('ArrayExpress::Datafile::Affymetrix::Calvin::CEL');
+}
+
+# Use the factory class to generate a parser, hopefully of the right
+# type.
+my $fac = ArrayExpress::Datafile::Affymetrix->new();
+
+my $chp = $fac->make_parser( File::Spec->catfile( 't', 'data', 'test_calvin.CHP' ) );
+
+# Create the instance
+ok( defined $chp, 'new() returned a value' );
+ok( $chp->isa('ArrayExpress::Datafile::Affymetrix::Calvin::CHP'),
+    'of the correct class' );
+
+# Check our return values
+is( $chp->get_chip_type,    'HG-U133A',       'chip type' );
+is( $chp->get_algorithm,    'ExpressionStat', 'algorithm' );
+is( $chp->get_version,      '1',              'version' );
+
+# Parameters
+my $expected_params = {
+    'Alpha1'    => '0.05000',
+    'Alpha2'    => '0.06500',
+    'BG'        => '2',
+    'HZ'        => '4',
+    'NF'        => '1.00000',
+    'SF'        => '1.91583',
+    'ScaleMask' => 'All',
+    'TGT'       => '500',
+    'Tau'       => '0.01500',
+    'VZ'        => '4',
+};
+
+is_deeply( $chp->get_parameters, $expected_params, 'parameters' );
+
+# Statistics
+my $expected_stats = {
+    '#A'                                             => '10615',
+    '#M'                                             => '363',
+    '#P'                                             => '11305',
+    '#Probe Sets Exceeding Probe Pair Threshold'     => '22283',
+    '%A'                                             => '47.63721',
+    '%M'                                             => '1.62904',
+    '%P'                                             => '50.73374',
+    'BG Avg'                                         => '117.28572',
+    'BG Max'                                         => '124.08419',
+    'BG Min'                                         => '106.84966',
+    'BG Std'                                         => '3.34962',
+    'Central- Avg'                                   => '21585',
+    'Central- Count'                                 => '9',
+    'Control Direction'                              => 'Antisense',
+    'Corner+ Avg'                                    => '157',
+    'Corner+ Count'                                  => '32',
+    'Corner- Avg'                                    => '22492',
+    'Corner- Count'                                  => '32',
+    'Housekeeping_AFFX-HSAC07/X00351_3-5-ratio'      => '1.11354',
+    'Housekeeping_AFFX-HSAC07/X00351_3_detection'    => 'P',
+    'Housekeeping_AFFX-HSAC07/X00351_3_signal'       => '17738.18555',
+    'Housekeeping_AFFX-HSAC07/X00351_5_detection'    => 'P',
+    'Housekeeping_AFFX-HSAC07/X00351_5_signal'       => '15929.51465',
+    'Housekeeping_AFFX-HSAC07/X00351_M_detection'    => 'P',
+    'Housekeeping_AFFX-HSAC07/X00351_M_signal'       => '20702.16602',
+    'Housekeeping_AFFX-HSAC07/X00351_avg-signal'     => '18123.28906',
+    'Housekeeping_AFFX-HUMGAPDH/M33197_3-5-ratio'    => '1.53856',
+    'Housekeeping_AFFX-HUMGAPDH/M33197_3_detection'  => 'P',
+    'Housekeeping_AFFX-HUMGAPDH/M33197_3_signal'     => '13372.21094',
+    'Housekeeping_AFFX-HUMGAPDH/M33197_5_detection'  => 'P',
+    'Housekeeping_AFFX-HUMGAPDH/M33197_5_signal'     => '8691.37793',
+    'Housekeeping_AFFX-HUMGAPDH/M33197_M_detection'  => 'P',
+    'Housekeeping_AFFX-HUMGAPDH/M33197_M_signal'     => '9520.40723',
+    'Housekeeping_AFFX-HUMGAPDH/M33197_avg-signal'   => '10527.99902',
+    'Noise Avg'            => '6.49794',
+    'Noise Max'            => '7.35717',
+    'Noise Min'            => '5.61606',
+    'Noise Std'            => '0.29578',
+    'Probe Pair Threshold' => '1',
+    'RawQ'                 => '5.36501',
+    'Signal(A)'            => '65.11629',
+    'Signal(All)'          => '728.75806',
+    'Signal(M)'            => '211.16080',
+    'Signal(P)'            => '1368.51440',
+    'Spike_AFFX-r2-Bs-dap_3-5-ratio'   => '0.79929',
+    'Spike_AFFX-r2-Bs-dap_3_detection' => 'A',
+    'Spike_AFFX-r2-Bs-dap_3_signal'    => '2.09471',
+    'Spike_AFFX-r2-Bs-dap_5_detection' => 'A',
+    'Spike_AFFX-r2-Bs-dap_5_signal'    => '2.62071',
+    'Spike_AFFX-r2-Bs-dap_M_detection' => 'A',
+    'Spike_AFFX-r2-Bs-dap_M_signal'    => '25.02802',
+    'Spike_AFFX-r2-Bs-dap_avg-signal'  => '9.91448',
+    'Spike_AFFX-r2-Bs-lys_3-5-ratio'   => '2.06401',
+    'Spike_AFFX-r2-Bs-lys_3_detection' => 'P',
+    'Spike_AFFX-r2-Bs-lys_3_signal'    => '49.26870',
+    'Spike_AFFX-r2-Bs-lys_5_detection' => 'A',
+    'Spike_AFFX-r2-Bs-lys_5_signal'    => '23.87037',
+    'Spike_AFFX-r2-Bs-lys_M_detection' => 'A',
+    'Spike_AFFX-r2-Bs-lys_M_signal'    => '8.82265',
+    'Spike_AFFX-r2-Bs-lys_avg-signal'  => '27.32057',
+    'Spike_AFFX-r2-Bs-phe_3-5-ratio'   => '8.54006',
+    'Spike_AFFX-r2-Bs-phe_3_detection' => 'A',
+    'Spike_AFFX-r2-Bs-phe_3_signal'    => '44.97870',
+    'Spike_AFFX-r2-Bs-phe_5_detection' => 'A',
+    'Spike_AFFX-r2-Bs-phe_5_signal'    => '5.26679',
+    'Spike_AFFX-r2-Bs-phe_M_detection' => 'A',
+    'Spike_AFFX-r2-Bs-phe_M_signal'    => '18.66685',
+    'Spike_AFFX-r2-Bs-phe_avg-signal'  => '22.97078',
+    'Spike_AFFX-r2-Bs-thr_3-5-ratio'   => '0.10473',
+    'Spike_AFFX-r2-Bs-thr_3_detection' => 'A',
+    'Spike_AFFX-r2-Bs-thr_3_signal'    => '5.12869',
+    'Spike_AFFX-r2-Bs-thr_5_detection' => 'A',
+    'Spike_AFFX-r2-Bs-thr_5_signal'    => '48.96920',
+    'Spike_AFFX-r2-Bs-thr_M_detection' => 'A',
+    'Spike_AFFX-r2-Bs-thr_M_signal'    => '77.69570',
+    'Spike_AFFX-r2-Bs-thr_avg-signal'  => '43.93119',
+    'Spike_AFFX-r2-Ec-bioB_3-5-ratio'  => '0.91620',
+    'Spike_AFFX-r2-Ec-bioB_3_detection' => 'P',
+    'Spike_AFFX-r2-Ec-bioB_3_signal'    => '103.24037',
+    'Spike_AFFX-r2-Ec-bioB_5_detection' => 'P',
+    'Spike_AFFX-r2-Ec-bioB_5_signal'    => '112.68278',
+    'Spike_AFFX-r2-Ec-bioB_M_detection' => 'P',
+    'Spike_AFFX-r2-Ec-bioB_M_signal'    => '151.41463',
+    'Spike_AFFX-r2-Ec-bioB_avg-signal'  => '122.44592',
+    'Spike_AFFX-r2-Ec-bioC_3-5-ratio'   => '1.31688',
+    'Spike_AFFX-r2-Ec-bioC_3_detection' => 'P',
+    'Spike_AFFX-r2-Ec-bioC_3_signal'    => '580.93274',
+    'Spike_AFFX-r2-Ec-bioC_5_detection' => 'P',
+    'Spike_AFFX-r2-Ec-bioC_5_signal'    => '441.14432',
+    'Spike_AFFX-r2-Ec-bioC_avg-signal'  => '511.03851',
+    'Spike_AFFX-r2-Ec-bioD_3-5-ratio'   => '1.29075',
+    'Spike_AFFX-r2-Ec-bioD_3_detection' => 'P',
+    'Spike_AFFX-r2-Ec-bioD_3_signal'    => '4272.42627',
+    'Spike_AFFX-r2-Ec-bioD_5_detection' => 'P',
+    'Spike_AFFX-r2-Ec-bioD_5_signal'    => '3310.04663',
+    'Spike_AFFX-r2-Ec-bioD_avg-signal'  => '3791.23633',
+    'Spike_AFFX-r2-P1-cre_3-5-ratio'    => '1.06699',
+    'Spike_AFFX-r2-P1-cre_3_detection'  => 'P',
+    'Spike_AFFX-r2-P1-cre_3_signal'     => '7970.32422',
+    'Spike_AFFX-r2-P1-cre_5_detection'  => 'P',
+    'Spike_AFFX-r2-P1-cre_5_signal'     => '7469.91846',
+    'Spike_AFFX-r2-P1-cre_avg-signal'   => '7720.12109',
+};
+
+is_deeply( $chp->get_stats, $expected_stats, 'statistics' );
+
+# QTD
+my $expected_headings = [
+    'Probe Set Name',
+    'Detection',
+    'Detection p-value',
+    'Signal',
+    'Number of Pairs',
+    'Number of Pairs Used',
+];
+
+is_deeply( $chp->get_headings, $expected_headings, 'column headings' );
+
+my $expected_QTs = [
+    'Affymetrix:QuantitationType:Probe Set Name',
+    'Affymetrix:QuantitationType:Detection',
+    'Affymetrix:QuantitationType:Detection p-value',
+    'Affymetrix:QuantitationType:Signal',
+    'Affymetrix:QuantitationType:Number of Pairs',
+    'Affymetrix:QuantitationType:Number of Pairs Used',
+];
+
+is_deeply( $chp->get_qtd, $expected_QTs, 'quantitation type identifiers' );
+
+# DED
+my $expected_features = [
+    'Affymetrix:CompositeSequence:HG-U133A:AFFX-BioB-5_at',
+    'Affymetrix:CompositeSequence:HG-U133A:AFFX-BioB-M_at',
+    'Affymetrix:CompositeSequence:HG-U133A:AFFX-BioB-3_at',
+    'Affymetrix:CompositeSequence:HG-U133A:AFFX-BioC-5_at',
+    'Affymetrix:CompositeSequence:HG-U133A:AFFX-BioC-3_at',
+    'Affymetrix:CompositeSequence:HG-U133A:AFFX-BioDn-5_at',
+    'Affymetrix:CompositeSequence:HG-U133A:AFFX-BioDn-3_at',
+    'Affymetrix:CompositeSequence:HG-U133A:AFFX-CreX-5_at',
+    'Affymetrix:CompositeSequence:HG-U133A:AFFX-CreX-3_at',
+    'Affymetrix:CompositeSequence:HG-U133A:AFFX-DapX-5_at',
+];
+
+is_deeply( $chp->get_ded(), $expected_features,
+    'design element (compseq) identifiers' );
diff --git a/t/affy_cdf.t b/t/affy_cdf.t
new file mode 100644
index 0000000..3049ff7
--- /dev/null
+++ b/t/affy_cdf.t
@@ -0,0 +1,51 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: affy_cel.t 1799 2007-10-31 17:39:45Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 11;
+
+use File::Spec;
+BEGIN {
+    use_ok ('ArrayExpress::Datafile::Affymetrix');
+    use_ok ('ArrayExpress::Datafile::Affymetrix::CDF::GDAC_CDF');
+}
+
+# Use the factory class to generate a parser, hopefully of the right
+# type.
+my $fac = ArrayExpress::Datafile::Affymetrix->new();
+
+my $cdf = $fac->make_parser( File::Spec->catfile( 't', 'data', 'dummy.CDF' ) );
+
+# Create the instance
+ok( defined $cdf, 'new() returned a value' );
+ok( $cdf->isa('ArrayExpress::Datafile::Affymetrix::CDF::GDAC_CDF'),
+    'of the correct class' );
+
+# Parse the test file
+$cdf->parse();
+
+is( $cdf->get_version,      'GC3.0',  'version' );
+is( $cdf->get_chip_type,    'YG_S98', 'chip type' );
+is( $cdf->get_num_rows,     '534',    'number of rows' );
+is( $cdf->get_num_columns,  '534',    'number of columns' );
+is( $cdf->get_num_cells,    '10',     'expected number of cells' );
+is( $cdf->get_num_qc_cells, '0',      'expected number of qc cells' );
+
+my $expected_ids =
+    [qw(
+	AFFX-MurIL2_at
+	AFFX-MurIL10_at
+	AFFX-MurIL4_at
+	AFFX-MurFAS_at
+	AFFX-BioB-5_at
+	AFFX-BioB-M_at
+	AFFX-BioB-3_at
+	AFFX-BioC-5_at
+	AFFX-BioC-3_at
+	AFFX-BioDn-5_at
+    )];
+
+is_deeply( $cdf->get_probeset_ids, $expected_ids, 'probeset id list' );
diff --git a/t/affy_cel.t b/t/affy_cel.t
new file mode 100644
index 0000000..59006b8
--- /dev/null
+++ b/t/affy_cel.t
@@ -0,0 +1,108 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: affy_cel.t 1811 2007-11-02 10:34:42Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 18;
+
+use File::Spec;
+
+BEGIN {
+    use_ok ('ArrayExpress::Datafile::Affymetrix');
+    use_ok ('ArrayExpress::Datafile::Affymetrix::CEL::CELv3');
+}
+
+# Use the factory class to generate a parser, hopefully of the right
+# type.
+my $fac = ArrayExpress::Datafile::Affymetrix->new();
+
+my $cel = $fac->make_parser( File::Spec->catfile( 't', 'data', 'test.CEL' ) );
+
+# Create the instance
+ok( defined $cel, 'new() returned a value' );
+ok( $cel->isa('ArrayExpress::Datafile::Affymetrix::CEL::CELv3'),
+    'of the correct class' );
+
+# Parse the test file
+$cel->parse();
+
+# Check our return values
+is( $cel->get_chip_type,   'Test',       'chip type' );
+is( $cel->get_algorithm,   'Percentile', 'algorithm' );
+is( $cel->get_version,     '3',          'version' );
+is( $cel->get_num_cells,   '10',         'expected number of cells' );
+is( $cel->get_num_rows,    '1',          'number of rows' );
+is( $cel->get_num_columns, '10',         'number of columns' );
+is( $cel->get_num_masked,   '0', 'number of masked cells' );
+is( $cel->get_num_outliers, '3', 'number of outlier cells' );
+is( $cel->get_num_modified, '0', 'number of modified cells' );
+
+# Parameters
+my $expected_params = {
+    'Percentile'  => '75',
+    'CellMargin'  => '2',
+    'OutlierHigh' => '1.500',
+    'OutlierLow'  => '1.004',
+};
+
+is_deeply( $cel->get_parameters, $expected_params, 'parameters' );
+
+# Statistics
+my $expected_stats = {
+    'Number of Cells'       => '10',
+    'Rows'                  => '1',
+    'Columns'               => '10',
+    'Number Cells Masked'   => '0',
+    'Number Outlier Cells'  => '3',
+    'Number Cells Modified' => '0',
+};
+
+is_deeply( $cel->get_stats, $expected_stats, 'statistics' );
+
+# QTD
+my $expected_headings = [
+    qw(
+        CELX
+        CELY
+        CELIntensity
+        CELIntensityStdev
+        CELPixels
+        CELOutlier
+        CELMask
+        )
+];
+
+is_deeply( $cel->get_headings, $expected_headings, 'column headings' );
+
+my $expected_QTs = [
+    qw(
+        Affymetrix:QuantitationType:CELX
+        Affymetrix:QuantitationType:CELY
+        Affymetrix:QuantitationType:CELIntensity
+        Affymetrix:QuantitationType:CELIntensityStdev
+        Affymetrix:QuantitationType:CELPixels
+        Affymetrix:QuantitationType:CELOutlier
+        Affymetrix:QuantitationType:CELMask
+        )
+];
+
+is_deeply( $cel->get_qtd, $expected_QTs, 'quantitation type identifiers' );
+
+# DED
+my $expected_features = [
+    'Affymetrix:Feature:Test:Probe(0,0)',
+    'Affymetrix:Feature:Test:Probe(1,0)',
+    'Affymetrix:Feature:Test:Probe(2,0)',
+    'Affymetrix:Feature:Test:Probe(3,0)',
+    'Affymetrix:Feature:Test:Probe(4,0)',
+    'Affymetrix:Feature:Test:Probe(5,0)',
+    'Affymetrix:Feature:Test:Probe(6,0)',
+    'Affymetrix:Feature:Test:Probe(7,0)',
+    'Affymetrix:Feature:Test:Probe(8,0)',
+    'Affymetrix:Feature:Test:Probe(9,0)',
+];
+
+is_deeply( $cel->get_ded, $expected_features,
+    'design element (feature) identifiers' );
diff --git a/t/affy_chp.t b/t/affy_chp.t
new file mode 100644
index 0000000..907a5e2
--- /dev/null
+++ b/t/affy_chp.t
@@ -0,0 +1,118 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: affy_chp.t 1988 2008-03-10 10:43:48Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 17;
+
+use File::Spec;
+
+BEGIN {
+    use_ok('ArrayExpress::Datafile::Affymetrix');
+    use_ok('ArrayExpress::Datafile::Affymetrix::CHP::CHPv8');
+    use_ok('ArrayExpress::Datafile::Affymetrix::CHP::CHPv12');
+    use_ok('ArrayExpress::Datafile::Affymetrix::CHP::CHPv13');
+}
+
+# Use the factory class to generate a parser, hopefully of the right
+# type.
+my $fac = ArrayExpress::Datafile::Affymetrix->new();
+
+my $chp = $fac->make_parser( File::Spec->catfile( 't', 'data', 'test.CHP' ) );
+
+# Create the instance
+ok( defined $chp, 'new() returned a value' );
+ok( $chp->isa('ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP'),
+    'of the correct class' );
+
+# Parse the test file
+$chp->parse();
+
+# Check our return values
+is( $chp->get_chip_type,    'YG_S98',         'chip type' );
+is( $chp->get_algorithm,    'ExpressionStat', 'algorithm' );
+is( $chp->get_version,      '12',             'version' );
+is( $chp->get_num_cells,    '10',             'expected number of cells' );
+is( $chp->get_num_rows,     '534',            'number of rows' );
+is( $chp->get_num_columns,  '534',            'number of columns' );
+
+# Parameters
+my $expected_params = {
+    'Alpha1'       => '0.04',
+    'Alpha2'       => '0.06',
+    'Tau'          => '0.015',
+    'Gamma1H'      => '0.0025',
+    'Gamma1L'      => '0.0025',
+    'Gamma2H'      => '0.003',
+    'Gamma2L'      => '0.003',
+    'Perturbation' => '1.1',
+    'TGT'          => '100',
+    'NF'           => '1.000000',
+    'SF'           => '0.822926',
+    'SFGene'       => 'All',
+};
+
+is_deeply( $chp->get_parameters, $expected_params, 'parameters' );
+
+# Statistics
+my $expected_stats = {
+    'Background Avg'   => '60.39',
+    'Background Stdev' => '1.34',
+    'Background Max'   => '65.5',
+    'Background Min'   => '58.0',
+    'Noise Avg'        => '1.42',
+    'Noise Stdev'      => '0.05',
+    'Noise Max'        => '1.6',
+    'Noise Min'        => '1.3',
+    'RawQ'             => '1.86',
+};
+
+is_deeply( $chp->get_stats, $expected_stats, 'statistics' );
+
+# QTD
+my $expected_headings = [
+    qw(
+        ProbeSetName
+        CHPPairs
+        CHPPairsUsed
+        CHPSignal
+        CHPDetection
+        CHPDetectionPvalue
+        )
+];
+
+is_deeply( $chp->get_headings, $expected_headings, 'column headings' );
+
+my $expected_QTs = [
+    qw(
+        Affymetrix:QuantitationType:ProbeSetName
+        Affymetrix:QuantitationType:CHPPairs
+        Affymetrix:QuantitationType:CHPPairsUsed
+        Affymetrix:QuantitationType:CHPSignal
+        Affymetrix:QuantitationType:CHPDetection
+        Affymetrix:QuantitationType:CHPDetectionPvalue
+        )
+];
+
+is_deeply( $chp->get_qtd, $expected_QTs, 'quantitation type identifiers' );
+
+# DED
+my $expected_features = [
+    'Affymetrix:CompositeSequence:YG_S98:AFFX-MurIL2_at',
+    'Affymetrix:CompositeSequence:YG_S98:AFFX-MurIL10_at',
+    'Affymetrix:CompositeSequence:YG_S98:AFFX-MurIL4_at',
+    'Affymetrix:CompositeSequence:YG_S98:AFFX-MurFAS_at',
+    'Affymetrix:CompositeSequence:YG_S98:AFFX-BioB-5_at',
+    'Affymetrix:CompositeSequence:YG_S98:AFFX-BioB-M_at',
+    'Affymetrix:CompositeSequence:YG_S98:AFFX-BioB-3_at',
+    'Affymetrix:CompositeSequence:YG_S98:AFFX-BioC-5_at',
+    'Affymetrix:CompositeSequence:YG_S98:AFFX-BioC-3_at',
+    'Affymetrix:CompositeSequence:YG_S98:AFFX-BioDn-5_at',
+];
+
+my $cdf = $fac->make_parser( File::Spec->catfile( 't', 'data', 'dummy.CDF' ) );
+$cdf->parse();
+is_deeply( $chp->get_ded($cdf), $expected_features,
+    'design element (compseq) identifiers' );
diff --git a/t/affy_exp.t b/t/affy_exp.t
new file mode 100644
index 0000000..0321337
--- /dev/null
+++ b/t/affy_exp.t
@@ -0,0 +1,103 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: affy_exp.t 1811 2007-11-02 10:34:42Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 21;
+
+use File::Spec;
+
+BEGIN {
+    use_ok ('ArrayExpress::Datafile::Affymetrix');
+    use_ok ('ArrayExpress::Datafile::Affymetrix::EXP');
+}
+
+# Use the factory class to generate a parser, hopefully of the right
+# type.
+my $fac = ArrayExpress::Datafile::Affymetrix->new();
+
+my $exp = $fac->make_parser( File::Spec->catfile( 't', 'data', 'test.EXP' ) );
+
+# Create the instance
+ok( defined $exp, 'new() returned a value' );
+ok( $exp->isa('ArrayExpress::Datafile::Affymetrix::EXP'),
+    'of the correct class' );
+
+# Parse a test file
+$exp->parse();
+
+# Check our return values
+is( $exp->get_chip_type,    'HG-U133A',            'chip type' );
+is( $exp->get_chip_lot,     '12345678',            'chip lot' );
+is( $exp->get_version,      '1',                   'version' );
+is( $exp->get_operator,     'Gandalf',             'operator' );
+is( $exp->get_protocol,     'EukGE-WS2v4',         'protocol' );
+is( $exp->get_station,      '2',                   'station' );
+is( $exp->get_module,       '1',                   'module' );
+is( $exp->get_hyb_date,     'Sep 20 2003 01:10PM', 'hyb_date' );
+is( $exp->get_pixel_size,   '3',                   'pixel_size' );
+is( $exp->get_filter,       '570',                 'filter' );
+is( $exp->get_scan_temp,    '30',                  'scan_temp' );
+is( $exp->get_scan_date,    'Sep 20 2003 01:25PM', 'scan_date' );
+is( $exp->get_scanner_id,   '87654321',            'scanner_id' );
+is( $exp->get_num_scans,    '2',                   'num_scans' );
+is( $exp->get_scanner_type, 'HP',                  'scanner_type' );
+
+# Parameters, ordered and named
+my $expected_hyb_params = [
+    { 'Wash A1 Recovery Mixes'      => '0' },
+    { 'Wash A1 Temperature (C)'     => '25' },
+    { 'Number of Wash A1 Cycles'    => '10' },
+    { 'Mixes per Wash A1 Cycle'     => '2' },
+    { 'Wash B Recovery Mixes'       => '0' },
+    { 'Wash B Temperature (C)'      => '50' },
+    { 'Number of Wash B Cycles'     => '4' },
+    { 'Mixes per Wash B Cycle'      => '15' },
+    { 'Stain Temperature (C)'       => '25' },
+    { 'First Stain Time (seconds)'  => '600' },
+    { 'Wash A2 Recovery Mixes'      => '0' },
+    { 'Wash A2 Temperature (C)'     => '25' },
+    { 'Number of Wash A2 Cycles'    => '10' },
+    { 'Mixes per Wash A2 Cycle'     => '4' },
+    { 'Second Stain Time (seconds)' => '600' },
+    { 'Third Stain Time (seconds)'  => '600' },
+    { 'Wash A3 Recovery Mixes'      => '0' },
+    { 'Wash A3 Temperature (C)'     => '30' },
+    { 'Number of Wash A3 Cycles'    => '15' },
+    { 'Mixes per Wash A3 Cycle'     => '4' },
+    { 'Holding Temperature (C)'     => '25' },
+];
+
+is_deeply( $exp->get_hyb_parameters, $expected_hyb_params,
+    'named hybridization parameters' );
+
+# Parameters, named as in MAGE-ML
+my $expected_params = {
+    'HybridizationStep0-EukGE-WS2v4'  => '0',
+    'HybridizationStep1-EukGE-WS2v4'  => '25',
+    'HybridizationStep2-EukGE-WS2v4'  => '10',
+    'HybridizationStep3-EukGE-WS2v4'  => '2',
+    'HybridizationStep4-EukGE-WS2v4'  => '0',
+    'HybridizationStep5-EukGE-WS2v4'  => '50',
+    'HybridizationStep6-EukGE-WS2v4'  => '4',
+    'HybridizationStep7-EukGE-WS2v4'  => '15',
+    'HybridizationStep8-EukGE-WS2v4'  => '25',
+    'HybridizationStep9-EukGE-WS2v4'  => '600',
+    'HybridizationStep10-EukGE-WS2v4' => '0',
+    'HybridizationStep11-EukGE-WS2v4' => '25',
+    'HybridizationStep12-EukGE-WS2v4' => '10',
+    'HybridizationStep13-EukGE-WS2v4' => '4',
+    'HybridizationStep14-EukGE-WS2v4' => '600',
+    'HybridizationStep15-EukGE-WS2v4' => '600',
+    'HybridizationStep16-EukGE-WS2v4' => '0',
+    'HybridizationStep17-EukGE-WS2v4' => '30',
+    'HybridizationStep18-EukGE-WS2v4' => '15',
+    'HybridizationStep19-EukGE-WS2v4' => '4',
+    'HybridizationStep20-EukGE-WS2v4' => '25',
+};
+
+is_deeply( $exp->get_parameters, $expected_params,
+    'numbered hybridization parameters' );
+
diff --git a/t/affy_xda_cdf.t b/t/affy_xda_cdf.t
new file mode 100644
index 0000000..d328670
--- /dev/null
+++ b/t/affy_xda_cdf.t
@@ -0,0 +1,26 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: affy_cel.t 1799 2007-10-31 17:39:45Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 4;
+
+use File::Spec;
+BEGIN {
+    use_ok ('ArrayExpress::Datafile::Affymetrix');
+    use_ok ('ArrayExpress::Datafile::Affymetrix::CDF::XDA_CDF');
+}
+
+# Very basic testing until such time as we get a good small test XDA
+# CDF file.
+my $cdf = ArrayExpress::Datafile::Affymetrix::CDF::XDA_CDF->new({
+    input => 'not a file',
+});
+
+# Create the instance
+ok( defined $cdf, 'new() returned a value' );
+ok( $cdf->isa('ArrayExpress::Datafile::Affymetrix::CDF::XDA_CDF'),
+    'of the correct class' );
+
diff --git a/t/affy_xda_cel.t b/t/affy_xda_cel.t
new file mode 100644
index 0000000..a606a4b
--- /dev/null
+++ b/t/affy_xda_cel.t
@@ -0,0 +1,103 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: affy_xda_cel.t 1811 2007-11-02 10:34:42Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 17;
+
+use File::Spec;
+BEGIN {
+    use_ok('ArrayExpress::Datafile::Affymetrix');
+    use_ok('ArrayExpress::Datafile::Affymetrix::CEL::CELv4');
+}
+
+my $fac = ArrayExpress::Datafile::Affymetrix->new();
+
+my $cel = $fac->make_parser( File::Spec->catfile( 't', 'data', 'test_xda.CEL' ) );
+
+# Create the instance
+ok( defined $cel, 'new() returned a value' );
+ok( $cel->isa('ArrayExpress::Datafile::Affymetrix::CEL::CELv4'),
+    'of the correct class' );
+
+# Parse a test file
+$cel->parse();
+
+# Check our return values
+is( $cel->get_chip_type,   'HG-U133A',   'chip type' );
+is( $cel->get_algorithm,   'Percentile', 'algorithm' );
+is( $cel->get_version,     '4',          'version' );
+is( $cel->get_num_cells,   '10',         'expected number of cells' );
+is( $cel->get_num_rows,    '1',          'number of rows' );
+is( $cel->get_num_columns, '10',         'number of columns' );
+is( $cel->get_num_masked,   '0', 'number of masked cells' );
+is( $cel->get_num_outliers, '3', 'number of outlier cells' );
+
+# Parameters
+my $expected_params = {
+    'Percentile'  => '75',
+    'CellMargin'  => '2',
+    'OutlierHigh' => '1.500',
+    'OutlierLow'  => '1.004',
+};
+
+is_deeply( $cel->get_parameters, $expected_params, 'parameters' );
+
+# Statistics
+my $expected_stats = {
+    'Number of Cells'      => '10',
+    'Rows'                 => '1',
+    'Columns'              => '10',
+    'Number Cells Masked'  => '0',
+    'Number Outlier Cells' => '3',
+};
+
+is_deeply( $cel->get_stats, $expected_stats, 'statistics' );
+
+# QTD
+my $expected_headings = [
+    qw(
+        CELX
+        CELY
+        CELIntensity
+        CELIntensityStdev
+        CELPixels
+        CELOutlier
+        CELMask
+        )
+];
+
+is_deeply( $cel->get_headings, $expected_headings, 'column headings' );
+
+my $expected_QTs = [
+    qw(
+        Affymetrix:QuantitationType:CELX
+        Affymetrix:QuantitationType:CELY
+        Affymetrix:QuantitationType:CELIntensity
+        Affymetrix:QuantitationType:CELIntensityStdev
+        Affymetrix:QuantitationType:CELPixels
+        Affymetrix:QuantitationType:CELOutlier
+        Affymetrix:QuantitationType:CELMask
+        )
+];
+
+is_deeply( $cel->get_qtd, $expected_QTs, 'quantitation type identifiers' );
+
+# DED
+my $expected_features = [
+    'Affymetrix:Feature:HG-U133A:Probe(0,0)',
+    'Affymetrix:Feature:HG-U133A:Probe(1,0)',
+    'Affymetrix:Feature:HG-U133A:Probe(2,0)',
+    'Affymetrix:Feature:HG-U133A:Probe(3,0)',
+    'Affymetrix:Feature:HG-U133A:Probe(4,0)',
+    'Affymetrix:Feature:HG-U133A:Probe(5,0)',
+    'Affymetrix:Feature:HG-U133A:Probe(6,0)',
+    'Affymetrix:Feature:HG-U133A:Probe(7,0)',
+    'Affymetrix:Feature:HG-U133A:Probe(8,0)',
+    'Affymetrix:Feature:HG-U133A:Probe(9,0)',
+];
+
+is_deeply( $cel->get_ded, $expected_features,
+    'design element (feature) identifiers' );
diff --git a/t/affy_xda_chp.t b/t/affy_xda_chp.t
new file mode 100644
index 0000000..11645d9
--- /dev/null
+++ b/t/affy_xda_chp.t
@@ -0,0 +1,127 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: affy_xda_chp.t 1988 2008-03-10 10:43:48Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 15;
+
+use File::Spec;
+
+BEGIN {
+    use_ok('ArrayExpress::Datafile::Affymetrix');
+    use_ok('ArrayExpress::Datafile::Affymetrix::CHP::XDA_CHP');
+}
+
+# Use the factory class to generate a parser, hopefully of the right
+# type.
+my $fac = ArrayExpress::Datafile::Affymetrix->new();
+
+my $chp = $fac->make_parser( File::Spec->catfile( 't', 'data', 'test_xda.CHP' ) );
+
+# Create the instance
+ok( defined $chp, 'new() returned a value' );
+ok( $chp->isa('ArrayExpress::Datafile::Affymetrix::CHP::XDA_CHP'),
+    'of the correct class' );
+
+# Parse the test file
+$chp->parse();
+
+# Check our return values
+is( $chp->get_chip_type,    'ATH1-121501',    'chip type' );
+is( $chp->get_algorithm,    'ExpressionStat', 'algorithm' );
+is( $chp->get_version,      '1',              'version' );
+is( $chp->get_num_cells,    '10',             'expected number of cells' );
+is( $chp->get_num_rows,     '712',            'number of rows' );
+is( $chp->get_num_columns,  '712',            'number of columns' );
+
+# Parameters
+my $expected_params = {
+    'VZ'             => '4',
+    'Gamma1H'        => '0.0045',
+    'Gamma2L'        => '0.006',
+    'Epsilon'        => '0.5',
+    'TGT'            => '200',
+    'Perturbation'   => '1.1',
+    'Alpha2'         => '0.065',
+    'BG'             => '4',
+    'Gamma2H'        => '0.006',
+    'SF'             => '0.944273293018',
+    'Alpha1'         => '0.05',
+    'SFGene'         => 'All',
+    'HZ'             => '4',
+    'NF'             => '1.000000000000',
+    'SmoothFactorBG' => '100',
+    'Gamma1L'        => '0.0045',
+    'Tau'            => '0.015',
+};
+
+is_deeply( $chp->get_parameters, $expected_params, 'parameters' );
+
+# Statistics
+my $expected_stats = {
+    'Corner+ Count'    => '32',
+    'Background Avg'   => '55.63',
+    'Central- Avg'     => '13530',
+    'Noise Max'        => '3.1',
+    'Noise Stdev'      => '0.08',
+    'Central- Count'   => '9',
+    'RawQ'             => '2.32',
+    'Corner+ Avg'      => '140',
+    'Noise Min'        => '2.6',
+    'Background Min'   => '54.1',
+    'Background Stdev' => '0.67',
+    'Corner- Avg'      => '15007',
+    'Background Max'   => '58.3',
+    'Noise Avg'        => '2.76',
+    'Corner- Count'    => '32',
+};
+
+is_deeply( $chp->get_stats, $expected_stats, 'statistics' );
+
+# QTD
+my $expected_headings = [
+    qw(
+        ProbeSetName
+        CHPPairs
+        CHPPairsUsed
+        CHPSignal
+        CHPDetection
+        CHPDetectionPvalue
+        )
+];
+
+is_deeply( $chp->get_headings, $expected_headings, 'column headings' );
+
+my $expected_QTs = [
+    qw(
+        Affymetrix:QuantitationType:ProbeSetName
+        Affymetrix:QuantitationType:CHPPairs
+        Affymetrix:QuantitationType:CHPPairsUsed
+        Affymetrix:QuantitationType:CHPSignal
+        Affymetrix:QuantitationType:CHPDetection
+        Affymetrix:QuantitationType:CHPDetectionPvalue
+        )
+];
+
+is_deeply( $chp->get_qtd, $expected_QTs, 'quantitation type identifiers' );
+
+# DED
+my $expected_features = [
+    'Affymetrix:CompositeSequence:ATH1-121501:AFFX-MurIL2_at',
+    'Affymetrix:CompositeSequence:ATH1-121501:AFFX-MurIL10_at',
+    'Affymetrix:CompositeSequence:ATH1-121501:AFFX-MurIL4_at',
+    'Affymetrix:CompositeSequence:ATH1-121501:AFFX-MurFAS_at',
+    'Affymetrix:CompositeSequence:ATH1-121501:AFFX-BioB-5_at',
+    'Affymetrix:CompositeSequence:ATH1-121501:AFFX-BioB-M_at',
+    'Affymetrix:CompositeSequence:ATH1-121501:AFFX-BioB-3_at',
+    'Affymetrix:CompositeSequence:ATH1-121501:AFFX-BioC-5_at',
+    'Affymetrix:CompositeSequence:ATH1-121501:AFFX-BioC-3_at',
+    'Affymetrix:CompositeSequence:ATH1-121501:AFFX-BioDn-5_at',
+];
+
+my $cdf = $fac->make_parser( File::Spec->catfile( 't', 'data', 'dummy.CDF' ) );
+$cdf->parse();
+is_deeply( $chp->get_ded($cdf), $expected_features,
+    'design element (compseq) identifiers' );
diff --git a/t/array_design.t b/t/array_design.t
new file mode 100644
index 0000000..db2757e
--- /dev/null
+++ b/t/array_design.t
@@ -0,0 +1,28 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: database.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 9;
+
+BEGIN { use_ok( 'ArrayExpress::Datafile::ArrayDesign' ); }
+
+my $array = ArrayExpress::Datafile::ArrayDesign->new();
+ok( defined $array, 'new() returned a value' );
+ok( $array->isa('ArrayExpress::Datafile::ArrayDesign'), 'of the correct class' );
+
+# Simple hashref attributes.
+my %testhash = (one => 1, two => 'two', three => 'III');
+foreach my $attr qw(features
+		    reporters
+		    compseqs) {
+    my $getter = "get_$attr";
+    is_deeply($array->$getter(), {}, "$attr initial value");
+
+    my $setter = "set_$attr";
+    $array->$setter(\%testhash);
+    is_deeply($array->$getter(), \%testhash, "$attr set value");
+}
+
diff --git a/t/arraymage.t b/t/arraymage.t
new file mode 100644
index 0000000..b14fd9e
--- /dev/null
+++ b/t/arraymage.t
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: arraymage.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+BEGIN { use_ok( 'ArrayExpress::ArrayMAGE' ); }
+
diff --git a/t/binary.t b/t/binary.t
new file mode 100644
index 0000000..8feca0c
--- /dev/null
+++ b/t/binary.t
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: binary.t 1269 2006-12-10 12:23:13Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+BEGIN {
+    use_ok(
+	'ArrayExpress::Datafile::Binary',
+	qw(
+	   get_integer
+	   get_DWORD
+	   get_unsigned_short
+	   get_unsigned_char
+	   get_signed_char
+	   get_float
+	   get_ascii
+	   get_hexadecimal
+       ));
+}
diff --git a/t/common.t b/t/common.t
new file mode 100644
index 0000000..f6bafcd
--- /dev/null
+++ b/t/common.t
@@ -0,0 +1,123 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: common.t 1273 2006-12-11 16:40:34Z tfrayner $
+
+use strict;
+use warnings;
+use charnames qw(:full);
+
+use File::Spec;
+use Test::More tests => 22;
+
+BEGIN {
+    use_ok(
+        'ArrayExpress::Curator::Common',
+        qw(get_indexcol
+          strip_discards
+          round date_now
+          check_linebreaks
+          clean_hash
+          )
+    );
+    use_ok(
+        'ArrayExpress::Curator::Config',
+        qw($CONFIG)
+    );
+}
+
+my @test_array = qw(One Two Three Four);
+
+##################
+# get_indexcol() #
+##################
+my @get_indexcol_data = (
+    { query => 'Two',   result => 1 },
+    { query => 'Three', result => 2 },
+    { query => 'Too',   result => -1 }
+);
+
+foreach my $test (@get_indexcol_data) {
+    is( get_indexcol( \@test_array, $test->{query} ),
+        $test->{result}, 'get_indexcol() matches' );
+}
+
+####################
+# strip_discards() #
+####################
+my @strip_discards_data = (
+    { query => [0], result => [qw(Two Three Four)] },
+    { query => [ 0, 1 ], result => [qw(Three Four)] },
+    { query => [ 1, 2 ], result => [qw(One Four)] },
+    { query => [ 1, 3 ], result => [qw(One Three)] }
+);
+
+foreach my $test (@strip_discards_data) {
+    is_deeply( strip_discards( $test->{query}, \@test_array ),
+        $test->{result}, 'strip_discards() matches' );
+}
+
+###########
+# round() #
+###########
+my @round_data = (
+    { query => [ 1,           1 ],  result => 1 },
+    { query => [ 1.0,         1 ],  result => 1 },
+    { query => [ 1.123,       0 ],  result => 1 },
+    { query => [ 1.125,       2 ],  result => 1.13 },
+    { query => [ 1.123456789, 5 ],  result => 1.12346 },
+    { query => [ 1.123,       -1 ], result => 0 },
+    { query => [ 1234.123,    -2 ], result => 1200 },
+);
+
+foreach my $test (@round_data) {
+    is( round( @{ $test->{query} } ), $test->{result}, 'round()' );
+}
+
+##############
+# date_now() #
+##############
+# Just check the basic format returned.
+ok( date_now() =~ m{\A \d{4}-\d{2}-\d{2} T \d{2}:\d{2}:\d{2} Z \z}xms,
+    'date_now() returns correct format' );
+
+######################
+# check_linebreaks() #
+######################
+
+# DOS
+my ( $counts, $line_ending ) =
+  check_linebreaks( File::Spec->catfile( 't', 'data', 'test.CEL' ) );
+is(
+    $line_ending,
+    "\N{CARRIAGE RETURN}\N{LINE FEED}",
+    'check_linebreaks() recognises DOS file'
+);
+is_deeply(
+    $counts,
+    { dos => 49, unix => 0, mac => 0 },
+    'check_linebreaks() returns correct line counts'
+);
+
+# Unix
+( $counts, $line_ending ) =
+  check_linebreaks( File::Spec->catfile( 't', 'data', 'test.txt' ) );
+is( $line_ending, "\N{LINE FEED}", 'check_linebreaks() recognises Unix file' );
+is_deeply(
+    $counts,
+    { dos => 0, unix => 8, mac => 0 },
+    'check_linebreaks() returns correct line counts'
+);
+
+# TODO: Mac
+
+################
+# clean_hash() #
+################
+is_deeply(
+    clean_hash( { one => 1, two => '', three => undef } ),
+    { one => 1 },
+    'clean_hash()'
+);
+
+# TODO: all the regexp patterns.
+
diff --git a/t/config.t b/t/config.t
new file mode 100644
index 0000000..c825762
--- /dev/null
+++ b/t/config.t
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: config.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+BEGIN { use_ok( 'ArrayExpress::Curator::Config' ); }
+
diff --git a/t/creator.t b/t/creator.t
new file mode 100644
index 0000000..5795702
--- /dev/null
+++ b/t/creator.t
@@ -0,0 +1,20 @@
+#!/usr/bin/env perl -wT
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+SKIP: {
+
+    eval {
+	require Class::DBI;
+    };
+
+    skip 'Class::DBI not installed',
+	1 if $@;
+
+    require_ok( 'ArrayExpress::AutoSubmission::Creator' );
+
+}
+
diff --git a/t/daemon.t b/t/daemon.t
new file mode 100644
index 0000000..1cb0d45
--- /dev/null
+++ b/t/daemon.t
@@ -0,0 +1,26 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: daemon.t 2069 2008-06-04 14:33:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+SKIP: {
+
+    eval {
+	require Class::DBI;
+	require MIME::Lite;
+	require Archive::Tar;
+	require Archive::Zip;
+	require Proc::Daemon;
+    };
+
+    skip 'MIME::Lite, Archive::Tar, Archive::Zip, Proc::Daemon or Class::DBI not installed',
+	1 if $@;
+
+    require_ok( 'ArrayExpress::AutoSubmission::Daemon' );
+
+}
+
diff --git a/t/daemon_checker.t b/t/daemon_checker.t
new file mode 100644
index 0000000..b02434d
--- /dev/null
+++ b/t/daemon_checker.t
@@ -0,0 +1,24 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: daemon.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 3;
+
+SKIP: {
+
+    eval {
+	require ArrayExpress::AutoSubmission::Daemon;
+    };
+
+    skip 'Parent class ArrayExpress::AutoSubmission::Daemon not available.',
+	3 if $@;
+
+    require_ok( 'ArrayExpress::AutoSubmission::Daemon::Checker' );
+    require_ok( 'ArrayExpress::AutoSubmission::Daemon::T2MChecker' );
+    require_ok( 'ArrayExpress::AutoSubmission::Daemon::MAGETABChecker' );
+
+}
+
diff --git a/t/daemon_exporter.t b/t/daemon_exporter.t
new file mode 100644
index 0000000..a842979
--- /dev/null
+++ b/t/daemon_exporter.t
@@ -0,0 +1,24 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: daemon.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 3;
+
+SKIP: {
+
+    eval {
+	require ArrayExpress::AutoSubmission::Daemon;
+    };
+
+    skip 'Parent class ArrayExpress::AutoSubmission::Daemon not available.',
+	3 if $@;
+
+    require_ok( 'ArrayExpress::AutoSubmission::Daemon::Exporter' );
+    require_ok( 'ArrayExpress::AutoSubmission::Daemon::MAGETABExporter' );
+    require_ok( 'ArrayExpress::AutoSubmission::Daemon::T2MExporter' );
+
+}
+
diff --git a/t/daemon_mxchecker.t b/t/daemon_mxchecker.t
new file mode 100644
index 0000000..e2e8fab
--- /dev/null
+++ b/t/daemon_mxchecker.t
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: daemon.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+SKIP: {
+
+    eval {
+	require ArrayExpress::AutoSubmission::Daemon;
+	require DBD::mysql;
+    };
+
+    skip 'DBD::mysql not installed, or ArrayExpress::AutoSubmission::Daemon superclass unavailable.',
+	1 if $@;
+
+    require_ok( 'ArrayExpress::AutoSubmission::Daemon::MXChecker' );
+
+}
+
diff --git a/t/daemon_mxexporter.t b/t/daemon_mxexporter.t
new file mode 100644
index 0000000..98a86b4
--- /dev/null
+++ b/t/daemon_mxexporter.t
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: daemon.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+SKIP: {
+
+    eval {
+	require ArrayExpress::AutoSubmission::Daemon;
+	require DBD::mysql;
+    };
+
+    skip 'DBD::mysql not installed, or ArrayExpress::AutoSubmission::Daemon superclass unavailable.',
+	1 if $@;
+
+    require_ok( 'ArrayExpress::AutoSubmission::Daemon::MXExporter' );
+
+}
+
diff --git a/t/data/dummy.CDF b/t/data/dummy.CDF
new file mode 100644
index 0000000..a6d4b85
--- /dev/null
+++ b/t/data/dummy.CDF
@@ -0,0 +1,192 @@
+[CDF]
+Version=GC3.0
+
+[Chip]
+Name=YG_S98
+Rows=534
+Cols=534
+NumberOfUnits=10
+MaxUnit=9375
+NumQCUnits=0
+ChipReference=
+
+[Unit1]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=1
+UnitType=3
+NumberBlocks=1
+
+[Unit1_Block1]
+Name=AFFX-MurIL2_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit2]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=2
+UnitType=3
+NumberBlocks=1
+
+[Unit2_Block1]
+Name=AFFX-MurIL10_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit3]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=3
+UnitType=3
+NumberBlocks=1
+
+[Unit3_Block1]
+Name=AFFX-MurIL4_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit4]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=4
+UnitType=3
+NumberBlocks=1
+
+[Unit4_Block1]
+Name=AFFX-MurFAS_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit5]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=5
+UnitType=3
+NumberBlocks=1
+
+[Unit5_Block1]
+Name=AFFX-BioB-5_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit6]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=6
+UnitType=3
+NumberBlocks=1
+
+[Unit6_Block1]
+Name=AFFX-BioB-M_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit7]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=7
+UnitType=3
+NumberBlocks=1
+
+[Unit7_Block1]
+Name=AFFX-BioB-3_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit8]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=8
+UnitType=3
+NumberBlocks=1
+
+[Unit8_Block1]
+Name=AFFX-BioC-5_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit9]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=9
+UnitType=3
+NumberBlocks=1
+
+[Unit9_Block1]
+Name=AFFX-BioC-3_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit10]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=10
+UnitType=3
+NumberBlocks=1
+
+[Unit10_Block1]
+Name=AFFX-BioDn-5_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
diff --git a/t/data/test.CEL b/t/data/test.CEL
new file mode 100644
index 0000000..368eedf
--- /dev/null
+++ b/t/data/test.CEL
@@ -0,0 +1,49 @@
+[CEL]
+Version=3
+
+[HEADER]
+Cols=10
+Rows=1
+TotalX=10
+TotalY=1
+OffsetX=0
+OffsetY=0
+GridCornerUL=233 236
+GridCornerUR=4500 251
+GridCornerLR=4487 4516
+GridCornerLL=221 4501
+Axis-invertX=0
+AxisInvertY=0
+swapXY=0
+DatHeader=[0..46093]  test 1:CLS=4733 RWS=4733 XIN=3  YIN=3  VE=17        2.0 03/27/03 23:53:16       Test.1sq                  6
+Algorithm=Percentile
+AlgorithmParameters=Percentile:75;CellMargin:2;OutlierHigh:1.500;OutlierLow:1.004
+
+[INTENSITY]
+NumberCells=10
+CellHeader=X	Y	MEAN	STDV	NPIXELS
+  0	  0	158.0	28.9	 36
+  1	  0	8875.5	1390.6	 36
+  2	  0	160.0	25.3	 36
+  3	  0	8552.5	1027.3	 36
+  4	  0	78.8	15.6	 36
+  5	  0	138.8	25.5	 36
+  6	  0	8124.0	1124.9	 36
+  7	  0	146.3	22.9	 36
+  8	  0	7531.3	1038.5	 36
+  9	  0	132.3	21.7	 36
+
+[MASKS]
+NumberCells=0
+CellHeader=X	Y
+
+[OUTLIERS]
+NumberCells=3
+CellHeader=X	Y
+3	0
+6	0
+9	0
+
+[MODIFIED]
+NumberCells=0
+CellHeader=X	Y	ORIGMEAN
diff --git a/t/data/test.CHP b/t/data/test.CHP
new file mode 100644
index 0000000..7df0f68
Binary files /dev/null and b/t/data/test.CHP differ
diff --git a/t/data/test.EXP b/t/data/test.EXP
new file mode 100644
index 0000000..8c93765
--- /dev/null
+++ b/t/data/test.EXP
@@ -0,0 +1,49 @@
+Affymetrix GeneChip Experiment Information
+Version	1
+
+[Sample Info]
+Chip Type	HG-U133A
+Chip Lot	12345678
+Operator	Gandalf
+Sample Type	
+Description	
+Project	Test
+Comments	
+Solution Type	
+Solution Lot	
+
+[Fluidics]
+Protocol	EukGE-WS2v4
+Wash A1 Recovery Mixes	0
+Wash A1 Temperature (C)	25
+Number of Wash A1 Cycles	10
+Mixes per Wash A1 Cycle	2
+Wash B Recovery Mixes	0
+Wash B Temperature (C)	50
+Number of Wash B Cycles	4
+Mixes per Wash B Cycle	15
+Stain Temperature (C)	25
+First Stain Time (seconds)	600
+Wash A2 Recovery Mixes	0
+Wash A2 Temperature (C)	25
+Number of Wash A2 Cycles	10
+Mixes per Wash A2 Cycle	4
+Second Stain Time (seconds)	600
+Third Stain Time (seconds)	600
+Wash A3 Recovery Mixes	0
+Wash A3 Temperature (C)	30
+Number of Wash A3 Cycles	15
+Mixes per Wash A3 Cycle	4
+Holding Temperature (C)	25
+Station	2
+Module	1
+Hybridize Date	Sep 20 2003 01:10PM
+
+[Scanner]
+Pixel Size	3
+Filter	570
+Scan Temperature	30
+Scan Date	Sep 20 2003 01:25PM
+Scanner ID	87654321
+Number of Scans	2
+Scanner Type	HP
diff --git a/t/data/test.gpr b/t/data/test.gpr
new file mode 100644
index 0000000..79564e8
--- /dev/null
+++ b/t/data/test.gpr
@@ -0,0 +1,37 @@
+ATF	1.0
+24	43    
+"Type=GenePix Results 1.2"
+"DateTime=2003/01/23 11:27:59"
+"Settings=E:\settings files\array.gps"
+"GalFile="
+"Scanner=GenePix 4000A"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm	532 nm"
+"FileName=\\Server\Data1.tif"
+"PMTVolts=600	590"
+"NormalizationFactor:RatioOfMedians=1.90064"
+"NormalizationFactor:RatioOfMeans=1.90384"
+"NormalizationFactor:MedianOfRatios=1.86554"
+"NormalizationFactor:MeanOfRatios=1.23878"
+"NormalizationFactor:RegressionRatio=2.45261"
+"JpegImage=\\Server\Data1.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=1240, 6320"
+"JpegOrigin=1840, 7160"
+"Creator=GenePix Pro 3.0.0.98"
+"Temperature=1.41243"
+"LaserPower=2.19092	0.863674"
+"LaserOnTime=86000	85977"
+"Block"	"Column"	"Row"	"Name"	"ID"	"X"	"Y"	"Dia."	"F635 Median"	"F635 Mean"	"F635 SD"	"B635 Median"	"B635 Mean"	"B635 SD"	"% > B635+1SD"	"% > B635+2SD"	"F635 % Sat."	"F532 Median"	"F532 Mean"	"F532 SD"	"B532notQTMedian"	"B532 Mean"	"B532 SD"	"% > B532+1SD"	"% > B532+2SD"	"F532 % Sat."	"Ratio of Medians"	"Ratio of Means"	"Median of Ratios"	"Mean of Ratios"	"Ratios SD"	"Rgn Ratio"	"Rgn R�"	"F Pixels"	"B Pixels"	"Sum of Medians"	"Sum of Means"	"Log Ratio"	"F635 Median - B635"	"F532 Median - [...]
+1	1	1			2100	7300	90	240	240	54	40	44	21	100	100	0	502	497	98	81	86	32	100	100	0	0.475	0.481	0.494	0.491	0.129	0.413	0.731	52	376	621	616	-1.074	200	421	200	416	0
+1	2	1			2410	7300	120	694	671	209	40	41	11	98	98	0	1406	1354	413	81	81	20	100	100	0	0.494	0.496	0.500	0.599	0.409	0.433	0.777	120	778	1979	1904	-1.019	654	1325	631	1273	0
+1	3	1			2690	7320	120	501	531	267	41	41	10	99	99	0	998	1080	498	79	82	21	99	98	0	0.501	0.490	0.493	0.641	1.280	0.460	0.837	120	810	1379	1491	-0.998	460	919	490	1001	0
+1	4	1			2980	7310	120	783	770	284	40	41	10	99	96	0	1034	1014	331	81	83	22	100	99	0	0.780	0.782	0.819	1.372	3.252	0.675	0.722	120	796	1696	1663	-0.359	743	953	730	933	0
+1	5	1			3260	7320	130	457	434	219	40	41	10	100	99	0	998	938	493	81	83	21	100	98	0	0.455	0.460	0.450	0.600	0.632	0.382	0.751	120	930	1334	1251	-1.137	417	917	394	857	0
+1	6	1			3570	7310	130	166	172	67	40	41	10	94	94	0	382	388	112	82	83	20	99	98	0	0.420	0.431	0.473	0.696	2.121	0.362	0.589	120	914	426	438	-1.252	126	300	132	306	0
+1	7	1			3860	7320	100	63	65	17	40	41	10	76	55	0	182	188	53	79	80	20	100	96	0	0.223	0.229	0.226	0.313	0.289	0.160	0.283	80	552	126	134	-2.163	23	103	25	109	0
+1	8	1			4130	7330	120	415	392	122	40	41	10	96	95	0	793	779	215	79	81	20	98	97	0	0.525	0.503	0.522	0.741	1.710	0.445	0.754	120	816	1089	1052	-0.929	375	714	352	700	0
+1	9	1			4430	7320	120	153	155	61	39	41	10	97	94	0	374	382	125	80	82	20	99	99	0	0.388	0.384	0.368	0.443	0.369	0.351	0.721	120	796	408	418	-1.367	114	294	116	302	0
+1	10	1			4730	7330	120	865	826	343	40	41	10	100	98	0	1011	941	389	79	81	21	99	98	0	0.885	0.912	0.923	1.477	3.031	0.751	0.698	120	810	1757	1648	-0.176	825	932	786	862	0
diff --git a/t/data/test.txt b/t/data/test.txt
new file mode 100644
index 0000000..27e69e8
--- /dev/null
+++ b/t/data/test.txt
@@ -0,0 +1,8 @@
+Reporter Identifier	QT1(Hyb1)	QT2(Hyb1)	QT1(Hyb2)	QT2(Hyb2)
+ID1	1	2	3	4
+
+Block	Column	Row	X	Y	QT1	QT2	QT3	QT4
+1	1	1	230	230	1	2	3	4
+
+
+
diff --git a/t/data/test_calvin.CHP b/t/data/test_calvin.CHP
new file mode 100644
index 0000000..2c301b2
Binary files /dev/null and b/t/data/test_calvin.CHP differ
diff --git a/t/data/test_fgem.txt b/t/data/test_fgem.txt
new file mode 100644
index 0000000..6bcb194
--- /dev/null
+++ b/t/data/test_fgem.txt
@@ -0,0 +1,11 @@
+Reporter Identifier	RMA(Sigma1278b_1)(Sigma1278b_2)	RMA(W303a_1)(W303a_2)
+A102340	0.147	0.473
+A102341	0.53	0.484
+A102342	0.169	0.188
+A102343	0.742	0.684
+A102344	0.479	0.514
+A102345	0.106	0.525
+A102346	0.592	0.612
+A102347	0.000	0.106
+A102348	0.606	0.624
+A102349	0.584	0.503
diff --git a/t/data/test_xda.CEL b/t/data/test_xda.CEL
new file mode 100644
index 0000000..a3f7d36
Binary files /dev/null and b/t/data/test_xda.CEL differ
diff --git a/t/data/test_xda.CHP b/t/data/test_xda.CHP
new file mode 100644
index 0000000..802a4cf
Binary files /dev/null and b/t/data/test_xda.CHP differ
diff --git a/t/data_metrics.t b/t/data_metrics.t
new file mode 100644
index 0000000..04a9831
--- /dev/null
+++ b/t/data_metrics.t
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: data_metrics.t 1269 2006-12-10 12:23:13Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+BEGIN { use_ok( 'ArrayExpress::Datafile::Metrics' ); }
+
diff --git a/t/database.t b/t/database.t
new file mode 100644
index 0000000..1e7c722
--- /dev/null
+++ b/t/database.t
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: database.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+BEGIN { use_ok( 'ArrayExpress::Curator::Database' ); }
+
diff --git a/t/datafile.t b/t/datafile.t
new file mode 100644
index 0000000..d2f46cc
--- /dev/null
+++ b/t/datafile.t
@@ -0,0 +1,467 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: datafile.t 2069 2008-06-04 14:33:52Z tfrayner $
+
+use strict;
+use warnings;
+
+# Very simple mock object for testing mage_qtd mutator.
+
+use Test::More tests => 131;
+use File::Spec;
+use Cwd;
+use ArrayExpress::Datafile::QT_list qw(get_QTs);
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+BEGIN { use_ok( 'ArrayExpress::Datafile' ); }
+
+my $filename = File::Spec->catfile('t', 'data', 'test.txt');    # right
+my $filepath = '/my/path/to/test.txt'; # wrong
+my $test_columns = [qw(x y z)];
+my $binfile  = File::Spec->catfile('t', 'data', 'test_xda.CEL');
+my $expfile  = File::Spec->catfile('t', 'data', 'test.EXP');
+my $gprfile  = File::Spec->catfile('t', 'data', 'test.gpr');
+my $fgemfile = File::Spec->catfile('t', 'data', 'test_fgem.txt');
+my $QTs = get_QTs();
+
+#######################################################################################
+{
+    # Create the instance.
+    my $file = ArrayExpress::Datafile->new({is_dummy => 1});
+    ok( defined $file, 'new() returned a value' );
+    ok( $file->isa('ArrayExpress::Datafile'),
+	'of the correct class' );
+
+    # File name setting.
+    $file->set_name($filename);
+    is( $file->get_name, $filename, 'name');
+    is( $file->get_path, File::Spec->catfile(getcwd, $filename), 'name-set path');
+    is( $file->get_is_binary, 0, 'text file detected');
+
+    # Incrementable integer attribute checks.
+    foreach my $attr qw(row_count parse_errors not_null) {
+	my $getter = "get_$attr";
+	is($file->$getter(), 0, "$attr initial value");
+	
+	my $setter = "set_$attr";
+	$file->$setter(10);
+	is($file->$getter(), 10, "$attr set value");
+	
+	my $adder = "increment_$attr";
+	$file->$adder();
+	is($file->$getter, 11, "$attr incremented value");
+	
+	$file->$adder(5);
+	is($file->$getter(), 16, "$attr multiply incremented value");
+    }
+
+    # Simple boolean attributes.
+    foreach my $attr qw(is_exp is_miamexpress) {
+	my $getter = "get_$attr";
+	is($file->$getter(), 0, "$attr initial value");
+	
+	my $setter = "set_$attr";
+	$file->$setter(1);
+	is($file->$getter(), 1, "$attr set value 1");
+	$file->$setter(0);
+	is($file->$getter(), 0, "$attr set value 0");
+    }
+
+    # Simple string attributes (empty string init).
+    foreach my $attr qw(hyb_identifier
+			hyb_sysuid
+			array_design_id
+			ded_identifier
+			test_data_line
+			qt_type) {
+	my $getter = "get_$attr";
+	is($file->$getter(), q{}, "$attr initial value");
+
+	my $setter = "set_$attr";
+	$file->$setter('test');
+	is($file->$getter(), 'test', "$attr set value");
+	$file->$setter(q{});
+	is($file->$getter(), q{}, "$attr set value empty");
+    }
+
+    # Simple hashref attributes.
+    my %testhash = (one => 1, two => 'two', three => 'III');
+    foreach my $attr qw(data_metrics) {
+	my $getter = "get_$attr";
+	is_deeply($file->$getter(), {}, "$attr initial value");
+
+	my $setter = "set_$attr";
+	$file->$setter(\%testhash);
+	is_deeply($file->$getter(), \%testhash, "$attr set value");
+    }
+
+    # Simple arrayref attributes.
+    my @testarray = qw(one two three);
+    foreach my $attr qw(index_columns
+			column_headings
+			heading_qts
+			heading_hybs) {
+	my $getter = "get_$attr";
+	is_deeply($file->$getter(), [], "$attr initial value");
+	
+	my $setter = "set_$attr";
+	$file->$setter(\@testarray);
+	is_deeply($file->$getter(), \@testarray, "$attr set value");
+    }
+
+    # Accumulative hashref attributes
+    my @testarray2 = qw(four five);
+    foreach my $attr qw(fail_columns
+			fail_hybs) {
+	my $getter = "get_$attr";
+	is_deeply($file->$getter(), [], "$attr initial value");
+
+	my $adder = "add_$attr";
+	$file->$adder(@testarray);
+	is_deeply(sort($file->$getter()), [sort @testarray], "$attr add values");
+	$file->$adder(@testarray);
+	is_deeply(sort($file->$getter()), [sort @testarray], "$attr duplicate values");
+	$file->$adder(@testarray2);
+	is_deeply(sort($file->$getter()), [sort @testarray, @testarray2], "$attr add more values");
+    }
+
+    # FactorValues.
+    $file->add_factor_value('category1', 'value1');
+    is_deeply($file->get_factor_value(), {'category1' => ['value1']}, 'add factor category and value');
+    $file->add_factor_value('category1', 'value1');
+    is_deeply($file->get_factor_value(), {'category1' => ['value1']}, 'add duplicate factor value');
+    $file->add_factor_value('category1', 'value2');
+    is_deeply($file->get_factor_value(), {'category1' => [qw(value1 value2)]}, 'add second factor value');
+    $file->add_factor_value('category2', 'value2');
+    is_deeply($file->get_factor_value(), {'category1' => [qw(value1 value2)],
+					  'category2' => ['value2']}, 'add second factor category and value');
+
+
+    # Enumerated types; some of these tests are designed to die.
+    SKIP: {
+
+	eval {
+	    require Test::Exception;
+	};
+
+	skip 'Test::Exception not installed',
+	    12 if $@;
+
+	use_ok( 'Test::Exception' );
+
+	# ded_type
+	dies_ok( sub { $file->set_ded_type('notarealdedtype') }, 'exception on bad ded_type');
+	lives_ok( sub { $file->set_ded_type('Reporter') }, 'valid ded_type');
+	is($file->get_ded_type(), 'Reporter', 'ded_type correctly set');
+
+	# format_type
+	dies_ok( sub { $file->set_format_type('notarealformattype') }, 'exception on bad format_type');
+	lives_ok( sub { $file->set_format_type('GenePix') }, 'valid format_type');
+	is($file->get_format_type(), 'GenePix', 'format_type correctly set');
+
+	# data_type
+	dies_ok( sub { $file->set_data_type('notarealdatatype') }, 'exception on bad data_type');
+	lives_ok( sub { $file->set_data_type('raw') }, 'valid data_type');
+	is($file->get_data_type(), 'raw', 'data_type correctly set');
+
+	# Test mage_qtd (using mock Bio::MAGE object).
+	SKIP: {
+	    eval {
+		require Bio::MAGE::BioAssayData::QuantitationTypeDimension;
+	    };
+
+	    skip 'Bio::MAGE::BioAssayData::QuantitationTypeDimension not installed',
+		2 if $@;
+	    
+	    my $qtd = Bio::MAGE::BioAssayData::QuantitationTypeDimension->new();
+
+	    lives_ok( sub {$file->set_mage_qtd($qtd) }, 'set mage_qtd');
+	    is_deeply($file->get_mage_qtd(), $qtd, 'get mage_qtd');
+	}
+    }
+
+    # File path setting.
+    $file->set_path($filepath);
+    is( $file->get_name, $filename, 'name');
+    is( $file->get_path, $filepath, 'name-set path');
+}
+#######################################################################################
+{
+    # Simple string attributes (undef init).
+    my $file2 = ArrayExpress::Datafile->new({is_dummy => 1});
+    foreach my $attr qw(path
+			target_filename) {
+	my $getter = "get_$attr";
+	is($file2->$getter(), undef, "$attr initial value");
+					 
+	my $setter = "set_$attr";
+	$file2->$setter('test');
+	is($file2->$getter(), 'test', "$attr set value");
+	$file2->$setter(q{});
+	is($file2->$getter(), q{}, "$attr set value empty");
+    }
+}
+#######################################################################################
+{
+    # EXP file accessors.
+    my $exp = ArrayExpress::Datafile->new({is_dummy => 1});
+    my $expdata = {section => {param => 'value'}};
+    $exp->set_exp_data($expdata);
+    is_deeply($exp->get_exp_data(), $expdata,     'set exp_data');
+    is($exp->get_is_exp(),          1,            'EXP flag set by set_exp_data');
+    is($exp->get_format_type(),     'Affymetrix', 'format_type set by set_exp_data');
+    is($exp->get_data_type(),       'EXP',        'data_type set by set_exp_data');
+}
+#######################################################################################
+{
+    # Create a more complex initial instance.
+    my $nfile1 = ArrayExpress::Datafile->new({
+	name            => $filename,
+	hyb_identifier  => 'one',
+	hyb_sysuid      => 'two',
+	array_design_id => 'three',
+	is_miamexpress  => 1,
+	is_exp          => 1,
+	target_filename => 'four',
+	data_type       => 'raw',
+	column_headings => $test_columns,
+    });
+    ok( defined $nfile1, 'new() returned a value' );
+    ok( $nfile1->isa('ArrayExpress::Datafile'),
+	'of the correct class' );
+
+    # Test that the path is also set.
+    is( $nfile1->get_name, $filename, 'constructor-set name');
+    is( $nfile1->get_path, File::Spec->catfile(getcwd, $filename), 'name-constructor-set path');
+
+    # Other init args settings.
+    is( $nfile1->get_hyb_identifier(),  'one',     'init hyb_identifier');
+    is( $nfile1->get_hyb_sysuid(),      'two',     'init hyb_sysuid');
+    is( $nfile1->get_array_design_id(), 'three',   'init array_design_id');
+    is( $nfile1->get_is_miamexpress(),  1,         'init is_miamexpress');
+    is( $nfile1->get_is_exp(),          1,         'init is_exp');
+    is( $nfile1->get_target_filename(), 'four',    'init target_filename');
+    is( $nfile1->get_data_type(),       'raw',     'init data_type');
+    is_deeply( $nfile1->get_column_headings(),  $test_columns, 'init column_headings');
+}
+#######################################################################################
+{
+    # Check precedence of path initialization
+    my $nfile2 = ArrayExpress::Datafile->new({
+	name     => $filename,
+	path     => $filepath,
+	is_dummy => 1,
+    });
+    is( $nfile2->get_name, $filename, 'constructor-set name');
+    is( $nfile2->get_path, $filepath, 'constructor-set path');
+
+    # Check binary file detection
+    my $file3 = ArrayExpress::Datafile->new({
+	name     => $binfile,
+	is_dummy => 1,
+    });
+    is( $file3->get_is_binary(), 1, 'binary file detected');
+}
+#######################################################################################
+{
+    my $exp = ArrayExpress::Datafile->new({
+	name     => $expfile,
+	is_dummy => 1,
+    });
+    $exp->parse_exp_file();
+    is( $exp->get_is_exp(), 1, 'real EXP file recognized');
+    my $expected = {
+	'Sample Info' => {
+	    'Chip Lot'  => 12345678,
+	    'Operator'  => 'Gandalf',
+	    'Project'   => 'Test',
+	    'Chip Type' => 'HG-U133A',
+	},
+	'Scanner' => {
+	    'Number of Scans'  => 2,
+	    'Scan Date'        => 'Sep 20 2003 01:25PM',
+	    'Filter'           => 570,
+	    'Scanner ID'       => '87654321',
+	    'Pixel Size'       => 3,
+	    'Scanner Type'     => 'HP',
+	    'Scan Temperature' => 30,
+	},
+	'Fluidics' => {
+	    'Wash A1 Recovery Mixes'      => 0,
+	    'Wash A1 Temperature (C)'     => 25,
+	    'Number of Wash A1 Cycles'    => 10,
+	    'Mixes per Wash A1 Cycle'     => 2,
+	    'Wash B Recovery Mixes'       => 0,
+	    'Wash B Temperature (C)'      => 50,
+	    'Number of Wash B Cycles'     => 4,
+	    'Mixes per Wash B Cycle'      => 15,
+	    'Stain Temperature (C)'       => 25,
+	    'First Stain Time (seconds)'  => 600,
+	    'Wash A2 Recovery Mixes'      => 0,
+	    'Wash A2 Temperature (C)'     => 25,
+	    'Number of Wash A2 Cycles'    => 10,
+	    'Mixes per Wash A2 Cycle'     => 4,
+	    'Second Stain Time (seconds)' => 600,
+	    'Third Stain Time (seconds)'  => 600,
+	    'Wash A3 Recovery Mixes'      => 0,
+	    'Wash A3 Temperature (C)'     => 30,
+	    'Number of Wash A3 Cycles'    => 15,
+	    'Mixes per Wash A3 Cycle'     => 4,
+	    'Holding Temperature (C)'     => 25,
+	    'Hybridize Date'              => 'Sep 20 2003 01:10PM',
+	    'Module'                      => 1,
+	    'Station'                     => 2,
+	    'Protocol'                    => 'EukGE-WS2v4',
+	},
+	'header' => {
+	    'Version' => 1,
+	},
+    };
+    is_deeply( $exp->get_exp_data, $expected, 'real EXP file fully parsed');
+}
+#######################################################################################
+my $start = [
+    'Block',
+    'Column',
+    'Row',
+    'Name',
+    'ID',
+    'X',
+    'Y',
+];
+my $ignored = [
+    'Name',
+    'ID',
+];
+my $end = [
+    'MetaColumn',
+    'MetaRow',
+    'Column',
+    'Row',
+];
+my $expected = [
+    'Dia.',
+    'F635 Median',
+    'F635 Mean',
+    'F635 SD',
+    'B635 Median',
+    'B635 Mean',
+    'B635 SD',
+    '% > B635+1SD',
+    '% > B635+2SD',
+    'F635 % Sat.',
+    'F532 Median',
+    'F532 Mean',
+    'F532 SD',
+    'B532notQTMedian',
+    'B532 Mean',
+    'B532 SD',
+    '% > B532+1SD',
+    '% > B532+2SD',
+    'F532 % Sat.',
+    'Ratio of Medians',
+    'Ratio of Means',
+    'Median of Ratios',
+    'Mean of Ratios',
+    'Ratios SD',
+    'Rgn Ratio',
+    'Rgn R�',
+    'F Pixels',
+    'B Pixels',
+    'Sum of Medians',
+    'Sum of Means',
+    'Log Ratio',
+    'F635 Median - B635',
+    'F532 Median - B532',
+    'F635 Mean - B635',
+    'F532 Mean - B532',
+    'flags',
+];
+#######################################################################################
+{
+    my $gpr = ArrayExpress::Datafile->new({
+	name     => $gprfile,
+	is_dummy => 1,
+    });
+    is($gpr->parse_header(),               'GenePix',             'parse_header return code');
+    is($gpr->get_format_type(),            'GenePix',             'real file get_format_type');
+    is_deeply($gpr->get_index_columns(),   [0,1,2,5,6],           'real file get_index_columns');
+    is_deeply($gpr->get_column_headings(), [@$start, @$expected], 'real file get_column_headings');
+
+    is($gpr->check_column_headings($QTs), q{},                         'check_column_headings return code');
+    is($gpr->get_qt_type(),               'GenePix[Axon Instruments]', 'real file get_qt_type');
+    is_deeply($gpr->get_heading_qts(),    [@$ignored, @$expected],       'real file get_heading_qts');
+
+    # These are alphabetically sorted.
+    is_deeply($gpr->get_fail_columns(), ['B532notQTMedian', 'flags'], 'real file get_fail_columns');
+}
+#######################################################################################
+{
+    my $gpr = ArrayExpress::Datafile->new({
+	name     => $gprfile,
+	is_dummy => 1,
+    });
+    $gpr->parse_datafile($QTs, {}, {}, 0);
+    is($gpr->get_line_format(),  'Unix',  'real file get_line_format');
+    is($gpr->get_row_count(),    10,      'real file row count');
+    is($gpr->get_parse_errors(), 0,       'real file parse errors');
+    is($gpr->get_not_null(),     320,     'real file not null');
+    is($gpr->percent_null(),     5.88235, 'real file percent null');
+
+    # This is the result of hashing a number of lines together using Digest::MD5::md5_hex.
+    my $expecteddata = 'fe3055946e452021b7b2d83ac5569eaea6c1d2f25c721684a0f7c13861a997b53cb62811f2c166212ddc4ec18fa76d5ef8d0261f69960b7fe7ef41025a3ed9667d3a17bc59819151312b7c8b4a6460f8';
+    is($gpr->get_test_data_line(),         $expecteddata,       'real file test_data_line');
+    is_deeply($gpr->get_column_headings(), [@$end, @$ignored, @$expected], 'parsed file get_column_headings');
+    is_deeply($gpr->get_heading_qts(),     [@$ignored, @$expected], 'parsed file get_heading_qts');
+}
+#######################################################################################
+{
+    my $fgem = ArrayExpress::Datafile->new({
+	name            => $fgemfile,
+	data_type       => 'transformed',
+	array_design_id => 'UNKNOWN',
+    });
+    $fgem->parse_datafile($QTs, {Sigma1278b_1 => 'X', W303a_1 => 'X', W303a_2 => 'X'}, {}, 0);
+
+    is_deeply($fgem->get_heading_qts(),  [qw(RMA RMA)],                         'fgem heading qts');
+    is_deeply($fgem->get_heading_hybs(), [['Sigma1278b_1', 'Sigma1278b_2'],
+					  ['W303a_1',      'W303a_2']],         'fgem heading hybs');
+    is_deeply($fgem->get_fail_columns(), [],                                    'fgem fail columns');
+    is_deeply($fgem->get_fail_hybs(),    ['Sigma1278b_2'],                      'fgem fail hybs');
+}
+#######################################################################################
+{
+
+    my $file = ArrayExpress::Datafile->new({
+	name            => $filename,
+	data_type       => 'raw',
+	array_design_id => 'UNKNOWN',
+    });
+
+    # First pass
+    $file->parse_header_with_indices( { GenePix => [qw(Block Column Row X Y)] } );
+
+    is( $file->get_format_type(), 'GenePix', 'parse_header recognises file type' );
+    is_deeply(
+	$file->get_column_headings(),
+	[qw(Block Column Row X Y QT1 QT2 QT3 QT4)],
+	'parse_header returns correct headings'
+    );
+    is_deeply( $file->get_index_columns(), [qw(0 1 2 3 4)],
+	       'parse_header returns correct index columns' );
+
+    # Second pass
+    seek( $file->get_filehandle(), 0, 0 ) or die($!);
+    $file->set_data_type($CONFIG->get_FGEM_FILE_TYPE());
+    $file->parse_header_with_indices( { FGEM => ['Reporter Identifier'] } );
+
+    is( $file->get_format_type(), 'FGEM', 'parse_header recognises file type' );
+    is_deeply(
+	$file->get_column_headings(),
+	[ 'Reporter Identifier', qw( QT1(Hyb1) QT2(Hyb1) QT1(Hyb2) QT2(Hyb2) ) ],
+	'parse_header returns correct headings'
+    );
+    is_deeply( $file->get_index_columns(), [0], 'parse_header returns correct index columns' );
+}
+
+# TODO: fix_known_text_format (all types), strip_and_sort
diff --git a/t/datafile_parser.t b/t/datafile_parser.t
new file mode 100644
index 0000000..7e648df
--- /dev/null
+++ b/t/datafile_parser.t
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: datafile_parser.t 1269 2006-12-10 12:23:13Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+BEGIN { use_ok( 'ArrayExpress::Datafile::Parser' ); }
+
diff --git a/t/db.t b/t/db.t
new file mode 100644
index 0000000..f0d6194
--- /dev/null
+++ b/t/db.t
@@ -0,0 +1,58 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: db.t 1960 2008-02-21 12:03:19Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 37;
+
+SKIP: {
+
+    eval {
+	require Class::DBI;
+    };
+
+    skip 'Class::DBI not installed',
+	37 if $@;
+
+    require_ok( 'ArrayExpress::AutoSubmission::DB' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::Accessionable' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::ArrayDesign' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::ArrayDesignExperiment' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::ArrayDesignOrganism' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::Category' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::CategoryDesign' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::CategoryMaterial' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::CategoryTaxon' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::DataFile' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::Design' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::DesignInstance' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::Event' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::Experiment' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::ExperimentFactor' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::ExperimentQT' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::Factor' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::File' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::Material' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::MaterialInstance' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::Organism' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::OrganismInstance' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::Permission' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::PermissionRole' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::Protocol' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::QuantitationType' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::Role' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::RoleUser' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::Spreadsheet' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::Taxon' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::User' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::LoadedData' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::ExperimentLoadedData' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::DataFormat' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::QualityMetric' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::QualityMetricInstance' );
+    require_ok( 'ArrayExpress::AutoSubmission::DB::Platform' );
+
+}
+
diff --git a/t/entrez_list.t b/t/entrez_list.t
new file mode 100644
index 0000000..e466d93
--- /dev/null
+++ b/t/entrez_list.t
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: entrez_list.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+BEGIN { use_ok( 'ArrayExpress::Curator::Entrez_list' ); }
+
diff --git a/t/experiment_checker.t b/t/experiment_checker.t
new file mode 100644
index 0000000..df04781
--- /dev/null
+++ b/t/experiment_checker.t
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: experiment_checker.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+BEGIN { use_ok( 'ArrayExpress::Curator::ExperimentChecker' ); }
+
diff --git a/t/fgem.t b/t/fgem.t
new file mode 100644
index 0000000..7d7b3ce
--- /dev/null
+++ b/t/fgem.t
@@ -0,0 +1,107 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: common.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 33;
+
+BEGIN {
+    require_ok(
+        'ArrayExpress::Datafile::DataMatrix'
+    );
+}
+
+my $dm = ArrayExpress::Datafile::DataMatrix->new();
+ok( defined $dm, 'new() returned a value' );
+ok( $dm->isa('ArrayExpress::Datafile::DataMatrix'), 'of the correct class' );
+
+#########################
+# get_dimension_lists() #
+#########################
+my @fgem_dimension_data = (
+    {
+
+        # DBQ/DQB (defaults to DBQ)
+        query => [
+            [ ['One'], ['Two'] ],    # Hybs
+            [qw(A A)],               # QTs
+        ],
+        result => [
+            [ ['One'], ['Two'] ],    # Hybs
+            [qw(A)],                 # QTs
+            'DBQ',                   # Order
+        ]
+    },
+    {
+
+        # DBQ
+        query => [ [ ['One'], ['One'], ['Two'], ['Two'] ], [qw(A B A B)], ],
+        result => [ [ ['One'], ['Two'] ], [qw(A B)], 'DBQ', ]
+    },
+    {
+
+        # DBQ
+        query => [
+            [ ['One'], ['One'], ['One'], ['Two'], ['Two'], ['Two'] ],
+            [qw(A B C A B C)],
+        ],
+        result => [ [ ['One'], ['Two'] ], [qw(A B C)], 'DBQ', ]
+    },
+    {
+
+        # DQB
+        query => [ [ ['One'], ['Two'], ['One'], ['Two'] ], [qw(A A B B)], ],
+        result => [ [ ['One'], ['Two'] ], [qw(A B)], 'DQB', ]
+    },
+    {
+
+        # DBQ, more complex
+        query => [
+            [
+                [qw(One Two)], [qw(One Two)], [qw(Three Four)], [qw(Three Four)]
+            ],
+            [qw(A B A B)],
+        ],
+        result => [ [ [qw(One Two)], [qw(Three Four)] ], [qw(A B)], 'DBQ', ]
+    },
+    {
+
+        # Broken, no order detectable
+        query => [ [ ['One'], ['Three'], ['One'], ['Two'] ], [qw(A D B C)], ],
+        result => [ [], [], undef, ]
+    },
+    {
+
+        # Broken, order at first okay, then breaks.
+        query => [ [ ['One'], ['Two'], ['One'], ['Two'] ], [qw(A A B C)], ],
+        result => [ [], [], undef, ]
+    },
+    {
+
+        # Broken, order at first okay, then breaks.
+        query => [ [ ['One'], ['Two'], ['Onxe'], ['Three'] ], [qw(A A B B)], ],
+        result => [ [], [], undef, ]
+    },
+    {
+
+        # Broken, order at first okay, then breaks.
+        query => [ [ ['One'], ['One'], ['Two'], ['Three'] ], [qw(A B A B)], ],
+        result => [ [], [], undef, ]
+    },
+    {
+
+        # Broken, order at first okay, then breaks.
+        query => [ [ ['One'], ['One'], ['Two'], ['Two'] ], [qw(A B B A)], ],
+        result => [ [], [], undef, ]
+    },
+);
+
+foreach my $test (@fgem_dimension_data) {
+    my ( $bas, $qts, $order ) = $dm->get_dimension_lists( @{ $test->{query} } );
+    is_deeply( $bas,   $test->{result}[0], 'get_dimension_lists bioassays' );
+    is_deeply( $qts,   $test->{result}[1], 'get_dimension_lists qts' );
+    is_deeply( $order, $test->{result}[2], 'get_dimension_lists order' );
+}
+
diff --git a/t/logger.t b/t/logger.t
new file mode 100644
index 0000000..5b1ac00
--- /dev/null
+++ b/t/logger.t
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl -wT
+#
+# $Id$
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+require_ok( 'ArrayExpress::Curator::Logger' );
+
diff --git a/t/mage.t b/t/mage.t
new file mode 100644
index 0000000..86f195b
--- /dev/null
+++ b/t/mage.t
@@ -0,0 +1,19 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: mage.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+SKIP: {
+
+    eval { require Bio::MAGE };
+
+    skip 'Bio::MAGE not installed', 1 if $@;
+
+    require_ok( 'ArrayExpress::Curator::MAGE' );
+
+}
+
diff --git a/t/mage_defs.t b/t/mage_defs.t
new file mode 100644
index 0000000..1647f1d
--- /dev/null
+++ b/t/mage_defs.t
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: mage_defs.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+BEGIN { use_ok( 'ArrayExpress::Curator::MAGE::Definitions' ); }
+
diff --git a/t/magetab.t b/t/magetab.t
new file mode 100644
index 0000000..f753a8a
--- /dev/null
+++ b/t/magetab.t
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: magetab.t 1462 2007-03-15 15:45:55Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+require_ok( 'ArrayExpress::MAGETAB' );
diff --git a/t/magetab_checker.t b/t/magetab_checker.t
new file mode 100644
index 0000000..f3356fc
--- /dev/null
+++ b/t/magetab_checker.t
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: validate.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 2;
+
+require_ok( 'ArrayExpress::MAGETAB::Checker' );
+require_ok( 'ArrayExpress::MAGETAB::Checker::IDF' );
diff --git a/t/magetab_datamatrix.t b/t/magetab_datamatrix.t
new file mode 100644
index 0000000..3ebf635
--- /dev/null
+++ b/t/magetab_datamatrix.t
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: magetab_datamatrix.t 1462 2007-03-15 15:45:55Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+require_ok( 'ArrayExpress::MAGETAB::DataMatrix' );
diff --git a/t/magetab_idf.t b/t/magetab_idf.t
new file mode 100644
index 0000000..ffcc2f5
--- /dev/null
+++ b/t/magetab_idf.t
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: magetab_idf.t 1462 2007-03-15 15:45:55Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+require_ok( 'ArrayExpress::MAGETAB::IDF' );
diff --git a/t/magetab_sdrf.t b/t/magetab_sdrf.t
new file mode 100644
index 0000000..c0dff2c
--- /dev/null
+++ b/t/magetab_sdrf.t
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: magetab_sdrf.t 1462 2007-03-15 15:45:55Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+require_ok( 'ArrayExpress::MAGETAB::SDRF' );
diff --git a/t/magetab_tabfile.t b/t/magetab_tabfile.t
new file mode 100644
index 0000000..6c1cefe
--- /dev/null
+++ b/t/magetab_tabfile.t
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: magetab_tabfile.t 1462 2007-03-15 15:45:55Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+require_ok( 'ArrayExpress::MAGETAB::TabFile' );
diff --git a/t/miamexpress.t b/t/miamexpress.t
new file mode 100644
index 0000000..d660ba7
--- /dev/null
+++ b/t/miamexpress.t
@@ -0,0 +1,22 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: miamexpress.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+SKIP: {
+
+    eval {
+	require DBI;
+	require DBD::mysql;
+       };
+
+    skip 'DBI and/or DBD::mysql not installed', 1 if $@;
+
+    require_ok( 'ArrayExpress::Curator::MIAMExpress' );
+
+}
+
diff --git a/t/qt_list.t b/t/qt_list.t
new file mode 100644
index 0000000..7ddc024
--- /dev/null
+++ b/t/qt_list.t
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: qt_list.t 1269 2006-12-10 12:23:13Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+BEGIN { use_ok( 'ArrayExpress::Datafile::QT_list' ); }
+
diff --git a/t/report.t b/t/report.t
new file mode 100644
index 0000000..150254d
--- /dev/null
+++ b/t/report.t
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: report.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+BEGIN { use_ok( 'ArrayExpress::Curator::Report' ); }
+
diff --git a/t/spreadsheet.t b/t/spreadsheet.t
new file mode 100644
index 0000000..052a336
--- /dev/null
+++ b/t/spreadsheet.t
@@ -0,0 +1,21 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: spreadsheet.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+SKIP: {
+
+    eval {
+	require Class::DBI;
+    };
+
+    skip 'Class::DBI not installed',
+	1 if $@;
+
+    require_ok( 'ArrayExpress::AutoSubmission::Spreadsheet' );
+
+}
diff --git a/t/standalone.t b/t/standalone.t
new file mode 100644
index 0000000..b03fd5c
--- /dev/null
+++ b/t/standalone.t
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: validate.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+require_ok( 'ArrayExpress::Curator::Standalone' );
diff --git a/t/tab2mage.t b/t/tab2mage.t
new file mode 100644
index 0000000..b1ba412
--- /dev/null
+++ b/t/tab2mage.t
@@ -0,0 +1,40 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: tab2mage.t 1298 2006-12-20 19:44:56Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 15;
+
+SKIP: {
+
+    eval { require Bio::MAGE };
+
+    skip 'Bio::MAGE not installed', 1 if $@;
+
+    require_ok( 'ArrayExpress::Curator::Tab2MAGE' );
+
+}
+
+my $t2m = ArrayExpress::Curator::Tab2MAGE->new({
+    target_directory     => 't',
+    spreadsheet_filename => 'test.txt',
+});
+ok( defined $t2m, 'new() returned a value' );
+ok( $t2m->isa('ArrayExpress::Curator::Tab2MAGE'),
+    'of the correct class' );
+
+# Test our basic attempts to sanitize protocol text for display in ArrayExpress.
+is($t2m->htmlify_protocoltext("&"),      "&",    'htmlify ampersand');
+is($t2m->htmlify_protocoltext("\n"),     "<br>\n",   'htmlify unix newline');
+is($t2m->htmlify_protocoltext("\r\n"),   "<br>\r\n", 'htmlify dos newline');
+is($t2m->htmlify_protocoltext("°"),  "°",    'htmlify named entity');
+is($t2m->htmlify_protocoltext("Δ"), "Δ",   'htmlify numbered entity');
+is($t2m->htmlify_protocoltext("�"),      "°",    'htmlify degree (�) sign');
+is($t2m->htmlify_protocoltext("�X"),     "°",    'htmlify degree (�X) sign');
+is($t2m->htmlify_protocoltext("�"),      "μ",   'htmlify mu (�) character');
+is($t2m->htmlify_protocoltext("��"),  "μ",   'htmlify mu (��) character');
+is($t2m->htmlify_protocoltext("�"),   "'",   'htmlify apostrophe (�) character');
+is($t2m->htmlify_protocoltext("�"),   """,   'htmlify quote (�) character');
+is($t2m->htmlify_protocoltext("�"),   """,   'htmlify quote (�) character');
diff --git a/t/tracking.t b/t/tracking.t
new file mode 100644
index 0000000..a3cf186
--- /dev/null
+++ b/t/tracking.t
@@ -0,0 +1,36 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: db.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 4;
+
+SKIP: {
+
+    eval {
+	require Date::Manip;
+    };
+
+    skip 'Date::Manip not installed',
+	1 if $@;
+
+    require_ok( 'ArrayExpress::Tracking::QueryHandler' );
+
+}
+
+require_ok( 'ArrayExpress::Tracking::Event' );
+require_ok( 'ArrayExpress::Tracking::CelQC' );
+
+SKIP: {
+
+    eval {
+	require Class::DBI;
+    };
+    skip 'Class::DBI not installed',
+	1 if $@;
+
+    require_ok( 'ArrayExpress::Tracking::QCJobManager' );
+
+}
diff --git a/t/validate.t b/t/validate.t
new file mode 100644
index 0000000..0a63d08
--- /dev/null
+++ b/t/validate.t
@@ -0,0 +1,19 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: validate.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+SKIP: {
+
+    eval { require Bio::MAGE };
+
+    skip 'Bio::MAGE not installed', 1 if $@;
+
+    require_ok( 'ArrayExpress::Curator::Validate' );
+
+}
+
diff --git a/t/visualize.t b/t/visualize.t
new file mode 100644
index 0000000..6fd17df
--- /dev/null
+++ b/t/visualize.t
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: visualize.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+BEGIN { use_ok( 'ArrayExpress::Curator::Visualize' ); }
+
diff --git a/t/web_form.t b/t/web_form.t
new file mode 100644
index 0000000..ad28626
--- /dev/null
+++ b/t/web_form.t
@@ -0,0 +1,25 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: web_form.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+SKIP: {
+
+    eval {
+	require CGI::Application;
+	require CGI::Application::Plugin::Authentication;
+	require CGI::Application::Plugin::ValidateRM;
+	require CGI::Upload;
+    };
+
+    skip 'Some module(s) not installed: CGI::Application, CGI::Upload, CAP::Authentication or CAP::ValidateRM',
+	1 if $@;
+
+    require_ok( 'ArrayExpress::AutoSubmission::WebForm' );
+
+}
+
diff --git a/util/AE_insert_celfiles.pl b/util/AE_insert_celfiles.pl
new file mode 100755
index 0000000..2d9aa1f
--- /dev/null
+++ b/util/AE_insert_celfiles.pl
@@ -0,0 +1,202 @@
+#!/usr/bin/env perl
+#
+# Script to take a tab-delimited list of BioAssayData identifiers
+# mapped to CEL file names, and insert them into the ArrayExpress
+# repository.
+#
+# With thanks to Margus Lukk for the actual CEL insertion code.
+#
+# Tim Rayner, 2007, EBI
+#
+# $Id: AE_insert_celfiles.pl 1978 2008-02-28 12:14:14Z tfrayner $
+#
+# From Margus: "The files go to tt_biodatacube table to netcdf
+# field. Although the name of the field indicates that the files
+# should be in netcdf format, it is not really so. Nowadays original
+# CEL is loaded to the field."
+
+use strict;
+use warnings;
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use DBI;
+require DBD::Oracle;
+
+use Term::ReadKey;
+use Getopt::Long;
+use Readonly;
+use English qw( -no_match_vars );
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(date_now);
+use ArrayExpress::Datafile::Affymetrix;
+
+Readonly my $MARGIN => " " x (length(date_now()) + 2);
+
+########
+# SUBS #
+########
+
+sub get_char_locator {
+
+    # Return a new lob locator for writing.
+
+    my ( $dbh, $badata_id ) = @_;
+
+    my $sth = $dbh->prepare(
+	"select bdc.netcdf from tt_biodatacube bdc, tt_bioassaydata bad, tt_identifiable i"
+      . " where i.id=bad.id and bad.biodatavalues_id=bdc.id and i.identifier=? for update",
+	{ ora_auto_lob => 0 }
+    );
+
+    $sth->execute($badata_id) or die($sth->errstr);
+    my $char_locator = $sth->fetchrow_array();
+    $sth->finish();
+
+    unless ($char_locator) {
+	print STDOUT ("$MARGIN No record for ID $badata_id in database; skipping.\n");
+    }
+
+    return $char_locator;
+}
+
+########
+# MAIN #
+########
+
+my ($listfile, $dataformat);
+GetOptions(
+    "f|file=s"       => \$listfile,
+    "d|dataformat=s" => \$dataformat,
+);
+
+unless ($listfile) {
+    print <<"USAGE";
+
+Usage: $0 -f <file listing IDs and CEL files>
+
+   N.B. File should contain two columns separated by tabs:
+
+   - BioAssayData ID
+   - CEL file name
+
+   Data format will typically be CELv3 or CELv4 and should be
+   automatically detected by the script. To override, use the option:
+
+     -d <data format>
+
+USAGE
+    exit 255;
+}
+
+open (my $list_fh, '<', $listfile) or
+    die("Can't open listing file $listfile: $!\n");
+
+print STDERR ("Username: ");
+chomp( my $username = <STDIN> );
+
+ReadMode 2;
+print STDERR ("Password: ");
+chomp( my $password = <STDIN> );
+ReadMode 0;
+print STDERR ("\n");
+
+# From Margus: "It is important that LongReadLen parameter is set big
+# while connecting to the db. I used even "LongReadLen => 100000000"
+# as some of the CELs where ~50MB."
+#
+# Note that we undertake to manage our commits ourselves.
+#
+my $dbh = DBI->connect(
+#    "DBI:Oracle:host=progression.ebi.ac.uk;sid=AECUR;port=1521",  # AECUR
+    $CONFIG->get_AE_DSN(),                                         # AEPUB1, usually
+    $username,
+    $password,
+    {LongReadLen => 100_000_000, RaiseError => 1, AutoCommit => 0},
+) or die($DBI::errstr);
+
+# Line-buffered output (may be unnecessary).
+$| = 1;
+
+my $affy_factory = ArrayExpress::Datafile::Affymetrix->new();
+
+my $linenum=0;
+LINE:
+while (my $line = <$list_fh>) {
+    chomp $line;
+    $linenum++;
+    my ($badata_id, $celfile) = split /\t/, $line;
+    unless ($badata_id && $celfile) {
+	warn("Can't parse values in line $linenum; skipping.\n");
+	next LINE;
+    }
+
+    printf STDOUT ("[%s] Processing file %s for BioAssayData ID %s...\n", date_now(), $celfile, $badata_id);
+
+    # Open BLOB file for update.
+    open (my $cel_fh, '<', $celfile) or die("Can't open file $celfile: $!\n"); # FIXME not to die somehow?
+
+    my $celformat;
+    if ( $dataformat ) {
+	$celformat = $dataformat;
+    }
+    else {
+	my $cel = $affy_factory->make_parser( $celfile );
+	eval { $cel->parse_header() };
+	if ($EVAL_ERROR) {
+	    print STDOUT ("$MARGIN Error parsing CEL header for $celfile (skipping): $EVAL_ERROR\n");
+	    next LINE;
+	}
+	$celformat = "CELv" . $cel->get_version();
+	seek($cel_fh, 0, 0) or die("Error rewinding filehandle for file $celfile: $!");
+    }
+
+    # Arbitrary chunk size; benchmarked reasonably well. N.B. the
+    # tt_biodatacube table is set up with 8K records, so an 8K chunk
+    # size might be more appropriate, but any multiple of 8K is okay
+    # (16K just beat 8K in benchmarking).
+    my $chunk_size = 16384;
+
+    # Write file to "netcdf" record.
+    my $offset = 1;    # Offsets start at 1, not 0
+    my $length = 0;
+    my $buffer = q{};
+
+    # Select BLOB for modification.
+    my $char_locator = get_char_locator( $dbh, $badata_id ) or next LINE;
+
+    # This loop reads in chunks and writes them to the relevant netcdf
+    # field, doing a commit on each chunk. This is to help avoid row
+    # locking on very long transactions.
+    while( $length = read( $cel_fh, $buffer, $chunk_size ) ) {
+	$dbh->ora_lob_write( $char_locator, $offset, $buffer );
+	$offset += $length;
+	print STDERR ".";
+    }
+
+    close($cel_fh);
+    print STDERR "\n";
+
+    # If the new BLOB in DB was smaller than the original, adjust the
+    # size of the BLOB field (i.e. delete rudiments from previous blob
+    # in the end of blob).
+    if ( $offset < $dbh->ora_lob_length( $char_locator ) ) {
+        $dbh->ora_lob_trim( $char_locator, $offset - 1 );
+    }
+
+    print STDOUT "$MARGIN File length: $offset bytes. Setting dataFormat to $celformat...\n";
+    my $sth = $dbh->prepare(   # FIXME this could probably be more concise
+	"update tt_biodatacube set dataformat=? where id in"
+      . " (select bdc.id from tt_biodatacube bdc, tt_bioassaydata bad, tt_identifiable i"
+      . " where i.id=bad.id and bad.biodatavalues_id=bdc.id and i.identifier=?)"
+    );
+    $sth->execute($celformat,$badata_id) or die($sth->errstr);
+    $sth->finish();
+
+    # Commit between each file.
+    $dbh->commit();
+}
+
+printf STDOUT ("[%s] Done.\n", date_now() );
diff --git a/util/fillin_null_rows.pl b/util/fillin_null_rows.pl
new file mode 100755
index 0000000..7ba86eb
--- /dev/null
+++ b/util/fillin_null_rows.pl
@@ -0,0 +1,124 @@
+#!/usr/bin/env perl
+#
+# fillin_null_rows.pl
+
+# A script taking a list of data files, and rewriting them with added
+# null rows such that subsequently generated DesignElementDimensions
+# are all the same. The original files are untouched; new files are
+# created with a ".new" extension.
+
+# $Id: fillin_null_rows.pl 1861 2007-12-24 17:26:19Z tfrayner $
+
+use strict;
+use warnings;
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+# Added to circumvent weird Exporter bug in 5.8.3 which caused
+# crashing when requiring ArrayExpress::Datafile (which in turn uses
+# ArrayExpress::Curator::Common).
+use ArrayExpress::Curator::Common;
+
+use IO::File;
+use ArrayExpress::Datafile;
+
+# Use a null byte char to delimit the index coords.
+my $tag_delim = qq{\x0};
+
+unless (@ARGV) {
+    print STDOUT (<<"USAGE");
+    Usage: $0 <list of data files>
+
+USAGE
+
+    exit 255;
+}
+
+my %coordinate;
+foreach my $filename (@ARGV) {
+
+    print STDERR ("Recording coordinates for file $filename...\n");
+
+    my $file = ArrayExpress::Datafile->new({
+	name     => $filename,
+	is_dummy => 1,
+    });
+
+    my $input_fh = $file->get_filehandle();
+
+    # Figure out what kind of file we have, get column headings and indices
+    $file->parse_header($input_fh);
+
+    while (my $line = <$input_fh>){
+	my @larry = split /\t/, $line, -1;
+
+	$coordinate{ join($tag_delim, @larry[ @{ $file->get_index_columns() } ] ) }++;
+    }
+}
+
+foreach my $filename (@ARGV) {
+
+    print STDERR ("Rewriting file $filename...\n");
+
+    my $output_fh = IO::File->new( "$filename.full", '>' )
+        or die("Error opening output file $filename.full: $!\n");
+
+    my $file = ArrayExpress::Datafile->new({
+	name     => $filename,
+	is_dummy => 1,
+    });
+
+    my $input_fh = $file->get_filehandle();
+
+    # Figure out what kind of file we have, get column headings and indices
+    $file->parse_header($input_fh);
+
+    my %index = map { $_ => 1 } @{ $file->get_index_columns() };
+    my @indexlist = @{ $file->get_index_columns() };
+
+    my @headings = @{$file->get_column_headings()};
+    my @harry;
+    for (my $i = 0; $i <= $#headings; $i++) {
+	push(@harry, $index{$i} ? $headings[shift(@indexlist)] : $headings[$i]);
+    }
+    print $output_fh (join("\t", @harry), "\n");
+
+    my %data;
+    while (my $line = <$input_fh>){
+
+	# Handle both unix and dos cleanly
+	$line =~ s/[\r\n]*$//;
+	my @larry = split /\t/, $line, -1;
+
+	$data{ join($tag_delim, @larry[ @{ $file->get_index_columns() } ] ) }
+	    = \@larry;
+    }
+
+    foreach my $coord ( sort keys %coordinate ) {
+
+	my @coords = split /$tag_delim/, $coord;
+
+	if (my $row = $data{$coord}) {
+
+	    my @larry;
+	    for (my $i = 0; $i <= $#$row; $i++) {
+		push(@larry, $index{$i} ? shift(@coords) : $row->[$i]);
+	    }
+
+	    print $output_fh (
+		join("\t", @larry), "\n",
+	    );
+	}
+	else { # empty row
+
+	    my @larry;
+	    for (my $i = 0; $i <= $#{ $file->get_column_headings() }; $i++) {
+		push(@larry, $index{$i} ? shift(@coords) : q{null});
+	    }
+	    print $output_fh (
+		join("\t", @larry), "\n",
+	    );
+	}
+    }
+}
diff --git a/util/join_by_first_column.pl b/util/join_by_first_column.pl
new file mode 100755
index 0000000..88209b6
--- /dev/null
+++ b/util/join_by_first_column.pl
@@ -0,0 +1,122 @@
+#!/sw/arch/bin/perl
+#
+# Joins a series of data files using the first column as index.
+#
+# $Id: join_by_first_column.pl 1861 2007-12-24 17:26:19Z tfrayner $
+
+use strict;
+use warnings;
+
+unless (@ARGV) {
+    print STDOUT (<<"USAGE");
+    Usage: $0 list of data files > new_FGEM_file.txt
+
+USAGE
+
+    exit 255;
+}
+
+# %HoH stores the data:
+#
+# {$file1 => {$index1 => \@row, $index2 => \@row}, $file2 => {}...]
+#
+# where $index is the first-column index.
+my %HoH;
+
+# %common_indices simply aggregates all the first-column indices in
+# the files.
+my %common_indices;
+
+# $uniq is a random unique tag, can be anything; this is used to keep
+# track of duplicate first-column indices. Set to a null byte since
+# it's unlikely we'll encounter that in a data file.
+my $uniq = qq{\x0};
+
+foreach my $file (@ARGV) {
+
+    open( my $in_fh, "<$file" ) or die("$!\n");
+
+    my %store;
+    while ( my $line = <$in_fh> ) {
+
+	# DOS and unix line endings
+        $line =~ s/[\r\n]*$//;
+
+        my @larry = split /\t/, $line, -1;
+
+        foreach my $value (@larry) {
+
+	    # Allow zero values.
+	    if ( ! defined($value) || $value eq q{} ) {
+		$value = 'null';
+	    }
+	}
+
+        my $index = shift(@larry);
+
+        while ( exists( $store{$index} ) ) {
+            $index .= $uniq;
+        }
+
+        $store{$index} = \@larry;
+
+        $common_indices{$index}++;
+    }
+
+    $HoH{$file} = \%store;
+
+}
+
+# Figure out the row array sizes
+my %max_row_sizes;
+foreach my $file (@ARGV) {
+
+    my $dataset = $HoH{$file};
+    my $max_row_size = [];
+    my @datarows     = values %$dataset;
+    foreach my $newmax_row_size (@datarows) {
+        if ( $#$max_row_size < $#$newmax_row_size ) {
+            $max_row_size = $newmax_row_size;
+        }
+    }
+    $max_row_sizes{$file} = $max_row_size;
+}
+
+# Print out a header
+my @header = q{};
+foreach my $file ( sort keys %HoH ) {
+    push(@header, map { $file } @{ $max_row_sizes{$file} });
+}
+print STDOUT (join("\t", @header),"\n");
+
+# Print out the data.
+foreach my $index ( sort keys %common_indices ) {
+
+    my $orig_index = $index;
+
+    $orig_index =~ s/$uniq//g;
+
+    print STDOUT "$orig_index";
+
+    foreach my $file (@ARGV) {
+
+	my $dataset = $HoH{$file};
+
+        my $max_row_size = $max_row_sizes{$file};
+
+        my $datastr;
+
+        if ( $dataset->{$index} ) {
+            $datastr = join( "\t", @{ $dataset->{$index} } );
+        }
+
+        else {
+            $datastr = join( "\t", map {'null'} @$max_row_size );
+        }
+
+        print STDOUT ("\t$datastr");
+    }
+
+    print STDOUT "\n";
+
+}
diff --git a/util/mx_legacy_check.pl b/util/mx_legacy_check.pl
new file mode 100755
index 0000000..884eb03
--- /dev/null
+++ b/util/mx_legacy_check.pl
@@ -0,0 +1,155 @@
+#!/usr/bin/env perl
+#
+# Script to re-check legacy MX submissions, and enter the relevant
+# info into the autosubs database.
+#
+# $Id: mx_legacy_check.pl 497 2006-10-10 21:04:09Z tfrayner $
+
+use strict;
+use warnings;
+
+require ArrayExpress::Curator::MIAMExpress;
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(date_now);
+require ArrayExpress::AutoSubmission::DB::Experiment;
+use DBI;
+use Getopt::Long;
+
+# Set up a global DB handle for MX; N.B. another handle will be
+# created for the checker instances (see MIAMExpress.pm).
+my $MX_DBH = DBI->connect(
+    $CONFIG->get_MX_DSN(),      $CONFIG->get_MX_USERNAME(),
+    $CONFIG->get_MX_PASSWORD(), $CONFIG->get_MX_DBPARAMS(),
+    )
+    or die("Database connection error: $DBI::errstr\n");
+
+########
+# SUBS #
+########
+
+sub check_sub {
+
+    my $sub = shift;
+
+    # Quick sanity check.
+    unless ( $sub->name() && $sub->miamexpress_login() ) {
+	printf("Submission %s has incomplete mx title and/or login. Skipping.\n",
+	       $sub->miamexpress_subid());
+	return;
+    }
+
+    printf(
+        "%s Checking experiment %s:  %s | %s\n",
+        date_now(),
+	$sub->miamexpress_subid(),
+	$sub->miamexpress_login(),
+        $sub->name()
+    );
+
+    my $checker = ArrayExpress::Curator::MIAMExpress->new(
+        {   mx_login           => $sub->miamexpress_login(),
+            mx_title           => $sub->name(),
+            skip_data_checks   => 1,
+            log_to_current_dir => '',
+            clobber            => 1,
+        }
+    );
+
+    eval { $checker->check() };
+
+    if ($@) {
+        $sub->set(
+            comment => $sub->comment() . "\n\nLegacy checks crashed: $@",
+        );
+        print("Checker CRASH!!\n");
+    }
+    else {
+        $sub->set(
+            miame_score          => $checker->get_miame(),
+            data_warehouse_ready => $checker->get_aedw_score(),
+        );
+    }
+
+    my $sth = $MX_DBH->prepare(<<'QUERY');
+select TSUBMIS_LAST_CHANGE from TSUBMIS
+where TSUBMIS_SYSUID=? and TSUBMIS_DEL_STATUS='U'
+QUERY
+
+    $sth->execute( $sub->miamexpress_subid() ) or die( $sth->errstr );
+
+    my $results = $sth->fetchrow_arrayref();
+
+    if ($results) {
+
+        my $mx_date = $results->[0];
+
+        my @dateparts;
+        unless (
+            @dateparts = (
+                $mx_date
+                    =~ m/\A (\d+)-(\d+)-(\d+) [ ]+ (\d+):(\d+):(\d+) \z/xms
+            )
+            ) {
+            die( "Error: Unable to parse date for submission "
+                    . $sub->miamexpress_subid() );
+        }
+
+        my $date = join( '-', @dateparts[ 0 .. 2 ] ) . 'T'
+            . join( ':', @dateparts[ 3 .. 5 ] ) . 'Z';
+
+        $sub->set( date_submitted => $date, );
+    }
+
+    $sub->update();
+
+    return;
+}
+
+########
+# MAIN #
+########
+
+my $check_all;
+GetOptions( "a|all" => \$check_all );
+
+unless ( $check_all || scalar @ARGV ) {
+    print(<<"USAGE");
+    Usage: $0 -a            (check all experiments in submissions database)
+	   $0 <MX subids>   (just check a handful)
+USAGE
+
+    exit 255;
+}
+
+if ($check_all) {
+
+    # Check all undeleted MX experiments.
+    print("Querying autosubs DB for all MX experiments...\n");
+
+    my $expt_iterator = ArrayExpress::AutoSubmission::DB::Experiment->search(
+        is_deleted      => 0,
+        experiment_type => 'MIAMExpress',
+    );
+    while ( my $sub = $expt_iterator->next() ) {
+        check_sub($sub);
+    }
+}
+else {
+
+    # Check only those experiments listed on the command line.
+    foreach my $subid (@ARGV) {
+        print("Querying autosubs DB for MX submission $subid...\n");
+        my $sub = ArrayExpress::AutoSubmission::DB::Experiment->retrieve(
+            miamexpress_subid => $subid,
+            experiment_type   => 'MIAMExpress',
+            is_deleted        => 0,
+        );
+
+	if ( $sub ) {
+	    check_sub($sub);
+	}
+	else {
+	    print ("Error: cannot find subid $subid in database. Skipping.\n");
+	}
+    }
+}
diff --git a/util/mx_qt_update.pl b/util/mx_qt_update.pl
new file mode 100755
index 0000000..5f6212e
--- /dev/null
+++ b/util/mx_qt_update.pl
@@ -0,0 +1,94 @@
+#!/usr/bin/env perl
+#
+# mx_qt_update.pl
+#
+# Maintenance script to update a QT_list.txt file with QT information taken from a local MIAMExpress installation.
+#
+# Tim Rayner 2005 ArrayExpress Team, EBI
+#
+# $Id: mx_qt_update.pl 1269 2006-12-10 12:23:13Z tfrayner $
+#
+
+use strict;
+use warnings;
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use Getopt::Long;
+use DBI;
+use DBD::mysql;
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+use ArrayExpress::Datafile::QT_list qw(
+    get_QTs
+    write_QTs
+);
+
+my $read_defaults;
+
+GetOptions( "d|defaults" => \$read_defaults, );
+
+my $new_QTs = {};
+
+if ($read_defaults) {
+    $new_QTs = get_QTs;
+}
+elsif (@ARGV) {
+    print STDOUT ("Parsing old QT file(s)... \n");
+    $new_QTs = get_QTs( \@ARGV );
+}
+
+print STDOUT ("Querying MIAMExpress for QT information... \n");
+
+my $dbh = DBI->connect(
+    $CONFIG->get_MX_DSN(),      $CONFIG->get_MX_USERNAME(),
+    $CONFIG->get_MX_PASSWORD(), $CONFIG->get_MX_DBPARAMS()
+    )
+    or die("$DBI::errstr\n");
+
+# Pull in all undeleted QTs
+my $sth = $dbh->prepare(<<'QUERY');
+select distinct TQUANTIT_TYPE, TQUANTIT_NAME, TQUANTIT_SUBCLASS,
+TQUANTIT_ISBACKGROUND, TQUANTIT_CHANNEL, TQUANTIT_SCALE,
+TQUANTIT_DATATYPE, TQUANTIT_TARGETQT, TQUANTIT_NAMESPACE,
+TQUANTIT_DESCR
+from TQUANTIT
+where TQUANTIT_DEL_STATUS='U'
+QUERY
+
+$sth->execute() or die("$sth->errstr\n");
+
+# Get the relevant stuff out of $sth
+while ( my $rowref = $sth->fetchrow_hashref ) {
+
+    my $software = $rowref->{'TQUANTIT_TYPE'} . "["
+        . $rowref->{'TQUANTIT_NAMESPACE'} . "]";
+
+    $new_QTs->{$software}{ $rowref->{'TQUANTIT_NAME'} }{datatype}
+        = $rowref->{'TQUANTIT_DATATYPE'} || q{};
+
+    $new_QTs->{$software}{ $rowref->{'TQUANTIT_NAME'} }{scale}
+        = $rowref->{'TQUANTIT_SCALE'} || q{};
+
+    $new_QTs->{$software}{ $rowref->{'TQUANTIT_NAME'} }{subclass}
+        = $rowref->{'TQUANTIT_SUBCLASS'} || q{};
+
+    $new_QTs->{$software}{ $rowref->{'TQUANTIT_NAME'} }{is_background}
+        = $rowref->{'TQUANTIT_ISBACKGROUND'} || q{};
+
+    $new_QTs->{$software}{ $rowref->{'TQUANTIT_NAME'} }{confmap}
+        = $rowref->{'TQUANTIT_TARGETQT'} || q{};
+
+    $new_QTs->{$software}{ $rowref->{'TQUANTIT_NAME'} }{channel}
+        = $rowref->{'TQUANTIT_CHANNEL'} || q{};
+
+    $new_QTs->{$software}{ $rowref->{'TQUANTIT_NAME'} }{description}
+        = $rowref->{'TQUANTIT_DESCR'} || q{};
+}
+
+$sth->finish();  # dump the statement handler now that we are finished with it
+
+write_QTs( $new_QTs, \*STDOUT );
+
diff --git a/util/t2m_legacy_check.pl b/util/t2m_legacy_check.pl
new file mode 100755
index 0000000..b806e70
--- /dev/null
+++ b/util/t2m_legacy_check.pl
@@ -0,0 +1,138 @@
+#!/usr/bin/env perl
+#
+# Script to re-check legacy spreadsheet submissions, and enter the
+# relevant info into the autosubs database.
+#
+# $Id$
+
+use strict;
+use warnings;
+
+use ArrayExpress::Curator::Validate;
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(date_now);
+require ArrayExpress::AutoSubmission::DB::Experiment;
+use Getopt::Long;
+
+########
+# SUBS #
+########
+
+sub check_sub {
+
+    my $sub = shift;
+
+    my $spreadsheet = $sub->spreadsheets(is_deleted => 0)->next();
+
+    # Quick sanity check.
+    unless ( $spreadsheet ) {
+	printf("Submission %s has no spreadsheet. Skipping.\n",
+	       $sub->id());
+	return;
+    }
+
+    printf(
+        "%s Checking experiment %s:  %s | %s\n",
+        date_now(),
+	$sub->id(),
+	$sub->user_id()->login(),
+        $sub->name()
+    );
+
+    my $checker = ArrayExpress::Curator::Validate->new(
+        {   spreadsheet_filename => $spreadsheet->filesystem_path(),
+	    source_directory     => $sub->unpack_directory(),
+	    skip_data_checks     => 1,
+            log_to_current_dir   => q{},
+            clobber              => 1,
+        }
+    );
+
+    eval { $checker->check() };
+
+    if ($@) {
+        $sub->set(
+            comment => $sub->comment() . "\n\nLegacy checks crashed: $@",
+        );
+        print("Checker CRASH!!\n");
+    }
+    else {
+        $sub->set(
+            miame_score          => $checker->get_miame(),
+            data_warehouse_ready => $checker->get_aedw_score(),
+        );
+    }
+
+    $sub->update();
+
+    return;
+}
+
+########
+# MAIN #
+########
+
+my ($check_all, $experiment_type);
+GetOptions(
+    "a|all"    => \$check_all,
+    "t|type=s" => \$experiment_type,
+);
+
+unless ( $check_all || scalar @ARGV ) {
+    print(<<"USAGE");
+    Usage: $0 -a            (check all experiments in submissions database)
+	   $0 <sub ids>     (just check a handful)
+
+    Optional: -t    (specify experiment type, e.g. "Tab2MAGE", "GEO")
+
+USAGE
+
+    exit 255;
+}
+
+$experiment_type ||= 'Tab2MAGE';
+
+if ($check_all) {
+
+    # Check all undeleted spreadsheet experiments.
+    print("Querying autosubs DB for all $experiment_type spreadsheet experiments...\n");
+
+    my $expt_iterator = ArrayExpress::AutoSubmission::DB::Experiment->search(
+        is_deleted      => 0,
+        experiment_type => $experiment_type,
+    );
+
+    EVERYEXPT:
+    while ( my $sub = $expt_iterator->next() ) {
+	if ( $sub->user_id()
+		 && scalar( $sub->spreadsheets(is_deleted => 0) ) ) {
+	    check_sub($sub);
+	}
+    }
+}
+else {
+
+    # Check only those experiments listed on the command line.
+    EACHEXPT:
+    foreach my $id (@ARGV) {
+        print("Querying autosubs DB for $experiment_type spreadsheet submission $id...\n");
+        my $sub = ArrayExpress::AutoSubmission::DB::Experiment->retrieve(
+            id                => $id,
+            experiment_type   => $experiment_type,
+            is_deleted        => 0,
+	    in_curation       => 1,
+        );
+	if ( $sub ) {
+	    unless ( $sub->user_id() ) {
+		print ("Submission $id has no associated user. Skipping.\n");
+		next EACHEXPT;
+	    }
+
+	    # Run the tests here.
+	    check_sub($sub);
+	}
+	else {
+	    print ("Error: cannot find submission with id $id in database. Skipping.\n");
+	}
+    }
+}
diff --git a/util/t2m_update_docs.pl b/util/t2m_update_docs.pl
new file mode 100755
index 0000000..239c9de
--- /dev/null
+++ b/util/t2m_update_docs.pl
@@ -0,0 +1,93 @@
+#!/usr/bin/env perl
+#
+# t2m_update_docs.pl
+#
+# Tim Rayner 2005 ArrayExpress Team, EBI
+#
+# $Id: t2m_update_docs.pl 2069 2008-06-04 14:33:52Z tfrayner $
+#
+
+use 5.8.0;    # Need a recent Pod::HTML, which only comes with newer Perls
+
+use strict;
+use warnings;
+
+use Pod::Html;
+use File::Spec;
+
+foreach my $doc qw(
+    bin/expt_check.pl
+    bin/tab2mage.pl
+    bin/magetab.pl
+    bin/t2m_visualize.pl
+    bin/parse_affy.pl
+    lib/ArrayExpress/ArrayMAGE.pm
+    lib/ArrayExpress/AutoSubmission/DB.pm
+    lib/ArrayExpress/AutoSubmission/Creator.pm
+    lib/ArrayExpress/AutoSubmission/Spreadsheet.pm
+    lib/ArrayExpress/AutoSubmission/WebForm.pm
+    lib/ArrayExpress/Curator/Config.pm
+    lib/ArrayExpress/Curator/Common.pm
+    lib/ArrayExpress/Curator/Database.pm
+    lib/ArrayExpress/Curator/Report.pm
+    lib/ArrayExpress/Curator/Entrez_list.pm
+    lib/ArrayExpress/Curator/Visualize.pm
+    lib/ArrayExpress/Curator/ExperimentChecker.pm
+    lib/ArrayExpress/Curator/Logger.pm
+    lib/ArrayExpress/Curator/MAGE/Definitions.pm
+    lib/ArrayExpress/Curator/MIAMExpress.pm
+    lib/ArrayExpress/Curator/Standalone.pm
+    lib/ArrayExpress/Curator/Validate.pm
+    lib/ArrayExpress/Datafile/Affymetrix/CDF/GDAC_CDF.pm
+    lib/ArrayExpress/Datafile/Affymetrix/CDF/XDA_CDF.pm
+    lib/ArrayExpress/Datafile/Affymetrix/CDF.pm
+    lib/ArrayExpress/Datafile/Affymetrix/CDFFactory.pm
+    lib/ArrayExpress/Datafile/Affymetrix/CEL/CELv3.pm
+    lib/ArrayExpress/Datafile/Affymetrix/CEL/CELv4.pm
+    lib/ArrayExpress/Datafile/Affymetrix/CEL.pm
+    lib/ArrayExpress/Datafile/Affymetrix/CELFactory.pm
+    lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv12.pm
+    lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv13.pm
+    lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv8.pm
+    lib/ArrayExpress/Datafile/Affymetrix/CHP/GDAC_CHP.pm
+    lib/ArrayExpress/Datafile/Affymetrix/CHP/XDA_CHP.pm
+    lib/ArrayExpress/Datafile/Affymetrix/CHP.pm
+    lib/ArrayExpress/Datafile/Affymetrix/CHPFactory.pm
+    lib/ArrayExpress/Datafile/Affymetrix/EXP.pm
+    lib/ArrayExpress/Datafile/Affymetrix/EXPFactory.pm
+    lib/ArrayExpress/Datafile/Affymetrix/Parser.pm
+    lib/ArrayExpress/Datafile/Affymetrix.pm
+    lib/ArrayExpress/Datafile/Parser.pm
+    lib/ArrayExpress/Datafile/QT_list.pm
+    lib/ArrayExpress/Datafile/ArrayDesign.pm
+    lib/ArrayExpress/Datafile.pm
+    lib/ArrayExpress/MAGETAB.pm
+    lib/ArrayExpress/MAGETAB/Checker.pm
+    ) {
+
+    unless ( -f $doc ) {
+        die("Error: Input file not found: $doc\n");
+    }
+
+    my ( $directory, $file ) = ( File::Spec->splitpath($doc) )[ 1, 2 ];
+
+    my @dirs = File::Spec->splitdir($directory);
+
+    shift @dirs;    # top-level dir is always stripped
+
+    my $htmldoc
+        = File::Spec->catfile( 'docs', @dirs, $file );    # docs subdir
+
+    $htmldoc =~ s!\.p[mlh]$!\.html!i;                 # replace extension
+
+    print "Creating $htmldoc\n";
+
+    pod2html(
+        "--infile=$doc",         "--outfile=$htmldoc",
+        "--css=/docs/style.css", "--noindex",
+        "--htmldir=docs",        "--podroot=lib",
+    );
+
+    system("tidy -m $htmldoc");
+
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/tab2mage.git



More information about the debian-med-commit mailing list