From e837348b4dc76bec10619f837b606ca7de04ab9b Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 31 Oct 2012 08:49:07 +0100 Subject: [PATCH 001/108] remove throws DataFormatException from reset --- src/org/molgenis/util/TupleReader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/molgenis/util/TupleReader.java b/src/org/molgenis/util/TupleReader.java index 465a0b628..c131609e7 100644 --- a/src/org/molgenis/util/TupleReader.java +++ b/src/org/molgenis/util/TupleReader.java @@ -100,7 +100,7 @@ public interface TupleReader extends TupleIterable, Closeable * @throws DataFormatException * @throws IOException */ - public void reset() throws IOException, DataFormatException; + public void reset() throws IOException; /** ask whether the source of the reader is closed **/ public boolean isClosed(); From 7513c037ba32832c791264c603c8468556cae954 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 31 Oct 2012 08:50:05 +0100 Subject: [PATCH 002/108] ignore specific Findbugs warnings, fix encoding warnings --- src/org/molgenis/util/trityper/reader/SNP.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/org/molgenis/util/trityper/reader/SNP.java b/src/org/molgenis/util/trityper/reader/SNP.java index 54484d674..10019b09b 100644 --- a/src/org/molgenis/util/trityper/reader/SNP.java +++ b/src/org/molgenis/util/trityper/reader/SNP.java @@ -4,10 +4,14 @@ */ package org.molgenis.util.trityper.reader; +import java.nio.charset.Charset; + /** * * @author harmjan */ +@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = +{ "EI_EXPOSE_REP", "EI_EXPOSE_REP2" }, justification = "Not accessed by untrusted code") public class SNP { private byte chr; @@ -118,11 +122,11 @@ public void setAlleles(byte[] allele1, byte[] allele2, Boolean[] indIncluded, Bo System.out.println("ERROR!!!: Number of different alleles for SNP\t" + name + "\t is more than two!"); System.out.println("Allele 1:\t" + alleles[0] + " / " + new String(new byte[] - { alleles[0] })); + { alleles[0] }, Charset.forName("UTF-8"))); System.out.println("Allele 2:\t" + alleles[1] + " / " + new String(new byte[] - { alleles[1] })); + { alleles[1] }, Charset.forName("UTF-8"))); System.out.println("Allele 3:\t" + alleles[2] + " / " + new String(new byte[] - { alleles[2] })); + { alleles[2] }, Charset.forName("UTF-8"))); break; } } From a81f608d13c45975515a73320a5b287f2607d26f Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 31 Oct 2012 08:50:44 +0100 Subject: [PATCH 003/108] rename target findbugs-html to findbugs.html --- build.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.xml b/build.xml index 07919a9d4..20ff85ea5 100644 --- a/build.xml +++ b/build.xml @@ -151,7 +151,7 @@ - + Date: Thu, 1 Nov 2012 11:15:01 +0100 Subject: [PATCH 004/108] Updated pom.xml. --- pom.xml | 51 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/pom.xml b/pom.xml index 56355ea64..358fac610 100644 --- a/pom.xml +++ b/pom.xml @@ -85,6 +85,11 @@ commons-lang 2.6 + + org.apache.commons + commons-lang3 + 3.0 + commons-fileupload commons-fileupload @@ -134,7 +139,14 @@ ganymed-ssh2 build210 - + + + + com.kenai.nbpwr + edu-umd-cs-findbugs-annotations + 1.3.2-201002241900 + + org.testng @@ -151,7 +163,7 @@ org.mockito mockito-all - 1.8.4 + 1.9.0 test @@ -168,6 +180,11 @@ poi 3.8 + + org.apache.poi + poi-ooxml + 3.8 + net.sourceforge.jexcelapi jxl @@ -253,6 +270,24 @@ + + org.codehaus.mojo + build-helper-maven-plugin + + + add-test-source + generate-sources + + add-test-source + + + + src + + + + + org.apache.maven.plugins maven-compiler-plugin @@ -263,16 +298,6 @@ ${endorsed.dir} - @@ -282,9 +307,7 @@ **/*ExcelReaderTest.java - **/*PbsTest.java **/*PlinkTest.java - **/*VcfReaderTest.java From 81066f7c87b5df7a68e58dec2d22b5a1d691b21e Mon Sep 17 00:00:00 2001 From: rw42 Date: Thu, 1 Nov 2012 11:15:30 +0100 Subject: [PATCH 005/108] Deleted pom_minimal.xml --- pom_minimal.xml | 302 ------------------------------------------------ 1 file changed, 302 deletions(-) delete mode 100644 pom_minimal.xml diff --git a/pom_minimal.xml b/pom_minimal.xml deleted file mode 100644 index 58859b4d0..000000000 --- a/pom_minimal.xml +++ /dev/null @@ -1,302 +0,0 @@ - - 4.0.0 - - org.molgenis - molgenis - 1.0-SNAPSHOT - jar - - molgenis - - - ${project.build.directory}/endorsed - UTF-8 - - - - - - - org.hibernate - hibernate-entitymanager - 3.6.9.Final - jar - - - org.hibernate - hibernate-validator - 4.2.0.Final - - - commons-dbcp - commons-dbcp - 1.4 - - - - commons-pool - commons-pool - 20030825.183949 - runtime - - - mysql - mysql-connector-java - 5.1.20 - provided - - - hsqldb - hsqldb - 1.8.0.10 - provided - - - - - - javax.servlet - servlet-api - 2.5 - provided - - - - - taglibs - standard - 1.1.2 - - - javax.servlet - jstl - 1.2 - runtime - - - - - commons-lang - commons-lang - 2.6 - - - commons-fileupload - commons-fileupload - 1.2 - - - org.apache.commons - commons-email - 1.2 - - - - - - org.quartz-scheduler - quartz - 1.8.6 - - - - - - org.freemarker - freemarker - 2.3.18 - - - - - ch.ethz.ganymed - ganymed-ssh2 - build210 - - - - - org.testng - testng - 6.4 - test - - - junit - junit - 4.10 - test - - - - - org.hibernate - hibernate-search - 3.4.1.Final - - - - - net.sourceforge.jexcelapi - jxl - 2.6.12 - - - - - javatar - javatar - 2.5 - - - - - org.apache.cxf - cxf-bundle-minimal - 2.5.2 - - - - - xerces - xercesImpl - 2.10.0 - - - - - net.sf.json-lib - json-lib - 2.4 - jdk15 - - - com.google.code.gson - gson - 2.2 - compile - - - - org.apache.ant - ant - 1.8.2 - - - - org.apache.ant - ant-apache-log4j - 1.8.2 - - - - - org.json - json - 20090211 - - - - - org.jboss.resteasy - tjws - 2.3.4.Final - - - - - src - src - - - - - src/ - - **/*.ftl - **/*.properties - **/*.index - org/molgenis/framework/ui/res/** - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 2.3.2 - - 1.6 - 1.6 - - ${endorsed.dir} - - - **/*Test.java - **/Test*.java - - - **/*Test.java - **/Test*.java - - - - - maven-surefire-plugin - 2.6 - - - - **/*ExcelReaderTest.java - **/*PbsTest.java - **/*PlinkTest.java - **/*VcfReaderTest.java - - - - - org.apache.maven.plugins - maven-dependency-plugin - 2.1 - - - validate - - copy - - - ${endorsed.dir} - true - - - javax - javaee-endorsed-api - 6.0 - jar - - - - - - - - - - From e224d19e3ca963b04ab0e3d9091c00a63176340b Mon Sep 17 00:00:00 2001 From: Despoina Antonakaki Date: Thu, 1 Nov 2012 13:16:39 +0100 Subject: [PATCH 006/108] Use constructor to set blockStart. Using setBlockStart() never works because parsing starts already in constructor. --- src/org/molgenis/util/CsvFileReader.java | 35 ++++++++++++++++++++++++ src/org/molgenis/util/vcf/VcfReader.java | 4 +-- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/org/molgenis/util/CsvFileReader.java b/src/org/molgenis/util/CsvFileReader.java index 9301ad498..fc50281a6 100644 --- a/src/org/molgenis/util/CsvFileReader.java +++ b/src/org/molgenis/util/CsvFileReader.java @@ -35,6 +35,21 @@ public CsvFileReader(File file) throws IOException this(file, CHARSET_UTF8, true); } + /** + * Creates a CsvFileReader that uses the UTF-8 charset + * + * @param file + * comma-separated values file + * @param blockStart + * last line before header block starts + * @throws IOException + * @throws DataFormatException + */ + public CsvFileReader(File file, String blockStart) throws IOException + { + this(file, CHARSET_UTF8, true, blockStart); + } + /** * Creates a CsvFileReader that uses the UTF-8 charset * @@ -78,12 +93,32 @@ public CsvFileReader(File file, Charset charset) throws IOException * @throws DataFormatException */ public CsvFileReader(File file, Charset charset, boolean hasHeader) throws IOException + { + this(file, charset, hasHeader, ""); + } + + /** + * Creates a CsvFileReader that uses the given charset + * + * @param file + * comma-separated values file + * @param charset + * file encoding + * @param hasHeader + * whether or not this file starts with a header + * @param blockStart + * last line before header block starts + * @throws IOException + * @throws DataFormatException + */ + public CsvFileReader(File file, Charset charset, boolean hasHeader, String blockStart) throws IOException { super(); if (file == null) throw new IllegalArgumentException("file is null"); this.file = file; this.charset = charset; this.hasHeader = hasHeader; + this.blockStart = blockStart; this.reset(); } diff --git a/src/org/molgenis/util/vcf/VcfReader.java b/src/org/molgenis/util/vcf/VcfReader.java index 7dfdd671e..dfe6625f9 100644 --- a/src/org/molgenis/util/vcf/VcfReader.java +++ b/src/org/molgenis/util/vcf/VcfReader.java @@ -24,8 +24,6 @@ public class VcfReader public VcfReader(File f) throws IOException, DataFormatException { - reader = new CsvFileReader(f); - // iterate through file to find the last line with ##, that is the // blockstart String blockStart = null; @@ -49,7 +47,7 @@ public VcfReader(File f) throws IOException, DataFormatException { IOUtils.closeQuietly(br); } - reader.setBlockStart(blockStart); + reader = new CsvFileReader(f, blockStart); } public List getFilters() From 8b3fd3c65f854717d262a58a33d6ef216f0204d2 Mon Sep 17 00:00:00 2001 From: Joeri van der Velde Date: Fri, 2 Nov 2012 11:07:15 +0100 Subject: [PATCH 007/108] Removed references to nonexisting generated folders --- .classpath | 2 -- 1 file changed, 2 deletions(-) diff --git a/.classpath b/.classpath index 476da60bc..5609d2fd0 100644 --- a/.classpath +++ b/.classpath @@ -3,8 +3,6 @@ - - From ce4b733ab33e993f941629e979ad0f256771800e Mon Sep 17 00:00:00 2001 From: Despoina Antonakaki Date: Fri, 2 Nov 2012 13:27:44 +0100 Subject: [PATCH 008/108] Added test for blockStart. --- .../org/molgenis/util/CsvFileReaderTest.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/java/org/molgenis/util/CsvFileReaderTest.java b/test/java/org/molgenis/util/CsvFileReaderTest.java index 11d697685..058c93697 100644 --- a/test/java/org/molgenis/util/CsvFileReaderTest.java +++ b/test/java/org/molgenis/util/CsvFileReaderTest.java @@ -163,4 +163,35 @@ public void testMultilineQuotesEscapingAndEmptyLines() throws Exception file0.delete(); } } + + @Test + public void testBlockStart() throws Exception + { + File file0 = File.createTempFile("CsvFileReaderTest_file0", null); + try + { + FileUtils.write(file0, + "##bla1\n##bla2\n##bla3\nHeader1\tHeader2\nRow1Col1\tRow1Col2\nRow2Col1\tRow2Col2\n", + Charset.forName("UTF-8")); + + CsvFileReader reader = new CsvFileReader(file0, "##bla3"); + + try + { + // test columnnames + List columnNames = reader.columnnames; + assertEquals(2, columnNames.size()); + assertEquals("Header1", columnNames.get(0)); + assertEquals("Header2", columnNames.get(1)); + } + finally + { + IOUtils.closeQuietly(reader); + } + } + finally + { + file0.delete(); + } + } } \ No newline at end of file From 8c864de7d04bae601678e9a44b7edfe556fc683c Mon Sep 17 00:00:00 2001 From: Joeri van der Velde Date: Fri, 2 Nov 2012 18:06:47 +0100 Subject: [PATCH 009/108] Fixing in-list references: entities should be able to refer to same-type entities that come together in one atomic import --- .../molgenis/framework/db/ResolveResult.java | 38 ++++++++++++++++ .../generators/csv/CsvReaderGen.java.ftl | 44 +++++++++++++------ 2 files changed, 68 insertions(+), 14 deletions(-) create mode 100644 src/org/molgenis/framework/db/ResolveResult.java diff --git a/src/org/molgenis/framework/db/ResolveResult.java b/src/org/molgenis/framework/db/ResolveResult.java new file mode 100644 index 000000000..fddbf5f97 --- /dev/null +++ b/src/org/molgenis/framework/db/ResolveResult.java @@ -0,0 +1,38 @@ +package org.molgenis.framework.db; + +import java.util.List; + +/** + * Helper bean for resolveForeignKeys in generated CsvReader classes. + * + * @author joeri + * + * @param + */ +public class ResolveResult +{ + + List resolved; + List unresolved; + + public List getResolved() + { + return resolved; + } + + public void setResolved(List resolved) + { + this.resolved = resolved; + } + + public List getUnresolved() + { + return unresolved; + } + + public void setUnresolved(List unresolved) + { + this.unresolved = unresolved; + } + +} diff --git a/src/org/molgenis/generators/csv/CsvReaderGen.java.ftl b/src/org/molgenis/generators/csv/CsvReaderGen.java.ftl index 73fd15899..401b6b0c8 100644 --- a/src/org/molgenis/generators/csv/CsvReaderGen.java.ftl +++ b/src/org/molgenis/generators/csv/CsvReaderGen.java.ftl @@ -34,6 +34,7 @@ import org.molgenis.framework.db.Database; import org.molgenis.framework.db.DatabaseException; import org.molgenis.framework.db.Query; import org.molgenis.framework.db.Database.DatabaseAction; +import org.molgenis.framework.db.ResolveResult; import org.molgenis.util.CsvReader; import org.molgenis.util.Tuple; @@ -89,14 +90,15 @@ public class ${JavaName(entity)}CsvReader extends CsvToDatabase<${JavaName(entit if(${name(entity)}List.size() == BATCH_SIZE) { //resolve foreign keys and copy those entities that could not be resolved to the missingRefs list - ${name(entity)}sMissingRefs.addAll(resolveForeignKeys(db, ${name(entity)}List)); + ResolveResult<${JavaName(entity)}> resolveResult = resolveForeignKeys(db, ${name(entity)}List); + ${name(entity)}sMissingRefs.addAll(resolveResult.getUnresolved()); <#if entity.getXrefLabels()?exists> //update objects in the database using xref_label defined secondary key(s) '${csv(entity.getXrefLabels())}' defined in xref_label - db.update(${name(entity)}List,dbAction<#list entity.getXrefLabels() as label>, "${label}"); + db.update(resolveResult.getResolved(),dbAction<#list entity.getXrefLabels() as label>, "${label}"); <#else> //update objects in the database using primary key(<#list entity.getAllKeys()[0].fields as field><#if field_index != 0>,${field.name}) - db.update(${name(entity)}List,dbAction<#list entity.getAllKeys()[0].fields as field>, "${field.name}"); + db.update(resolveResult.getResolved(),dbAction<#list entity.getAllKeys()[0].fields as field>, "${field.name}"); //clear for next batch @@ -111,30 +113,31 @@ public class ${JavaName(entity)}CsvReader extends CsvToDatabase<${JavaName(entit if(!${name(entity)}List.isEmpty()) { //resolve foreign keys, again keeping track of those entities that could not be solved - ${name(entity)}sMissingRefs.addAll(resolveForeignKeys(db, ${name(entity)}List)); + ResolveResult<${JavaName(entity)}> resolveResult = resolveForeignKeys(db, ${name(entity)}List); + ${name(entity)}sMissingRefs.addAll(resolveResult.getUnresolved()); <#if entity.getXrefLabels()?exists> //update objects in the database using xref_label defined secondary key(s) '${csv(entity.getXrefLabels())}' defined in xref_label - db.update(${name(entity)}List,dbAction<#list entity.getXrefLabels() as label>, "${label}"); + db.update(resolveResult.getResolved(),dbAction<#list entity.getXrefLabels() as label>, "${label}"); <#else> //update objects in the database using primary key(<#list entity.getAllKeys()[0].fields as field><#if field_index != 0>,${field.name}) - db.update(${name(entity)}List,dbAction<#list entity.getAllKeys()[0].fields as field>, "${field.name}"); + db.update(resolveResult.getResolved(),dbAction<#list entity.getAllKeys()[0].fields as field>, "${field.name}"); } //second import round, try to resolve FK's for entities again as they might have referred to entities in the imported list - List<${JavaName(entity)}> ${name(entity)}sStillMissingRefs = resolveForeignKeys(db, ${name(entity)}sMissingRefs); + ResolveResult<${JavaName(entity)}> resolveResult = resolveForeignKeys(db, ${name(entity)}List); //if there are still missing references, throw error and rollback - if(${name(entity)}sStillMissingRefs.size() > 0){ - throw new Exception("Import of '${JavaName(entity)}' objects failed: attempting to resolve in-list references, but there are still ${JavaName(entity)}s referring to ${JavaName(entity)}s that are neither in the database nor in the list of to-be imported ${JavaName(entity)}s. (the first one being: "+${name(entity)}sStillMissingRefs.get(0)+")"); + if(resolveResult.getUnresolved().size() > 0){ + throw new Exception("Import of '${JavaName(entity)}' objects failed: attempting to resolve in-list references, but there are still ${JavaName(entity)}s referring to ${JavaName(entity)}s that are neither in the database nor in the list of to-be imported ${JavaName(entity)}s. (the first one being: " + resolveResult.getUnresolved().get(0)+")"); } - //else update the entities in the database with the found references and return total + //else add the rest of the entities to the database and return total else { <#if entity.getXrefLabels()?exists> - db.update(${name(entity)}sMissingRefs,DatabaseAction.UPDATE<#list entity.getXrefLabels() as label>, "${label}"); + db.update(resolveResult.getResolved(),DatabaseAction.ADD<#list entity.getXrefLabels() as label>, "${label}"); <#else> - db.update(${name(entity)}sMissingRefs,DatabaseAction.UPDATE<#list entity.getAllKeys()[0].fields as field>, "${field.name}"); + db.update(resolveResult.getResolved(),DatabaseAction.ADD<#list entity.getAllKeys()[0].fields as field>, "${field.name}"); //output count @@ -152,8 +155,11 @@ public class ${JavaName(entity)}CsvReader extends CsvToDatabase<${JavaName(entit * @param ${name(entity)}List * @return the entities for which foreign keys cannot be resolved */ - private List<${JavaName(entity)}> resolveForeignKeys(Database db, List<${JavaName(entity)}> ${name(entity)}List) throws Exception + private ResolveResult<${JavaName(entity)}> resolveForeignKeys(Database db, List<${JavaName(entity)}> ${name(entity)}List) throws Exception { + //wrapper bean for resolved and unresolved entities, sorted out from ${name(entity)}List + ResolveResult<${JavaName(entity)}> resolveResult = new ResolveResult<${JavaName(entity)}>(); + //keep a list of ${entity.name} instances that miss a reference which might be resolvable later List<${JavaName(entity)}> ${name(entity)}sMissingRefs = new ArrayList<${JavaName(entity)}>(); @@ -305,7 +311,17 @@ public class ${JavaName(entity)}CsvReader extends CsvToDatabase<${JavaName(entit ${name(f)}Keymap.clear(); - return ${name(entity)}sMissingRefs; + List<${JavaName(entity)}> resolved = new ArrayList<${JavaName(entity)}>(); + for(${JavaName(entity)} e : ${name(entity)}List) + { + if(!${name(entity)}sMissingRefs.contains(e)){ + resolved.add(e); + } + } + resolveResult.setResolved(resolved); + resolveResult.setUnresolved(${name(entity)}sMissingRefs); + + return resolveResult; } } From 2110932555c2e9214f02dc70ff5229abb4dbe23c Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Fri, 2 Nov 2012 18:26:27 +0100 Subject: [PATCH 010/108] add functionality --- src/org/molgenis/util/XlsWriter.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/org/molgenis/util/XlsWriter.java b/src/org/molgenis/util/XlsWriter.java index 558f57e45..94278e56e 100644 --- a/src/org/molgenis/util/XlsWriter.java +++ b/src/org/molgenis/util/XlsWriter.java @@ -14,6 +14,7 @@ import jxl.write.WritableSheet; import jxl.write.WritableWorkbook; import jxl.write.WriteException; +import jxl.write.biff.RowsExceededException; /** * Write values to an Excel file @@ -80,6 +81,22 @@ public void writeHeader() throws Exception } } + public void writeCell(int col, int row, String value) throws IOException + { + try + { + sheet.addCell(new Label(col, row, value)); + } + catch (RowsExceededException e) + { + throw new IOException(e); + } + catch (WriteException e) + { + throw new IOException(e); + } + } + @Override public void setHeaders(List headers) { From 05c2a925ed9a686a9dd78685688c1e70fad71008 Mon Sep 17 00:00:00 2001 From: Joeri van der Velde Date: Fri, 2 Nov 2012 18:47:44 +0100 Subject: [PATCH 011/108] Working on FK resolving problem, needs more work still --- src/org/molgenis/generators/csv/CsvReaderGen.java.ftl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/org/molgenis/generators/csv/CsvReaderGen.java.ftl b/src/org/molgenis/generators/csv/CsvReaderGen.java.ftl index 401b6b0c8..8f048ef20 100644 --- a/src/org/molgenis/generators/csv/CsvReaderGen.java.ftl +++ b/src/org/molgenis/generators/csv/CsvReaderGen.java.ftl @@ -131,13 +131,16 @@ public class ${JavaName(entity)}CsvReader extends CsvToDatabase<${JavaName(entit if(resolveResult.getUnresolved().size() > 0){ throw new Exception("Import of '${JavaName(entity)}' objects failed: attempting to resolve in-list references, but there are still ${JavaName(entity)}s referring to ${JavaName(entity)}s that are neither in the database nor in the list of to-be imported ${JavaName(entity)}s. (the first one being: " + resolveResult.getUnresolved().get(0)+")"); } - //else add the rest of the entities to the database and return total + //else add/update the rest of the entities and return total + //FIXME: this is now ADD_UPDATE_EXISTING because we try to import already imported entities? + //this function needs a thorough cleanup and a solid FK resolving strategy!! ie. disable FK's, import, and update all entities + //so that any dependency tree is imported in 2 steps instead of being able to only import 'one layer' of references, or using inefficient/dangerous N-tries approach else { <#if entity.getXrefLabels()?exists> - db.update(resolveResult.getResolved(),DatabaseAction.ADD<#list entity.getXrefLabels() as label>, "${label}"); + db.update(resolveResult.getResolved(),DatabaseAction.ADD_UPDATE_EXISTING<#list entity.getXrefLabels() as label>, "${label}"); <#else> - db.update(resolveResult.getResolved(),DatabaseAction.ADD<#list entity.getAllKeys()[0].fields as field>, "${field.name}"); + db.update(resolveResult.getResolved(),DatabaseAction.ADD_UPDATE_EXISTING<#list entity.getAllKeys()[0].fields as field>, "${field.name}"); //output count From 9e829edab4e39db9cf58adccdb91ce0c9cb7c9ec Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Sat, 3 Nov 2012 17:40:19 +0100 Subject: [PATCH 012/108] fix bug: close stream --- src/org/molgenis/Molgenis.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/org/molgenis/Molgenis.java b/src/org/molgenis/Molgenis.java index 49cc207e1..2a3ae98a3 100644 --- a/src/org/molgenis/Molgenis.java +++ b/src/org/molgenis/Molgenis.java @@ -20,6 +20,7 @@ import java.util.Map; import org.apache.commons.dbcp.BasicDataSource; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.apache.log4j.BasicConfigurator; @@ -804,12 +805,18 @@ else if (answer.equals("n")) try { BufferedReader in = new BufferedReader(new FileReader(create_tables_file)); - String line; - while ((line = in.readLine()) != null) + try + { + String line; + while ((line = in.readLine()) != null) + { + create_tables_sqlBuilder.append(line).append('\n'); + } + } + finally { - create_tables_sqlBuilder.append(line).append('\n'); + IOUtils.closeQuietly(in); } - in.close(); } catch (IOException e) { From 6637da3fe9f9265fa23aaf7a18a72d0e32ee064e Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Sat, 3 Nov 2012 17:41:21 +0100 Subject: [PATCH 013/108] style: remove template doc header --- src/org/molgenis/framework/db/jpa/JpaFramework.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/org/molgenis/framework/db/jpa/JpaFramework.java b/src/org/molgenis/framework/db/jpa/JpaFramework.java index 88f4be19c..8147ec3df 100644 --- a/src/org/molgenis/framework/db/jpa/JpaFramework.java +++ b/src/org/molgenis/framework/db/jpa/JpaFramework.java @@ -1,7 +1,3 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ package org.molgenis.framework.db.jpa; import java.sql.Connection; From ee81ee16817af746f5cbbef78eac611b46bc10c7 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Sat, 3 Nov 2012 17:41:54 +0100 Subject: [PATCH 014/108] fix bug: close stream --- src/org/molgenis/framework/tupletable/impl/CsvTable.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/org/molgenis/framework/tupletable/impl/CsvTable.java b/src/org/molgenis/framework/tupletable/impl/CsvTable.java index 699cf2f82..1ea0147f6 100644 --- a/src/org/molgenis/framework/tupletable/impl/CsvTable.java +++ b/src/org/molgenis/framework/tupletable/impl/CsvTable.java @@ -13,6 +13,7 @@ import java.util.List; import java.util.zip.DataFormatException; +import org.apache.commons.io.IOUtils; import org.molgenis.framework.tupletable.AbstractTupleTable; import org.molgenis.framework.tupletable.TableException; import org.molgenis.model.elements.Field; @@ -82,9 +83,9 @@ public int getCount() throws TableException { if (rowCount == -1) { + LineNumberReader lineReader = new LineNumberReader(new InputStreamReader(countStream)); try { - LineNumberReader lineReader = new LineNumberReader(new InputStreamReader(countStream)); String line = null; while ((line = lineReader.readLine()) != null) { @@ -98,6 +99,10 @@ public int getCount() throws TableException { throw new TableException(e); } + finally + { + IOUtils.closeQuietly(lineReader); + } } return rowCount; } From 4188f8e9caf92879b9f9262453ad237dc20a0a46 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Sat, 3 Nov 2012 17:43:38 +0100 Subject: [PATCH 015/108] fix bug: toHtml() should return all elements --- src/org/molgenis/framework/ui/html/JQueryTreeView.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/org/molgenis/framework/ui/html/JQueryTreeView.java b/src/org/molgenis/framework/ui/html/JQueryTreeView.java index d97c5af29..8723d0e2b 100644 --- a/src/org/molgenis/framework/ui/html/JQueryTreeView.java +++ b/src/org/molgenis/framework/ui/html/JQueryTreeView.java @@ -1,5 +1,6 @@ package org.molgenis.framework.ui.html; +import java.util.Collections; import java.util.List; import java.util.Vector; @@ -129,6 +130,6 @@ public String toHtml(List selected) @Override public String toHtml() { - return ""; + return toHtml(Collections. emptyList()); } } \ No newline at end of file From 1f191b1d0d825a1e9c0da8e9ffcf1663ca63f556 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Sat, 3 Nov 2012 17:44:48 +0100 Subject: [PATCH 016/108] ignore findbugs warnings, replace .equals("") with .isEmpty() --- src/org/molgenis/util/SimpleTuple.java | 32 +++++++++++++++----------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/org/molgenis/util/SimpleTuple.java b/src/org/molgenis/util/SimpleTuple.java index 3b014d68f..243a8025a 100644 --- a/src/org/molgenis/util/SimpleTuple.java +++ b/src/org/molgenis/util/SimpleTuple.java @@ -231,7 +231,7 @@ public void set(List values) public Integer getInt(int column) { - if (getObject(column) == null || getString(column).equals("")) return null; + if (getObject(column) == null || getString(column).isEmpty()) return null; try { return Integer.valueOf(getString(column)); @@ -245,7 +245,7 @@ public Integer getInt(int column) public Integer getInt(String column) { - if (getObject(column) == null || getString(column).equals("")) return null; + if (getObject(column) == null || getString(column).isEmpty()) return null; try { return Integer.valueOf(getString(column)); @@ -268,7 +268,7 @@ public Integer getOnoff(String column) public Long getLong(int column) { - if (getObject(column) == null || getString(column).equals("")) return null; + if (getObject(column) == null || getString(column).isEmpty()) return null; try { return Long.valueOf(getString(column)); @@ -282,7 +282,7 @@ public Long getLong(int column) public Long getLong(String column) { - if (getObject(column) == null || getString(column).equals("")) return null; + if (getObject(column) == null || getString(column).isEmpty()) return null; try { return Long.valueOf(getString(column)); @@ -307,23 +307,25 @@ public Long getLong(String column) } + @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "NP_BOOLEAN_RETURN_NULL", justification = "Could be null under some circumstances") public Boolean getBoolean(int column) { - if (getObject(column) == null || getString(column).equals("")) return null; + if (getObject(column) == null || getString(column).isEmpty()) return null; return Boolean.valueOf(getString(column).toLowerCase().equals("true") || getString(column).trim().equals("1") || getString(column).trim().equalsIgnoreCase("on")); } + @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "NP_BOOLEAN_RETURN_NULL", justification = "Could be null under some circumstances") public Boolean getBoolean(String column) { - if (getObject(column) == null || getString(column).equals("")) return null; + if (getObject(column) == null || getString(column).isEmpty()) return null; return Boolean.valueOf(getString(column).toLowerCase().equals("true") || getString(column).trim().equals("1") || getString(column).trim().equalsIgnoreCase("on")); } public Double getDecimal(int column) { - if (getObject(column) == null || getString(column).equals("")) return null; + if (getObject(column) == null || getString(column).isEmpty()) return null; try { return Double.valueOf(getString(column)); @@ -337,7 +339,7 @@ public Double getDecimal(int column) public Double getDecimal(String column) { - if (getObject(column) == null || getString(column).equals("")) return null; + if (getObject(column) == null || getString(column).isEmpty()) return null; try { return new Double(getString(column)); @@ -449,21 +451,23 @@ public String getString(String column) return getObject(column).toString().trim(); } + @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "NP_BOOLEAN_RETURN_NULL", justification = "Could be null under some circumstances") public Boolean getBool(int column) { - if (getObject(column) == null || getString(column).equals("")) return null; + if (getObject(column) == null || getString(column).isEmpty()) return null; return getBoolean(column); } + @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "NP_BOOLEAN_RETURN_NULL", justification = "Could be null under some circumstances") public Boolean getBool(String column) { - if (getObject(column) == null || getString(column).equals("")) return null; + if (getObject(column) == null || getString(column).isEmpty()) return null; return getBoolean(column); } public java.sql.Date getDate(int column) throws ParseException { - if (this.getObject(column) == null || this.getString(column).equals("")) return null; + if (this.getObject(column) == null || this.getString(column).isEmpty()) return null; if (this.getObject(column) instanceof java.sql.Date) return (java.sql.Date) this.getObject(column); if (this.getObject(column) instanceof java.sql.Timestamp) return (java.sql.Date) this.getObject(column); if (this.getObject(column) instanceof java.util.Date) return new java.sql.Date( @@ -500,7 +504,7 @@ public java.sql.Date getDate(int column) throws ParseException public java.sql.Date getDate(String column) throws ParseException { - if (this.getObject(column) == null || this.getString(column).equals("")) return null; + if (this.getObject(column) == null || this.getString(column).isEmpty()) return null; if (this.getObject(column) instanceof java.sql.Date) return (java.sql.Date) this.getObject(column); if (this.getObject(column) instanceof java.sql.Timestamp) return (java.sql.Date) this.getObject(column); if (this.getObject(column) instanceof java.util.Date) return new java.sql.Date( @@ -583,7 +587,7 @@ public File getFile(int column) public Timestamp getTimestamp(String column) throws ParseException { - if (this.getObject(column) == null || this.getString(column).equals("")) return null; + if (this.getObject(column) == null || this.getString(column).isEmpty()) return null; if (this.getObject(column) instanceof java.sql.Timestamp) return (java.sql.Timestamp) this.getObject(column); try { @@ -627,7 +631,7 @@ public Timestamp getTimestamp(String column) throws ParseException public Timestamp getTimestamp(int column) throws ParseException { - if (this.getObject(column) == null || this.getString(column).equals("")) return null; + if (this.getObject(column) == null || this.getString(column).isEmpty()) return null; if (this.getObject(column) instanceof java.sql.Timestamp) return (java.sql.Timestamp) this.getObject(column); try { From e5f1f7524b1a3e0df08ea73d5411f027e6e7c318 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Sat, 3 Nov 2012 17:45:55 +0100 Subject: [PATCH 017/108] fix bug: rely on default encoding, close stream --- src/org/molgenis/util/TextFileUtils.java | 27 ++++++++++++++++-------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/org/molgenis/util/TextFileUtils.java b/src/org/molgenis/util/TextFileUtils.java index c0863ac5a..6527466f4 100644 --- a/src/org/molgenis/util/TextFileUtils.java +++ b/src/org/molgenis/util/TextFileUtils.java @@ -1,10 +1,12 @@ package org.molgenis.util; import java.io.File; -import java.io.FileReader; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStreamReader; import java.io.LineNumberReader; import java.io.RandomAccessFile; +import java.nio.charset.Charset; import org.apache.commons.io.IOUtils; @@ -25,7 +27,8 @@ public class TextFileUtils */ public static int getNumberOfLines(File inFile) throws IOException { - LineNumberReader lnr = new LineNumberReader(new FileReader(inFile)); + LineNumberReader lnr = new LineNumberReader(new InputStreamReader(new FileInputStream(inFile), + Charset.forName("UTF-8"))); try { lnr.skip(Long.MAX_VALUE); @@ -49,16 +52,22 @@ public static int getNumberOfLines(File inFile) throws IOException public static boolean fileEndsWithNewlineChar(File inFile) throws Exception { RandomAccessFile raf = new RandomAccessFile(inFile, "r"); - raf.seek(raf.length() - 1); - char c = (char) raf.readByte(); - raf.close(); - if (c == '\n' || c == '\r') + try { - return true; + raf.seek(raf.length() - 1); + char c = (char) raf.readByte(); + if (c == '\n' || c == '\r') + { + return true; + } + else + { + return false; + } } - else + finally { - return false; + raf.close(); } } From 1e29e554e66cd217a556a13d4e0b04ff65f45c93 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Sat, 3 Nov 2012 17:47:51 +0100 Subject: [PATCH 018/108] remove template doc header, remove dead local variables --- src/org/molgenis/util/trityper/reader/BaseAnnot.java | 4 ---- src/org/molgenis/util/trityper/reader/ChrAnnotation.java | 4 ---- src/org/molgenis/util/trityper/reader/Gpio.java | 4 ---- src/org/molgenis/util/trityper/reader/SNP.java | 4 ---- src/org/molgenis/util/trityper/reader/SNPLoader.java | 4 ---- src/org/molgenis/util/trityper/reader/TextFile.java | 4 ---- .../molgenis/util/trityper/reader/TriTyperGenotypeData.java | 4 ---- src/org/molgenis/util/trityper/reader/TriTyperReader.java | 6 ------ 8 files changed, 34 deletions(-) diff --git a/src/org/molgenis/util/trityper/reader/BaseAnnot.java b/src/org/molgenis/util/trityper/reader/BaseAnnot.java index 1fa5fecaa..4d7eace4b 100644 --- a/src/org/molgenis/util/trityper/reader/BaseAnnot.java +++ b/src/org/molgenis/util/trityper/reader/BaseAnnot.java @@ -1,7 +1,3 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ package org.molgenis.util.trityper.reader; /** diff --git a/src/org/molgenis/util/trityper/reader/ChrAnnotation.java b/src/org/molgenis/util/trityper/reader/ChrAnnotation.java index 31e19a22c..471f52a67 100644 --- a/src/org/molgenis/util/trityper/reader/ChrAnnotation.java +++ b/src/org/molgenis/util/trityper/reader/ChrAnnotation.java @@ -1,7 +1,3 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ package org.molgenis.util.trityper.reader; /** diff --git a/src/org/molgenis/util/trityper/reader/Gpio.java b/src/org/molgenis/util/trityper/reader/Gpio.java index 7258a07d3..48eb65ba8 100644 --- a/src/org/molgenis/util/trityper/reader/Gpio.java +++ b/src/org/molgenis/util/trityper/reader/Gpio.java @@ -1,7 +1,3 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ package org.molgenis.util.trityper.reader; import java.io.File; diff --git a/src/org/molgenis/util/trityper/reader/SNP.java b/src/org/molgenis/util/trityper/reader/SNP.java index 10019b09b..968980bb4 100644 --- a/src/org/molgenis/util/trityper/reader/SNP.java +++ b/src/org/molgenis/util/trityper/reader/SNP.java @@ -1,7 +1,3 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ package org.molgenis.util.trityper.reader; import java.nio.charset.Charset; diff --git a/src/org/molgenis/util/trityper/reader/SNPLoader.java b/src/org/molgenis/util/trityper/reader/SNPLoader.java index 1a40d25e2..6bc165403 100644 --- a/src/org/molgenis/util/trityper/reader/SNPLoader.java +++ b/src/org/molgenis/util/trityper/reader/SNPLoader.java @@ -1,7 +1,3 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ package org.molgenis.util.trityper.reader; import java.io.IOException; diff --git a/src/org/molgenis/util/trityper/reader/TextFile.java b/src/org/molgenis/util/trityper/reader/TextFile.java index 0177aa87f..244236c04 100644 --- a/src/org/molgenis/util/trityper/reader/TextFile.java +++ b/src/org/molgenis/util/trityper/reader/TextFile.java @@ -1,7 +1,3 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ package org.molgenis.util.trityper.reader; import java.io.BufferedReader; diff --git a/src/org/molgenis/util/trityper/reader/TriTyperGenotypeData.java b/src/org/molgenis/util/trityper/reader/TriTyperGenotypeData.java index 0939a8aeb..7dca3e809 100644 --- a/src/org/molgenis/util/trityper/reader/TriTyperGenotypeData.java +++ b/src/org/molgenis/util/trityper/reader/TriTyperGenotypeData.java @@ -1,7 +1,3 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ package org.molgenis.util.trityper.reader; import java.io.IOException; diff --git a/src/org/molgenis/util/trityper/reader/TriTyperReader.java b/src/org/molgenis/util/trityper/reader/TriTyperReader.java index 6a1602c8d..14d7df098 100644 --- a/src/org/molgenis/util/trityper/reader/TriTyperReader.java +++ b/src/org/molgenis/util/trityper/reader/TriTyperReader.java @@ -1,7 +1,3 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ package org.molgenis.util.trityper.reader; import java.io.IOException; @@ -29,8 +25,6 @@ public static void main(String[] args) String[] snps = data.getSNPs(); SNPLoader loader = data.createSNPLoader(); - Integer snpId = data.getSnpToSNPId().get("rs123456789"); - String[] individuals = data.getIndividuals(); for (int i = 0; i < snps.length; i++) From 1c9e64c30575be9d4d45e981faf3bd7a1fb491d8 Mon Sep 17 00:00:00 2001 From: erwinwinder Date: Mon, 5 Nov 2012 12:39:39 +0100 Subject: [PATCH 019/108] Fix import of entity with a reference to other entity in the batch --- src/org/molgenis/generators/csv/CsvReaderGen.java.ftl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/org/molgenis/generators/csv/CsvReaderGen.java.ftl b/src/org/molgenis/generators/csv/CsvReaderGen.java.ftl index 73fd15899..1cb18f099 100644 --- a/src/org/molgenis/generators/csv/CsvReaderGen.java.ftl +++ b/src/org/molgenis/generators/csv/CsvReaderGen.java.ftl @@ -70,7 +70,7 @@ public class ${JavaName(entity)}CsvReader extends CsvToDatabase<${JavaName(entit { //cache for entities of which xrefs couldn't be resolved (e.g. if there is a self-refence) //these entities can be updated with their xrefs in a second round when all entities are in the database - final List<${JavaName(entity)}> ${name(entity)}sMissingRefs = new ArrayList<${JavaName(entity)}>(); + List<${JavaName(entity)}> ${name(entity)}sMissingRefs = new ArrayList<${JavaName(entity)}>(); //cache for objects to be imported from file (in batch) final List<${JavaName(entity)}> ${name(entity)}List = new ArrayList<${JavaName(entity)}>(BATCH_SIZE); @@ -90,6 +90,7 @@ public class ${JavaName(entity)}CsvReader extends CsvToDatabase<${JavaName(entit { //resolve foreign keys and copy those entities that could not be resolved to the missingRefs list ${name(entity)}sMissingRefs.addAll(resolveForeignKeys(db, ${name(entity)}List)); + ${name(entity)}List.removeAll(${name(entity)}sMissingRefs); <#if entity.getXrefLabels()?exists> //update objects in the database using xref_label defined secondary key(s) '${csv(entity.getXrefLabels())}' defined in xref_label @@ -111,7 +112,9 @@ public class ${JavaName(entity)}CsvReader extends CsvToDatabase<${JavaName(entit if(!${name(entity)}List.isEmpty()) { //resolve foreign keys, again keeping track of those entities that could not be solved - ${name(entity)}sMissingRefs.addAll(resolveForeignKeys(db, ${name(entity)}List)); + ${name(entity)}sMissingRefs = resolveForeignKeys(db, ${name(entity)}List); + ${name(entity)}List.removeAll(${name(entity)}sMissingRefs); + <#if entity.getXrefLabels()?exists> //update objects in the database using xref_label defined secondary key(s) '${csv(entity.getXrefLabels())}' defined in xref_label db.update(${name(entity)}List,dbAction<#list entity.getXrefLabels() as label>, "${label}"); @@ -132,9 +135,9 @@ public class ${JavaName(entity)}CsvReader extends CsvToDatabase<${JavaName(entit else { <#if entity.getXrefLabels()?exists> - db.update(${name(entity)}sMissingRefs,DatabaseAction.UPDATE<#list entity.getXrefLabels() as label>, "${label}"); + db.update(${name(entity)}sMissingRefs,dbAction<#list entity.getXrefLabels() as label>, "${label}"); <#else> - db.update(${name(entity)}sMissingRefs,DatabaseAction.UPDATE<#list entity.getAllKeys()[0].fields as field>, "${field.name}"); + db.update(${name(entity)}sMissingRefs,dbAction<#list entity.getAllKeys()[0].fields as field>, "${field.name}"); //output count From b72d4dc4b51e4675969afac50b2b3c96970e34af Mon Sep 17 00:00:00 2001 From: erwinwinder Date: Tue, 6 Nov 2012 12:14:49 +0100 Subject: [PATCH 020/108] Fix for multiple entity dependencies in import CSV --- .../generators/csv/CsvReaderGen.java.ftl | 51 ++++++++++++------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/src/org/molgenis/generators/csv/CsvReaderGen.java.ftl b/src/org/molgenis/generators/csv/CsvReaderGen.java.ftl index 1cb18f099..d84372108 100644 --- a/src/org/molgenis/generators/csv/CsvReaderGen.java.ftl +++ b/src/org/molgenis/generators/csv/CsvReaderGen.java.ftl @@ -28,6 +28,7 @@ import java.util.TreeMap; <#break> +import org.apache.commons.collections.CollectionUtils; import org.apache.log4j.Logger; import org.molgenis.framework.db.CsvToDatabase; import org.molgenis.framework.db.Database; @@ -111,8 +112,10 @@ public class ${JavaName(entity)}CsvReader extends CsvToDatabase<${JavaName(entit //add remaining elements to the database if(!${name(entity)}List.isEmpty()) { + total.set(total.get() + ${name(entity)}List.size()); + //resolve foreign keys, again keeping track of those entities that could not be solved - ${name(entity)}sMissingRefs = resolveForeignKeys(db, ${name(entity)}List); + ${name(entity)}sMissingRefs.addAll(resolveForeignKeys(db, ${name(entity)}List)); ${name(entity)}List.removeAll(${name(entity)}sMissingRefs); <#if entity.getXrefLabels()?exists> @@ -124,28 +127,38 @@ public class ${JavaName(entity)}CsvReader extends CsvToDatabase<${JavaName(entit } - //second import round, try to resolve FK's for entities again as they might have referred to entities in the imported list - List<${JavaName(entity)}> ${name(entity)}sStillMissingRefs = resolveForeignKeys(db, ${name(entity)}sMissingRefs); - - //if there are still missing references, throw error and rollback - if(${name(entity)}sStillMissingRefs.size() > 0){ - throw new Exception("Import of '${JavaName(entity)}' objects failed: attempting to resolve in-list references, but there are still ${JavaName(entity)}s referring to ${JavaName(entity)}s that are neither in the database nor in the list of to-be imported ${JavaName(entity)}s. (the first one being: "+${name(entity)}sStillMissingRefs.get(0)+")"); - } - //else update the entities in the database with the found references and return total - else - { + //Try to resolve FK's for entities until all are resolved or we have more then 100 iterations + List<${JavaName(entity)}> ${name(entity)}s = new ArrayList<${JavaName(entity)}>(${name(entity)}sMissingRefs); + + int iterationCount = 0; + + do + { + ${name(entity)}sMissingRefs = resolveForeignKeys(db, ${name(entity)}sMissingRefs); + @SuppressWarnings("unchecked") + List<${JavaName(entity)}> resolvable${name(entity)}s = new ArrayList<${JavaName(entity)}>(CollectionUtils.disjunction(${name(entity)}s, + ${name(entity)}sMissingRefs)); + ${name(entity)}s.removeAll(resolvable${name(entity)}s); + <#if entity.getXrefLabels()?exists> - db.update(${name(entity)}sMissingRefs,dbAction<#list entity.getXrefLabels() as label>, "${label}"); + db.update(resolvable${name(entity)}s,dbAction<#list entity.getXrefLabels() as label>, "${label}"); <#else> - db.update(${name(entity)}sMissingRefs,dbAction<#list entity.getAllKeys()[0].fields as field>, "${field.name}"); + db.update(resolvable${name(entity)}s,dbAction<#list entity.getAllKeys()[0].fields as field>, "${field.name}"); - - //output count - total.set(total.get() + ${name(entity)}List.size()); - logger.info("imported "+total.get()+" ${name(entity)} from CSV"); - - return total.get(); + + if (iterationCount++ > 100) + { + throw new Exception( + "Import of 'Individual' objects failed: attempting to resolve in-list references," + + "but after 100 iterations there are still Individuals referring to Individuals that are neither in the database nor in the list of to-be imported Individuals." + + "Maybe there is a cyclic reference somewhere ?"); + } } + while (${name(entity)}sMissingRefs.size() > 0); + + logger.info("imported " + total.get() + " ${name(entity)} from CSV"); + + return total.get(); } /** From 806d22e85b7fbf269c391e8665732cfcb5bf257f Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Tue, 6 Nov 2012 21:55:07 +0100 Subject: [PATCH 021/108] add generated java src folder --- .classpath | 1 + 1 file changed, 1 insertion(+) diff --git a/.classpath b/.classpath index 5609d2fd0..76bb685d1 100644 --- a/.classpath +++ b/.classpath @@ -3,6 +3,7 @@ + From 298936f6acaa9b83a59b221c85e70e27ea9c5a76 Mon Sep 17 00:00:00 2001 From: erwinwinder Date: Wed, 7 Nov 2012 09:16:55 +0100 Subject: [PATCH 022/108] Replaced literal entityname with template --- src/org/molgenis/generators/csv/CsvReaderGen.java.ftl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/molgenis/generators/csv/CsvReaderGen.java.ftl b/src/org/molgenis/generators/csv/CsvReaderGen.java.ftl index d84372108..84ff03853 100644 --- a/src/org/molgenis/generators/csv/CsvReaderGen.java.ftl +++ b/src/org/molgenis/generators/csv/CsvReaderGen.java.ftl @@ -149,8 +149,8 @@ public class ${JavaName(entity)}CsvReader extends CsvToDatabase<${JavaName(entit if (iterationCount++ > 100) { throw new Exception( - "Import of 'Individual' objects failed: attempting to resolve in-list references," - + "but after 100 iterations there are still Individuals referring to Individuals that are neither in the database nor in the list of to-be imported Individuals." + "Import of '${name(entity)}' objects failed: attempting to resolve in-list references," + + "but after 100 iterations there are still ${name(entity)}s referring to Individuals that are neither in the database nor in the list of to-be imported ${name(entity)}s." + "Maybe there is a cyclic reference somewhere ?"); } } From b99c52f77a3925f5ffae3fa01f55a6b071c43663 Mon Sep 17 00:00:00 2001 From: rw42 Date: Wed, 7 Nov 2012 13:04:37 +0100 Subject: [PATCH 023/108] Bugfix: Removed hardcoded "autoConnect" parameter since it might interfere with other options. --- .../generators/db/PersistenceGen.xml.ftl | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/org/molgenis/generators/db/PersistenceGen.xml.ftl b/src/org/molgenis/generators/db/PersistenceGen.xml.ftl index 7664967ce..f660e1b2d 100644 --- a/src/org/molgenis/generators/db/PersistenceGen.xml.ftl +++ b/src/org/molgenis/generators/db/PersistenceGen.xml.ftl @@ -11,11 +11,17 @@ - - - - + + + + + + + + @@ -27,7 +33,10 @@ + + From 5fbe34e762e803f0420fb4b81caa27cdd82238e9 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 7 Nov 2012 14:15:26 +0100 Subject: [PATCH 025/108] add generated test source dir to path --- build.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.xml b/build.xml index 20ff85ea5..bf839c5b7 100644 --- a/build.xml +++ b/build.xml @@ -67,9 +67,10 @@ - + + From f6f3fd6851b71ae30d544fab873035b61c728835 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 7 Nov 2012 14:18:03 +0100 Subject: [PATCH 026/108] add generate method with other signature --- src/org/molgenis/generators/Generator.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/org/molgenis/generators/Generator.java b/src/org/molgenis/generators/Generator.java index 841148504..da0c3d668 100644 --- a/src/org/molgenis/generators/Generator.java +++ b/src/org/molgenis/generators/Generator.java @@ -23,6 +23,12 @@ public abstract class Generator public abstract void generate(Model model, MolgenisOptions options) throws Exception; + // TODO make abstract (not practical to do at the moment) + public void generate(Model model, MolgenisOptions options, String generatedPath) throws Exception + { + throw new UnsupportedOperationException(); + } + /** * Create a template for the generators to use. * @@ -146,8 +152,5 @@ public String getExtension() return ".java"; } - // private static transient final Logger logger = - // Logger.getLogger(Generator.class.getSimpleName()); - public abstract String getDescription(); } From 229cea8249fbbcc4bb7c544f091724605268d6e8 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 7 Nov 2012 14:19:16 +0100 Subject: [PATCH 027/108] remove date from documentation (keep generated file the same if generator didn't change) --- src/org/molgenis/generators/csv/CsvExportGen.java.ftl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/molgenis/generators/csv/CsvExportGen.java.ftl b/src/org/molgenis/generators/csv/CsvExportGen.java.ftl index 5a3b56175..2ab442fac 100644 --- a/src/org/molgenis/generators/csv/CsvExportGen.java.ftl +++ b/src/org/molgenis/generators/csv/CsvExportGen.java.ftl @@ -6,7 +6,7 @@ <#-- START OF THE OUTPUT ##--> <#-- ##--> <#--#####################################################################--> -/* Date: ${date} +/* * * generator: ${generator} ${version} * From 2833d3023ef22ff3cf93b87a74bad7c76d433590 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 7 Nov 2012 14:20:37 +0100 Subject: [PATCH 028/108] use new generate method signature (required for testing) --- .../molgenis/generators/csv/CsvExportGen.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/org/molgenis/generators/csv/CsvExportGen.java b/src/org/molgenis/generators/csv/CsvExportGen.java index 68ad1c698..cc00766d6 100644 --- a/src/org/molgenis/generators/csv/CsvExportGen.java +++ b/src/org/molgenis/generators/csv/CsvExportGen.java @@ -20,7 +20,7 @@ public class CsvExportGen extends MySqlCreateClassPerTableGen { - public static transient final Logger logger = Logger.getLogger(CsvExportGen.class); + public static transient final Logger LOG = Logger.getLogger(CsvExportGen.class); @Override public String getDescription() @@ -54,24 +54,25 @@ public void generate(Model model, MolgenisOptions options) throws Exception logger.info("generated " + target); } - public void generate(Model model, MolgenisOptions options, OutputStream os) throws Exception + public void generate(Model model, MolgenisOptions options, String target) throws Exception { - Template template = createTemplate("/" + this.getClass().getSimpleName() + ".java.ftl"); + Template template = createTemplate(this.getClass().getSimpleName() + ".java.ftl"); Map templateArgs = createTemplateArguments(options); List entityList = model.getEntities(); entityList = MolgenisModel.sortEntitiesByDependency(entityList, model); // side - // effect? - File target = new File("/git/molgenis/generated/java/org/molgenis/generators/csv/CsvExport.java"); - boolean created = target.getParentFile().mkdirs(); - if (!created && !target.getParentFile().exists()) + // effect? + + File generatedJavaFile = new File(target); + boolean created = generatedJavaFile.getParentFile().mkdirs(); + if (!created && !generatedJavaFile.getParentFile().exists()) { - throw new IOException("could not create " + target.getParentFile()); + throw new IOException("could not create " + generatedJavaFile.getParentFile()); } templateArgs.put("model", model); templateArgs.put("entities", entityList); - templateArgs.put("package", "org.molgenis.generators.csv"); + templateArgs.put("package", this.getClass().getPackage().getName()); OutputStream targetOut = new FileOutputStream(target); template.process(templateArgs, new OutputStreamWriter(targetOut, Charset.forName("UTF-8"))); targetOut.close(); From 8cde85536b940ee8030fec388287887b4eb2e890 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 7 Nov 2012 14:22:53 +0100 Subject: [PATCH 029/108] add generated code testing + example --- generated/javaorg/molgenis/test/Autoid.java | 23 + generated/javaorg/molgenis/test/Category.java | 404 ++++++++++++ .../javaorg/molgenis/test/Characteristic.java | 578 ++++++++++++++++++ generated/javaorg/molgenis/test/Feature.java | 488 +++++++++++++++ .../javaorg/molgenis/test/Identifiable.java | 27 + .../molgenis/generators/csv/CsvExport.java | 337 ++++++++++ .../test/java/org/molgenis/model/Autoid.java | 23 + .../java/org/molgenis/model/Category.java | 404 ++++++++++++ .../org/molgenis/model/Characteristic.java | 578 ++++++++++++++++++ .../test/java/org/molgenis/model/Feature.java | 488 +++++++++++++++ .../java/org/molgenis/model/Identifiable.java | 27 + test/java/org/molgenis/GeneratorTestCase.java | 147 +++++ .../generators/csv/CsvExportTest.java | 46 ++ 13 files changed, 3570 insertions(+) create mode 100644 generated/javaorg/molgenis/test/Autoid.java create mode 100644 generated/javaorg/molgenis/test/Category.java create mode 100644 generated/javaorg/molgenis/test/Characteristic.java create mode 100644 generated/javaorg/molgenis/test/Feature.java create mode 100644 generated/javaorg/molgenis/test/Identifiable.java create mode 100644 generated/test/java/org/molgenis/generators/csv/CsvExport.java create mode 100644 generated/test/java/org/molgenis/model/Autoid.java create mode 100644 generated/test/java/org/molgenis/model/Category.java create mode 100644 generated/test/java/org/molgenis/model/Characteristic.java create mode 100644 generated/test/java/org/molgenis/model/Feature.java create mode 100644 generated/test/java/org/molgenis/model/Identifiable.java create mode 100644 test/java/org/molgenis/GeneratorTestCase.java create mode 100644 test/java/org/molgenis/generators/csv/CsvExportTest.java diff --git a/generated/javaorg/molgenis/test/Autoid.java b/generated/javaorg/molgenis/test/Autoid.java new file mode 100644 index 000000000..484e061cc --- /dev/null +++ b/generated/javaorg/molgenis/test/Autoid.java @@ -0,0 +1,23 @@ + +/* File: org.molgenis/model/Autoid.java + * Copyright: GBIC 2000-2012, all rights reserved + * Date: November 6, 2012 + * Generator: org.molgenis.generators.DataTypeGen 4.0.0-testing + * + * THIS FILE HAS BEEN GENERATED, PLEASE DO NOT EDIT! + */ + + +package org.molgenis.test; + +/** + * Autoid: . + * @version November 6, 2012 + * @author MOLGENIS generator + */ +public interface Autoid extends org.molgenis.util.Entity +{ + public Integer getId(); + public void setId(Integer id); +} + diff --git a/generated/javaorg/molgenis/test/Category.java b/generated/javaorg/molgenis/test/Category.java new file mode 100644 index 000000000..09b65a3a7 --- /dev/null +++ b/generated/javaorg/molgenis/test/Category.java @@ -0,0 +1,404 @@ + +/* File: org.molgenis/model/Category.java + * Copyright: GBIC 2000-2012, all rights reserved + * Date: November 6, 2012 + * Generator: org.molgenis.generators.DataTypeGen 4.0.0-testing + * + * THIS FILE HAS BEEN GENERATED, PLEASE DO NOT EDIT! + */ + + +package org.molgenis.test; + +/** + * Category: . + * @version November 6, 2012 + * @author MOLGENIS generator + */ +@javax.persistence.Entity +//@org.hibernate.search.annotations.Indexed +@javax.persistence.Table(name = "Category" +) + + +@javax.xml.bind.annotation.XmlAccessorType(javax.xml.bind.annotation.XmlAccessType.FIELD) +//@EntityListeners({org.molgenis.test.db.CategoryEntityListener.class}) +public class Category extends org.molgenis.util.AbstractEntity implements org.molgenis.test.Autoid +{ + // fieldname constants + public final static String ID = "id"; + public final static String FEATURE = "feature"; + public final static String FEATURE_IDENTIFIER = "feature_Identifier"; + + //static methods + /** + * Shorthand for db.query(Category.class). + */ + public static org.molgenis.framework.db.Query query(org.molgenis.framework.db.Database db) + { + return db.query(Category.class); + } + + /** + * Shorthand for db.find(Category.class, org.molgenis.framework.db.QueryRule ... rules). + */ + public static java.util.List find(org.molgenis.framework.db.Database db, org.molgenis.framework.db.QueryRule ... rules) throws org.molgenis.framework.db.DatabaseException + { + return db.find(Category.class, rules); + } + + /** + * + */ + public static Category findById(org.molgenis.framework.db.Database db, Integer id) throws org.molgenis.framework.db.DatabaseException + { + org.molgenis.framework.db.Query q = db.query(Category.class); + q.eq(Category.ID, id); + java.util.List result = q.find(); + if(result.size()>0) return result.get(0); + else return null; + } + + + // member variables (including setters.getters for interface) + + + //id[type=int] + @javax.persistence.Id @javax.persistence.GeneratedValue(strategy = javax.persistence.GenerationType.AUTO) + @javax.persistence.Column(name="id", nullable=false) + @javax.xml.bind.annotation.XmlElement(name="id") + + //@javax.validation.constraints.NotNull + private Integer id = null; + + + //feature[type=xref] + @javax.persistence.ManyToOne(fetch=javax.persistence.FetchType.LAZY /*cascade={javax.persistence.CascadeType.MERGE, javax.persistence.CascadeType.PERSIST, javax.persistence.CascadeType.REFRESH}*/) + @javax.persistence.JoinColumn(name="feature", nullable=false) + + + + @javax.validation.constraints.NotNull + private org.molgenis.test.Feature feature = null; + @javax.persistence.Transient + private Integer feature_id = null; + @javax.persistence.Transient + private String feature_Identifier = null; + + //constructors + public Category() + { + + } + + //getters and setters + /** + * Get the id. + * @return id. + */ + public Integer getId() + { + return this.id; + } + + + /** + * Set the id. + * @param id + */ + public void setId( Integer id) + { + this.id = id; + } + + + + /** + * Get the feature. + * @return feature. + */ + public org.molgenis.test.Feature getFeature() + { + return this.feature; + } + + @Deprecated + public org.molgenis.test.Feature getFeature(org.molgenis.framework.db.Database db) + { + throw new UnsupportedOperationException(); + } + + /** + * Set the feature. + * @param feature + */ + public void setFeature( org.molgenis.test.Feature feature) + { + + this.feature = feature; + } + + + + /** + * Set foreign key for field feature. + * This will erase any foreign key objects currently set. + * FIXME: can we autoload the new object? + */ + public void setFeature_Id(Integer feature_id) + { + this.feature_id = feature_id; + } + + public void setFeature(Integer feature_id) + { + this.feature_id = feature_id; + } + + public Integer getFeature_Id() + { + + if(feature != null) + { + return feature.getId(); + } + else + { + return feature_id; + } + } + + /** + * Get a pretty label Identifier for cross reference Feature to Feature.Id. + */ + public String getFeature_Identifier() + { + //FIXME should we auto-load based on getFeature()? + if(feature != null) { + return feature.getIdentifier(); + } else { + return feature_Identifier; + } + } + + /** + * Set a pretty label for cross reference Feature to Feature.Id. + * Implies setFeature(null) until save + */ + public void setFeature_Identifier(String feature_Identifier) + { + this.feature_Identifier = feature_Identifier; + } + + + + + /** + * Generic getter. Get the property by using the name. + */ + public Object get(String name) + { + name = name.toLowerCase(); + if (name.toLowerCase().equals("id")) + return getId(); + if (name.toLowerCase().equals("feature")) + return getFeature(); + if(name.toLowerCase().equals("feature_id")) + return getFeature_Id(); + if(name.toLowerCase().equals("feature_identifier")) + return getFeature_Identifier(); + return ""; + } + + public void validate() throws org.molgenis.framework.db.DatabaseException + { + if(this.getId() == null) throw new org.molgenis.framework.db.DatabaseException("required field id is null"); + if(this.getFeature() == null) throw new org.molgenis.framework.db.DatabaseException("required field feature is null"); + } + + + + //@Implements + public void set( org.molgenis.util.Tuple tuple, boolean strict ) throws Exception + { + //optimization :-( + if(tuple instanceof org.molgenis.util.ResultSetTuple) + { + //set Id + this.setId(tuple.getInt("id")); + //set Feature + this.setFeature(tuple.getInt("feature")); + //set label Identifier for xref field Feature + this.setFeature_Identifier(tuple.getString("feature_Identifier")); + } + else if(tuple != null) + { + //set Id + if( strict || tuple.getInt("id") != null) this.setId(tuple.getInt("id")); + if( tuple.getInt("Category_id") != null) this.setId(tuple.getInt("Category_id")); + //set Feature + if( strict || tuple.getInt("feature_id") != null) this.setFeature(tuple.getInt("feature_id")); + if( tuple.getInt("Category_feature_id") != null) this.setFeature(tuple.getInt("Category_feature_id")); + //alias of xref + if( tuple.getObject("feature") != null) this.setFeature(tuple.getInt("feature")); + if( tuple.getObject("Category_feature") != null) this.setFeature(tuple.getInt("Category_feature")); + //set label for field Feature + if( strict || tuple.getObject("feature_Identifier") != null) this.setFeature_Identifier(tuple.getString("feature_Identifier")); + if( tuple.getObject("Category_feature_Identifier") != null ) this.setFeature_Identifier(tuple.getString("Category_feature_Identifier")); + } + //org.apache.log4j.Logger.getLogger("test").debug("set "+this); + } + + + + + + @Override + public String toString() + { + return this.toString(false); + } + + public String toString(boolean verbose) + { + String result = "Category("; + result+= "id='" + getId()+"' "; + result+= " feature_id='" + getFeature_Id()+"' "; + result+= " feature_identifier='" + getFeature_Identifier()+"' "; + result += ");"; + return result; + + } + + /** + * Get the names of all public properties of Category. + */ + public java.util.Vector getFields(boolean skipAutoIds) + { + java.util.Vector fields = new java.util.Vector(); + if(!skipAutoIds) + { + fields.add("id"); + } + { + fields.add("feature_id"); + } + fields.add("feature_identifier"); + return fields; + } + + public java.util.Vector getFields() + { + return getFields(false); + } + + @Override + public String getIdField() + { + return "id"; + } + + + + @Override + public java.util.List getLabelFields() + { + java.util.List result = new java.util.ArrayList(); + result.add("id"); + return result; + } + + @Deprecated + public String getFields(String sep) + { + return ("" + + "id" +sep + + "feature" + ); + } + + @Override + public Object getIdValue() + { + return get(getIdField()); + } + + + public String getXrefIdFieldName(String fieldName) { + if (fieldName.equalsIgnoreCase("feature")) { + return "id"; + } + + return null; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { return false; } + if (obj == this) { return true; } + if (obj.getClass() != getClass()) { + return false; + } + Category rhs = (Category) obj; + return new org.apache.commons.lang.builder.EqualsBuilder() + .isEquals(); + } + + @Override + public int hashCode() { + int firstNumber = this.getClass().getName().hashCode(); + int secondNumber = this.getClass().getSimpleName().hashCode(); + if(firstNumber % 2 == 0) { + firstNumber += 1; + } + if(secondNumber % 2 == 0) { + secondNumber += 1; + } + + return new org.apache.commons.lang.builder.HashCodeBuilder(firstNumber, secondNumber) + .toHashCode(); + } + + + + @Deprecated + public String getValues(String sep) + { + java.io.StringWriter out = new java.io.StringWriter(); + { + Object valueO = getId(); + String valueS; + if (valueO != null) + valueS = valueO.toString(); + else + valueS = ""; + valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); + valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); + out.write(valueS+sep); + } + { + Object valueO = getFeature(); + String valueS; + if (valueO != null) + valueS = valueO.toString(); + else + valueS = ""; + valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); + valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); + out.write(valueS); + } + return out.toString(); + } + + @Override + public Category create(org.molgenis.util.Tuple tuple) throws Exception + { + Category e = new Category(); + e.set(tuple); + return e; + } + + + +} + diff --git a/generated/javaorg/molgenis/test/Characteristic.java b/generated/javaorg/molgenis/test/Characteristic.java new file mode 100644 index 000000000..7edd52995 --- /dev/null +++ b/generated/javaorg/molgenis/test/Characteristic.java @@ -0,0 +1,578 @@ + +/* File: org.molgenis/model/Characteristic.java + * Copyright: GBIC 2000-2012, all rights reserved + * Date: November 6, 2012 + * Generator: org.molgenis.generators.DataTypeGen 4.0.0-testing + * + * THIS FILE HAS BEEN GENERATED, PLEASE DO NOT EDIT! + */ + + +package org.molgenis.test; + +/** + * Characteristic: . + * @version November 6, 2012 + * @author MOLGENIS generator + */ +@javax.persistence.Entity +//@org.hibernate.search.annotations.Indexed +@javax.persistence.Table(name = "Characteristic", uniqueConstraints={ @javax.persistence.UniqueConstraint( columnNames={ "Identifier" }), @javax.persistence.UniqueConstraint( columnNames={ "Name", "Identifier" } ) } +) + + +@javax.persistence.Inheritance(strategy=javax.persistence.InheritanceType.JOINED) +@javax.persistence.DiscriminatorColumn(name="DType", discriminatorType=javax.persistence.DiscriminatorType.STRING) +@javax.xml.bind.annotation.XmlAccessorType(javax.xml.bind.annotation.XmlAccessType.FIELD) +//@EntityListeners({org.molgenis.test.db.CharacteristicEntityListener.class}) +public class Characteristic extends org.molgenis.util.AbstractEntity implements org.molgenis.test.Identifiable +{ + // fieldname constants + public final static String ID = "id"; + public final static String IDENTIFIER = "Identifier"; + public final static String NAME = "Name"; + public final static String __TYPE = "__Type"; + public final static String DESCRIPTION = "description"; + + //static methods + /** + * Shorthand for db.query(Characteristic.class). + */ + public static org.molgenis.framework.db.Query query(org.molgenis.framework.db.Database db) + { + return db.query(Characteristic.class); + } + + /** + * Shorthand for db.find(Characteristic.class, org.molgenis.framework.db.QueryRule ... rules). + */ + public static java.util.List find(org.molgenis.framework.db.Database db, org.molgenis.framework.db.QueryRule ... rules) throws org.molgenis.framework.db.DatabaseException + { + return db.find(Characteristic.class, rules); + } + + /** + * + */ + public static Characteristic findById(org.molgenis.framework.db.Database db, Integer id) throws org.molgenis.framework.db.DatabaseException + { + org.molgenis.framework.db.Query q = db.query(Characteristic.class); + q.eq(Characteristic.ID, id); + java.util.List result = q.find(); + if(result.size()>0) return result.get(0); + else return null; + } + + /** + * + */ + public static Characteristic findByIdentifier(org.molgenis.framework.db.Database db, String identifier) throws org.molgenis.framework.db.DatabaseException + { + org.molgenis.framework.db.Query q = db.query(Characteristic.class); + q.eq(Characteristic.IDENTIFIER, identifier); + java.util.List result = q.find(); + if(result.size()>0) return result.get(0); + else return null; + } + + /** + * + */ + public static Characteristic findByNameIdentifier(org.molgenis.framework.db.Database db, String name, String identifier) throws org.molgenis.framework.db.DatabaseException + { + org.molgenis.framework.db.Query q = db.query(Characteristic.class); + q.eq(Characteristic.NAME, name);q.eq(Characteristic.IDENTIFIER, identifier); + java.util.List result = q.find(); + if(result.size()>0) return result.get(0); + else return null; + } + + + // member variables (including setters.getters for interface) + + + //id[type=int] + @javax.persistence.Id @javax.persistence.GeneratedValue(strategy = javax.persistence.GenerationType.AUTO) + @javax.persistence.Column(name="id", nullable=false) + @javax.xml.bind.annotation.XmlElement(name="id") + + //@javax.validation.constraints.NotNull + private Integer id = null; + + + //Identifier[type=string] +// @org.hibernate.search.annotations.Field(index=org.hibernate.search.annotations.Index.TOKENIZED, store=org.hibernate.search.annotations.Store.NO) + @javax.persistence.Column(name="Identifier", nullable=false) + @javax.xml.bind.annotation.XmlElement(name="identifier") + + + + @javax.validation.constraints.NotNull + private String identifier = null; + + + //assign name[type=string] +// @org.hibernate.search.annotations.Field(index=org.hibernate.search.annotations.Index.TOKENIZED, store=org.hibernate.search.annotations.Store.NO) + @javax.persistence.Column(name="Name", nullable=false) + @javax.xml.bind.annotation.XmlElement(name="name") + + + + @javax.validation.constraints.NotNull + private String name = null; + + + //Subtypes have to be set to allow searching[type=enum] + @javax.persistence.Column(name="DType", nullable=false) + @javax.xml.bind.annotation.XmlElement(name="__Type") + + + + @javax.validation.constraints.NotNull + private String __Type = null; + @javax.persistence.Transient + private String __Type_label = null; + @javax.persistence.Transient + private java.util.List __Type_options = new java.util.ArrayList(); + + + //description[type=text] +// @javax.persistence.Lob() + @javax.persistence.Column(name="description", length=16777216) + + + + private String description = null; + + //constructors + public Characteristic() + { + //set the type for a new instance + set__Type(this.getClass().getSimpleName()); + + //options for enum __Type + __Type_options.add(new org.molgenis.util.ValueLabel("Characteristic","Characteristic")); + __Type_options.add(new org.molgenis.util.ValueLabel("Feature","Feature")); + } + + //getters and setters + /** + * Get the id. + * @return id. + */ + public Integer getId() + { + return this.id; + } + + + /** + * Set the id. + * @param id + */ + public void setId( Integer id) + { + this.id = id; + } + + + + /** + * Get the Identifier. + * @return identifier. + */ + public String getIdentifier() + { + return this.identifier; + } + + @Deprecated + public String getIdentifier(org.molgenis.framework.db.Database db) + { + throw new UnsupportedOperationException(); + } + + /** + * Set the Identifier. + * @param identifier + */ + public void setIdentifier( String identifier) + { + + this.identifier = identifier; + } + + + + /** + * Get the assign name. + * @return name. + */ + public String getName() + { + return this.name; + } + + @Deprecated + public String getName(org.molgenis.framework.db.Database db) + { + throw new UnsupportedOperationException(); + } + + /** + * Set the assign name. + * @param name + */ + public void setName( String name) + { + + this.name = name; + } + + + + /** + * Get the Subtypes have to be set to allow searching. + * @return __Type. + */ + public String get__Type() + { + return this.__Type; + } + + @Deprecated + public String get__Type(org.molgenis.framework.db.Database db) + { + throw new UnsupportedOperationException(); + } + + /** + * Set the Subtypes have to be set to allow searching. + * @param __Type + */ + public void set__Type( String __Type) + { + + this.__Type = __Type; + } + + + /** + * Get tha label for enum __Type. + */ + public String get__TypeLabel() + { + return this.__Type_label; + } + + /** + * __Type is enum. This method returns all available enum options. + */ + public java.util.List get__TypeOptions() + { + return __Type_options; + } + + + /** + * Get the description. + * @return description. + */ + public String getDescription() + { + return this.description; + } + + @Deprecated + public String getDescription(org.molgenis.framework.db.Database db) + { + throw new UnsupportedOperationException(); + } + + /** + * Set the description. + * @param description + */ + public void setDescription( String description) + { + + this.description = description; + } + + + + + /** + * Generic getter. Get the property by using the name. + */ + public Object get(String name) + { + name = name.toLowerCase(); + if (name.toLowerCase().equals("id")) + return getId(); + if (name.toLowerCase().equals("identifier")) + return getIdentifier(); + if (name.toLowerCase().equals("name")) + return getName(); + if (name.toLowerCase().equals("__type")) + return get__Type(); + if(name.toLowerCase().equals("__type_label")) + return get__TypeLabel(); + if (name.toLowerCase().equals("description")) + return getDescription(); + return ""; + } + + public void validate() throws org.molgenis.framework.db.DatabaseException + { + if(this.getId() == null) throw new org.molgenis.framework.db.DatabaseException("required field id is null"); + if(this.getIdentifier() == null) throw new org.molgenis.framework.db.DatabaseException("required field identifier is null"); + if(this.getName() == null) throw new org.molgenis.framework.db.DatabaseException("required field name is null"); + if(this.get__Type() == null) throw new org.molgenis.framework.db.DatabaseException("required field __Type is null"); + } + + + + //@Implements + public void set( org.molgenis.util.Tuple tuple, boolean strict ) throws Exception + { + //optimization :-( + if(tuple instanceof org.molgenis.util.ResultSetTuple) + { + //set Id + this.setId(tuple.getInt("id")); + //set Identifier + this.setIdentifier(tuple.getString("Identifier")); + //set Name + this.setName(tuple.getString("Name")); + //set __Type + this.set__Type(tuple.getString("__Type")); + //set Description + this.setDescription(tuple.getString("description")); + } + else if(tuple != null) + { + //set Id + if( strict || tuple.getInt("id") != null) this.setId(tuple.getInt("id")); + if( tuple.getInt("Characteristic_id") != null) this.setId(tuple.getInt("Characteristic_id")); + //set Identifier + if( strict || tuple.getString("Identifier") != null) this.setIdentifier(tuple.getString("Identifier")); + if( tuple.getString("Characteristic_Identifier") != null) this.setIdentifier(tuple.getString("Characteristic_Identifier")); + //set Name + if( strict || tuple.getString("Name") != null) this.setName(tuple.getString("Name")); + if( tuple.getString("Characteristic_Name") != null) this.setName(tuple.getString("Characteristic_Name")); + //set __Type + if( strict || tuple.getString("__Type") != null) this.set__Type(tuple.getString("__Type")); + if( tuple.getString("Characteristic___Type") != null) this.set__Type(tuple.getString("Characteristic___Type")); + //set Description + if( strict || tuple.getString("description") != null) this.setDescription(tuple.getString("description")); + if( tuple.getString("Characteristic_description") != null) this.setDescription(tuple.getString("Characteristic_description")); + } + //org.apache.log4j.Logger.getLogger("test").debug("set "+this); + } + + + + + + @Override + public String toString() + { + return this.toString(false); + } + + public String toString(boolean verbose) + { + String result = "Characteristic("; + result+= "id='" + getId()+"' "; + result+= "identifier='" + getIdentifier()+"' "; + result+= "name='" + getName()+"' "; + result+= "__Type='" + get__Type()+"' "; + result+= "description='" + getDescription()+"'"; + result += ");"; + return result; + + } + + /** + * Get the names of all public properties of Characteristic. + */ + public java.util.Vector getFields(boolean skipAutoIds) + { + java.util.Vector fields = new java.util.Vector(); + if(!skipAutoIds) + { + fields.add("id"); + } + { + fields.add("identifier"); + } + { + fields.add("name"); + } + { + fields.add("__Type"); + } + { + fields.add("description"); + } + return fields; + } + + public java.util.Vector getFields() + { + return getFields(false); + } + + @Override + public String getIdField() + { + return "id"; + } + + + + @Override + public java.util.List getLabelFields() + { + java.util.List result = new java.util.ArrayList(); + result.add("Identifier"); + return result; + } + + @Deprecated + public String getFields(String sep) + { + return ("" + + "id" +sep + + "identifier" +sep + + "name" +sep + + "__Type" +sep + + "description" + ); + } + + @Override + public Object getIdValue() + { + return get(getIdField()); + } + + + public String getXrefIdFieldName(String fieldName) { + + return null; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { return false; } + if (obj == this) { return true; } + if (obj.getClass() != getClass()) { + return false; + } + Characteristic rhs = (Characteristic) obj; + return new org.apache.commons.lang.builder.EqualsBuilder() + //identifier + .append(identifier, rhs.getIdentifier()) + //name + .append(name, rhs.getName()) + //identifier + .append(identifier, rhs.getIdentifier()) + .isEquals(); + } + + @Override + public int hashCode() { + int firstNumber = this.getClass().getName().hashCode(); + int secondNumber = this.getClass().getSimpleName().hashCode(); + if(firstNumber % 2 == 0) { + firstNumber += 1; + } + if(secondNumber % 2 == 0) { + secondNumber += 1; + } + + return new org.apache.commons.lang.builder.HashCodeBuilder(firstNumber, secondNumber) + .append(identifier) + .append(name) + .append(identifier) + .toHashCode(); + } + + + + @Deprecated + public String getValues(String sep) + { + java.io.StringWriter out = new java.io.StringWriter(); + { + Object valueO = getId(); + String valueS; + if (valueO != null) + valueS = valueO.toString(); + else + valueS = ""; + valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); + valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); + out.write(valueS+sep); + } + { + Object valueO = getIdentifier(); + String valueS; + if (valueO != null) + valueS = valueO.toString(); + else + valueS = ""; + valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); + valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); + out.write(valueS+sep); + } + { + Object valueO = getName(); + String valueS; + if (valueO != null) + valueS = valueO.toString(); + else + valueS = ""; + valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); + valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); + out.write(valueS+sep); + } + { + Object valueO = get__Type(); + String valueS; + if (valueO != null) + valueS = valueO.toString(); + else + valueS = ""; + valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); + valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); + out.write(valueS+sep); + } + { + Object valueO = getDescription(); + String valueS; + if (valueO != null) + valueS = valueO.toString(); + else + valueS = ""; + valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); + valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); + out.write(valueS); + } + return out.toString(); + } + + @Override + public Characteristic create(org.molgenis.util.Tuple tuple) throws Exception + { + Characteristic e = new Characteristic(); + e.set(tuple); + return e; + } + + + +} + diff --git a/generated/javaorg/molgenis/test/Feature.java b/generated/javaorg/molgenis/test/Feature.java new file mode 100644 index 000000000..244ded4db --- /dev/null +++ b/generated/javaorg/molgenis/test/Feature.java @@ -0,0 +1,488 @@ + +/* File: org.molgenis/model/Feature.java + * Copyright: GBIC 2000-2012, all rights reserved + * Date: November 6, 2012 + * Generator: org.molgenis.generators.DataTypeGen 4.0.0-testing + * + * THIS FILE HAS BEEN GENERATED, PLEASE DO NOT EDIT! + */ + + +package org.molgenis.test; + +/** + * Feature: . + * @version November 6, 2012 + * @author MOLGENIS generator + */ +@javax.persistence.Entity +//@org.hibernate.search.annotations.Indexed +@javax.persistence.Table(name = "Feature" +) + + +@javax.xml.bind.annotation.XmlAccessorType(javax.xml.bind.annotation.XmlAccessType.FIELD) +//@EntityListeners({org.molgenis.test.db.FeatureEntityListener.class}) +public class Feature extends org.molgenis.test.Characteristic +{ + // fieldname constants + public final static String DATATYPE = "dataType"; + public final static String ID = "id"; + + //static methods + /** + * Shorthand for db.query(Feature.class). + */ + public static org.molgenis.framework.db.Query query(org.molgenis.framework.db.Database db) + { + return db.query(Feature.class); + } + + /** + * Shorthand for db.find(Feature.class, org.molgenis.framework.db.QueryRule ... rules). + */ + public static java.util.List find(org.molgenis.framework.db.Database db, org.molgenis.framework.db.QueryRule ... rules) throws org.molgenis.framework.db.DatabaseException + { + return db.find(Feature.class, rules); + } + + /** + * + */ + public static Feature findById(org.molgenis.framework.db.Database db, Integer id) throws org.molgenis.framework.db.DatabaseException + { + org.molgenis.framework.db.Query q = db.query(Feature.class); + q.eq(Feature.ID, id); + java.util.List result = q.find(); + if(result.size()>0) return result.get(0); + else return null; + } + + /** + * + */ + public static Feature findByIdentifier(org.molgenis.framework.db.Database db, String identifier) throws org.molgenis.framework.db.DatabaseException + { + org.molgenis.framework.db.Query q = db.query(Feature.class); + q.eq(Feature.IDENTIFIER, identifier); + java.util.List result = q.find(); + if(result.size()>0) return result.get(0); + else return null; + } + + /** + * + */ + public static Feature findByNameIdentifier(org.molgenis.framework.db.Database db, String name, String identifier) throws org.molgenis.framework.db.DatabaseException + { + org.molgenis.framework.db.Query q = db.query(Feature.class); + q.eq(Feature.NAME, name);q.eq(Feature.IDENTIFIER, identifier); + java.util.List result = q.find(); + if(result.size()>0) return result.get(0); + else return null; + } + + + // member variables (including setters.getters for interface) + + + //dataType[type=enum] + @javax.persistence.Column(name="dataType", nullable=false) + @javax.xml.bind.annotation.XmlElement(name="dataType") + + + + @javax.validation.constraints.NotNull + private String dataType = "string"; + @javax.persistence.Transient + private String dataType_label = null; + @javax.persistence.Transient + private java.util.List dataType_options = new java.util.ArrayList(); + + + //id[type=int] + + + //constructors + public Feature() + { + //set the type for a new instance + set__Type(this.getClass().getSimpleName()); + + //options for enum DataType + dataType_options.add(new org.molgenis.util.ValueLabel("xref","xref")); + dataType_options.add(new org.molgenis.util.ValueLabel("string","string")); + dataType_options.add(new org.molgenis.util.ValueLabel("nominal","nominal")); + dataType_options.add(new org.molgenis.util.ValueLabel("ordinal","ordinal")); + dataType_options.add(new org.molgenis.util.ValueLabel("date","date")); + dataType_options.add(new org.molgenis.util.ValueLabel("datetime","datetime")); + dataType_options.add(new org.molgenis.util.ValueLabel("int","int")); + dataType_options.add(new org.molgenis.util.ValueLabel("code","code")); + dataType_options.add(new org.molgenis.util.ValueLabel("image","image")); + dataType_options.add(new org.molgenis.util.ValueLabel("decimal","decimal")); + dataType_options.add(new org.molgenis.util.ValueLabel("bool","bool")); + dataType_options.add(new org.molgenis.util.ValueLabel("file","file")); + dataType_options.add(new org.molgenis.util.ValueLabel("log","log")); + dataType_options.add(new org.molgenis.util.ValueLabel("data","data")); + dataType_options.add(new org.molgenis.util.ValueLabel("exe","exe")); + } + + //getters and setters + /** + * Get the dataType. + * @return dataType. + */ + public String getDataType() + { + return this.dataType; + } + + @Deprecated + public String getDataType(org.molgenis.framework.db.Database db) + { + throw new UnsupportedOperationException(); + } + + /** + * Set the dataType. + * @param dataType + */ + public void setDataType( String dataType) + { + + this.dataType = dataType; + } + + + /** + * Get tha label for enum DataType. + */ + public String getDataTypeLabel() + { + return this.dataType_label; + } + + /** + * DataType is enum. This method returns all available enum options. + */ + public java.util.List getDataTypeOptions() + { + return dataType_options; + } + + + + + + + + /** + * Generic getter. Get the property by using the name. + */ + public Object get(String name) + { + name = name.toLowerCase(); + if (name.toLowerCase().equals("id")) + return getId(); + if (name.toLowerCase().equals("identifier")) + return getIdentifier(); + if (name.toLowerCase().equals("name")) + return getName(); + if (name.toLowerCase().equals("__type")) + return get__Type(); + if(name.toLowerCase().equals("__type_label")) + return get__TypeLabel(); + if (name.toLowerCase().equals("description")) + return getDescription(); + if (name.toLowerCase().equals("datatype")) + return getDataType(); + if(name.toLowerCase().equals("datatype_label")) + return getDataTypeLabel(); + return ""; + } + + public void validate() throws org.molgenis.framework.db.DatabaseException + { + if(this.getId() == null) throw new org.molgenis.framework.db.DatabaseException("required field id is null"); + if(this.getIdentifier() == null) throw new org.molgenis.framework.db.DatabaseException("required field identifier is null"); + if(this.getName() == null) throw new org.molgenis.framework.db.DatabaseException("required field name is null"); + if(this.get__Type() == null) throw new org.molgenis.framework.db.DatabaseException("required field __Type is null"); + if(this.getDataType() == null) throw new org.molgenis.framework.db.DatabaseException("required field dataType is null"); + } + + + + //@Implements + public void set( org.molgenis.util.Tuple tuple, boolean strict ) throws Exception + { + //optimization :-( + if(tuple instanceof org.molgenis.util.ResultSetTuple) + { + //set Id + this.setId(tuple.getInt("id")); + //set Identifier + this.setIdentifier(tuple.getString("Identifier")); + //set Name + this.setName(tuple.getString("Name")); + //set __Type + this.set__Type(tuple.getString("__Type")); + //set Description + this.setDescription(tuple.getString("description")); + //set DataType + this.setDataType(tuple.getString("dataType")); + } + else if(tuple != null) + { + //set Id + if( strict || tuple.getInt("id") != null) this.setId(tuple.getInt("id")); + if( tuple.getInt("Feature_id") != null) this.setId(tuple.getInt("Feature_id")); + //set Identifier + if( strict || tuple.getString("Identifier") != null) this.setIdentifier(tuple.getString("Identifier")); + if( tuple.getString("Feature_Identifier") != null) this.setIdentifier(tuple.getString("Feature_Identifier")); + //set Name + if( strict || tuple.getString("Name") != null) this.setName(tuple.getString("Name")); + if( tuple.getString("Feature_Name") != null) this.setName(tuple.getString("Feature_Name")); + //set __Type + if( strict || tuple.getString("__Type") != null) this.set__Type(tuple.getString("__Type")); + if( tuple.getString("Feature___Type") != null) this.set__Type(tuple.getString("Feature___Type")); + //set Description + if( strict || tuple.getString("description") != null) this.setDescription(tuple.getString("description")); + if( tuple.getString("Feature_description") != null) this.setDescription(tuple.getString("Feature_description")); + //set DataType + if( strict || tuple.getString("dataType") != null) this.setDataType(tuple.getString("dataType")); + if( tuple.getString("Feature_dataType") != null) this.setDataType(tuple.getString("Feature_dataType")); + } + //org.apache.log4j.Logger.getLogger("test").debug("set "+this); + } + + + + + + @Override + public String toString() + { + return this.toString(false); + } + + public String toString(boolean verbose) + { + String result = "Feature("; + result+= "id='" + getId()+"' "; + result+= "identifier='" + getIdentifier()+"' "; + result+= "name='" + getName()+"' "; + result+= "__Type='" + get__Type()+"' "; + result+= "description='" + getDescription()+"' "; + result+= "dataType='" + getDataType()+"'"; + result += ");"; + return result; + + } + + /** + * Get the names of all public properties of Feature. + */ + public java.util.Vector getFields(boolean skipAutoIds) + { + java.util.Vector fields = new java.util.Vector(); + if(!skipAutoIds) + { + fields.add("id"); + } + { + fields.add("identifier"); + } + { + fields.add("name"); + } + { + fields.add("__Type"); + } + { + fields.add("description"); + } + { + fields.add("dataType"); + } + return fields; + } + + public java.util.Vector getFields() + { + return getFields(false); + } + + @Override + public String getIdField() + { + return "id"; + } + + + + @Override + public java.util.List getLabelFields() + { + java.util.List result = new java.util.ArrayList(); + result.add("Identifier"); + return result; + } + + @Deprecated + public String getFields(String sep) + { + return ("" + + "id" +sep + + "identifier" +sep + + "name" +sep + + "__Type" +sep + + "description" +sep + + "dataType" + ); + } + + @Override + public Object getIdValue() + { + return get(getIdField()); + } + + + public String getXrefIdFieldName(String fieldName) { + + return null; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { return false; } + if (obj == this) { return true; } + if (obj.getClass() != getClass()) { + return false; + } + Feature rhs = (Feature) obj; + return new org.apache.commons.lang.builder.EqualsBuilder() + .appendSuper(super.equals(obj)) + .isEquals(); + } + + @Override + public int hashCode() { + int firstNumber = this.getClass().getName().hashCode(); + int secondNumber = this.getClass().getSimpleName().hashCode(); + if(firstNumber % 2 == 0) { + firstNumber += 1; + } + if(secondNumber % 2 == 0) { + secondNumber += 1; + } + + return new org.apache.commons.lang.builder.HashCodeBuilder(firstNumber, secondNumber) + .appendSuper(super.hashCode()) + .toHashCode(); + } + + + + @Deprecated + public String getValues(String sep) + { + java.io.StringWriter out = new java.io.StringWriter(); + { + Object valueO = getId(); + String valueS; + if (valueO != null) + valueS = valueO.toString(); + else + valueS = ""; + valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); + valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); + out.write(valueS+sep); + } + { + Object valueO = getIdentifier(); + String valueS; + if (valueO != null) + valueS = valueO.toString(); + else + valueS = ""; + valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); + valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); + out.write(valueS+sep); + } + { + Object valueO = getName(); + String valueS; + if (valueO != null) + valueS = valueO.toString(); + else + valueS = ""; + valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); + valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); + out.write(valueS+sep); + } + { + Object valueO = get__Type(); + String valueS; + if (valueO != null) + valueS = valueO.toString(); + else + valueS = ""; + valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); + valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); + out.write(valueS+sep); + } + { + Object valueO = getDescription(); + String valueS; + if (valueO != null) + valueS = valueO.toString(); + else + valueS = ""; + valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); + valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); + out.write(valueS+sep); + } + { + Object valueO = getDataType(); + String valueS; + if (valueO != null) + valueS = valueO.toString(); + else + valueS = ""; + valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); + valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); + out.write(valueS); + } + return out.toString(); + } + + @Override + public Feature create(org.molgenis.util.Tuple tuple) throws Exception + { + Feature e = new Feature(); + e.set(tuple); + return e; + } + +//1 + @javax.persistence.OneToMany(fetch=javax.persistence.FetchType.LAZY, mappedBy="feature"/*, cascade={javax.persistence.CascadeType.MERGE, javax.persistence.CascadeType.PERSIST, javax.persistence.CascadeType.REFRESH}*/) + private java.util.Collection featureCategoryCollection = new java.util.ArrayList(); + + @javax.xml.bind.annotation.XmlTransient + public java.util.Collection getFeatureCategoryCollection() + { + return featureCategoryCollection; + } + + public void setFeatureCategoryCollection(java.util.Collection collection) + { + for (org.molgenis.test.Category category : collection) { + category.setFeature(this); + } + featureCategoryCollection = collection; + } + + +} + diff --git a/generated/javaorg/molgenis/test/Identifiable.java b/generated/javaorg/molgenis/test/Identifiable.java new file mode 100644 index 000000000..13da6c17f --- /dev/null +++ b/generated/javaorg/molgenis/test/Identifiable.java @@ -0,0 +1,27 @@ + +/* File: org.molgenis/model/Identifiable.java + * Copyright: GBIC 2000-2012, all rights reserved + * Date: November 6, 2012 + * Generator: org.molgenis.generators.DataTypeGen 4.0.0-testing + * + * THIS FILE HAS BEEN GENERATED, PLEASE DO NOT EDIT! + */ + + +package org.molgenis.test; + +/** + * Identifiable: . + * @version November 6, 2012 + * @author MOLGENIS generator + */ +public interface Identifiable extends org.molgenis.test.Autoid +{ + public Integer getId(); + public void setId(Integer id); + public String getIdentifier(); + public void setIdentifier(String identifier); + public String getName(); + public void setName(String name); +} + diff --git a/generated/test/java/org/molgenis/generators/csv/CsvExport.java b/generated/test/java/org/molgenis/generators/csv/CsvExport.java new file mode 100644 index 000000000..0ec19afd4 --- /dev/null +++ b/generated/test/java/org/molgenis/generators/csv/CsvExport.java @@ -0,0 +1,337 @@ + +/* + * + * generator: org.molgenis.generators.csv.CsvExportGen 4.0.0-testing + * + * + * THIS FILE HAS BEEN GENERATED, PLEASE DO NOT EDIT! + */ +package org.molgenis.generators.csv; + +import java.io.File; +import java.io.IOException; +import java.text.ParseException; +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; + +import org.apache.log4j.Logger; +import org.molgenis.framework.db.Database; +import org.molgenis.framework.db.DatabaseException; +import org.molgenis.framework.db.Query; +import org.molgenis.framework.db.QueryRule; +import org.molgenis.framework.db.QueryRule.Operator; +import org.molgenis.model.MolgenisModelException; +import org.molgenis.util.Entity; +import org.molgenis.util.CsvFileWriter; + + + import org.molgenis.model.Characteristic; + import org.molgenis.model.Feature; + import org.molgenis.model.Category; + +public class CsvExport +{ + static Logger logger = Logger.getLogger(CsvExport.class.getSimpleName()); + + /** + * Default export all using a target directory and a database to export + * @param directory + * @param db + * @throws Exception + */ + public void exportAll(File directory, Database db) throws Exception + { + exportAll(directory, db, true, new QueryRule[]{}); + } + + /** + * Export all using a set of QueryRules used for all entities if applicable to that entity + * @param directory + * @param db + * @param rules + * @throws Exception + */ + public void exportAll(File directory, Database db, QueryRule ... rules) throws Exception + { + exportAll(directory, db, true, rules); + } + + /** + * Export all where a boolean skip autoid fields forces an ignore of the auto id field ("id") + * @param directory + * @param db + * @param skipAutoId + * @throws Exception + */ + public void exportAll(File directory, Database db, boolean skipAutoId) throws Exception + { + exportAll(directory, db, skipAutoId, new QueryRule[]{}); + } + + /** + * Export all with both a boolean skipAutoId and a set of QueryRules to specify both the skipping of auto id, and applying of a filter + * @param directory + * @param db + * @param skipAutoId + * @param rules + * @throws Exception + */ + public void exportAll(File directory, Database db, boolean skipAutoId, QueryRule ... rules) throws Exception + { + exportCharacteristic(db, new File(directory+"/characteristic.txt"), skipAutoId ? Arrays.asList(new String[]{"Identifier","Name","__Type","description"}) : null, rules); + exportFeature(db, new File(directory+"/feature.txt"), skipAutoId ? Arrays.asList(new String[]{"Identifier","Name","__Type","description","dataType"}) : null, rules); + exportCategory(db, new File(directory+"/category.txt"), skipAutoId ? Arrays.asList(new String[]{"feature_Identifier"}) : null, rules); + + logger.debug("done"); + } + + /** + * Export without system tables. + */ + public void exportRegular(File directory, Database db, boolean skipAutoId) throws Exception + { + exportRegular(directory, db, skipAutoId, new QueryRule[]{}); + } + + /** + * Export without system tables. + */ + public void exportRegular(File directory, Database db, boolean skipAutoId, QueryRule ... rules) throws Exception + { + exportCharacteristic(db, new File(directory+"/characteristic.txt"), skipAutoId ? Arrays.asList(new String[]{"Identifier","Name","__Type","description"}) : null, rules); + exportFeature(db, new File(directory+"/feature.txt"), skipAutoId ? Arrays.asList(new String[]{"Identifier","Name","__Type","description","dataType"}) : null, rules); + exportCategory(db, new File(directory+"/category.txt"), skipAutoId ? Arrays.asList(new String[]{"feature_Identifier"}) : null, rules); + + logger.debug("done"); + } + + public void exportAll(File directory, List ... entityLists) throws Exception + { + for(List l: entityLists) if(l.size()>0) + { + if(l.get(0).getClass().equals(Characteristic.class)) + exportCharacteristic(l, new File(directory+"/characteristic.txt")); + if(l.get(0).getClass().equals(Feature.class)) + exportFeature(l, new File(directory+"/feature.txt")); + if(l.get(0).getClass().equals(Category.class)) + exportCategory(l, new File(directory+"/category.txt")); + } + + logger.debug("done"); + } + + /** + * Export while excluding or including certain entity types. Defaults set: skip autoId, no QueryRules. + * If exclusion is set to true, the specialCases are used to exlude those entities from the export (entities not in list are exported). + * If exclusion is set to false, the specialCases are used to include those entities in the export (only entities in list are exported). + */ + public void exportSpecial(File directory, Database db, List> specialCases, boolean exclusion) throws Exception + { + exportSpecial(directory, db, true, specialCases, exclusion, new QueryRule[]{}); + } + + /** + * Export while excluding or including certain entity types. + * If exclusion is set to true, the specialCases are used to exlude those entities from the export (entities not in list are exported). + * If exclusion is set to false, the specialCases are used to include those entities in the export (only entities in list are exported). + * TODO: Could maybe replace exportAll(File directory, List ... entityLists) ? + */ + public void exportSpecial(File directory, Database db, boolean skipAutoId, List> specialCases, boolean exclusion, QueryRule ... rules) throws Exception + { + if((exclusion && !specialCases.contains(Characteristic.class)) || (!exclusion && specialCases.contains(Characteristic.class))) + { exportCharacteristic(db, new File(directory+"/characteristic.txt"), skipAutoId ? Arrays.asList(new String[]{"Identifier","Name","__Type","description"}) : null, rules); } + if((exclusion && !specialCases.contains(Feature.class)) || (!exclusion && specialCases.contains(Feature.class))) + { exportFeature(db, new File(directory+"/feature.txt"), skipAutoId ? Arrays.asList(new String[]{"Identifier","Name","__Type","description","dataType"}) : null, rules); } + if((exclusion && !specialCases.contains(Category.class)) || (!exclusion && specialCases.contains(Category.class))) + { exportCategory(db, new File(directory+"/category.txt"), skipAutoId ? Arrays.asList(new String[]{"feature_Identifier"}) : null, rules); } + + logger.debug("done"); + } + + private QueryRule[] matchQueryRulesToEntity(org.molgenis.model.elements.Entity e, QueryRule ... rules) throws MolgenisModelException + { + ArrayList tmpResult = new ArrayList(); + for(QueryRule q : rules){ + if(!(e.getAllField(q.getField()) == null)){ + tmpResult.add(q); //field is okay for this entity + } + //special case: eg. investigation.name -> if current entity is 'investigation', use field 'name' + String[] splitField = q.getField().split("\\."); + if(splitField.length == 2){ + if(e.getName().equals(splitField[0])){ + QueryRule copy = new QueryRule(q); + copy.setField(splitField[1]); + tmpResult.add(copy); + } + } + } + QueryRule[] result = new QueryRule[tmpResult.size()]; + for(int i=0; i fieldsToExport, QueryRule ... rules) throws DatabaseException, IOException, ParseException, MolgenisModelException + { + if(db.count(Characteristic.class, new QueryRule("__Type",Operator.EQUALS, "Characteristic")) > 0) + { + + org.molgenis.framework.db.Query query = db.query(Characteristic.class); + QueryRule type = new QueryRule("__Type",Operator.EQUALS, "Characteristic"); + query.addRules(type); + QueryRule[] newRules = matchQueryRulesToEntity(db.getMetaData().getEntity("Characteristic"), rules); + query.addRules(newRules); + int count = query.count(); + if(count > 0){ + CsvFileWriter characteristicWriter = new CsvFileWriter(f); + query.find(characteristicWriter, fieldsToExport); + characteristicWriter.close(); + } + } + } + + public void exportCharacteristic(List entities, File file) throws IOException, MolgenisModelException + { + if(entities.size()>0) + { + //filter nulls + List fields = entities.get(0).getFields(); + List notNulls = new ArrayList(); + + for(String f: fields) + { + for(Entity e: entities) + { + if(e.get(f) != null) + { + notNulls.add(f); + break; + } + } + } + + //write + CsvFileWriter characteristicWriter = new CsvFileWriter(file, notNulls); + characteristicWriter.writeHeader(); + for(Entity e: entities) + { + characteristicWriter.writeRow((org.molgenis.util.Entity)e); + } + characteristicWriter.close(); + } + } + /** + * export Feature to file. + * @param db the database to export from. + * @param f the file to export to. + */ + public void exportFeature(Database db, File f, List fieldsToExport, QueryRule ... rules) throws DatabaseException, IOException, ParseException, MolgenisModelException + { + if(db.count(Feature.class, new QueryRule("__Type",Operator.EQUALS, "Feature")) > 0) + { + + org.molgenis.framework.db.Query query = db.query(Feature.class); + QueryRule type = new QueryRule("__Type",Operator.EQUALS, "Feature"); + query.addRules(type); + QueryRule[] newRules = matchQueryRulesToEntity(db.getMetaData().getEntity("Feature"), rules); + query.addRules(newRules); + int count = query.count(); + if(count > 0){ + CsvFileWriter featureWriter = new CsvFileWriter(f); + query.find(featureWriter, fieldsToExport); + featureWriter.close(); + } + } + } + + public void exportFeature(List entities, File file) throws IOException, MolgenisModelException + { + if(entities.size()>0) + { + //filter nulls + List fields = entities.get(0).getFields(); + List notNulls = new ArrayList(); + + for(String f: fields) + { + for(Entity e: entities) + { + if(e.get(f) != null) + { + notNulls.add(f); + break; + } + } + } + + //write + CsvFileWriter featureWriter = new CsvFileWriter(file, notNulls); + featureWriter.writeHeader(); + for(Entity e: entities) + { + featureWriter.writeRow((org.molgenis.util.Entity)e); + } + featureWriter.close(); + } + } + /** + * export Category to file. + * @param db the database to export from. + * @param f the file to export to. + */ + public void exportCategory(Database db, File f, List fieldsToExport, QueryRule ... rules) throws DatabaseException, IOException, ParseException, MolgenisModelException + { + if(db.count(Category.class) > 0) + { + + org.molgenis.framework.db.Query query = db.query(Category.class); + + QueryRule[] newRules = matchQueryRulesToEntity(db.getMetaData().getEntity("Category"), rules); + query.addRules(newRules); + int count = query.count(); + if(count > 0){ + CsvFileWriter categoryWriter = new CsvFileWriter(f); + query.find(categoryWriter, fieldsToExport); + categoryWriter.close(); + } + } + } + + public void exportCategory(List entities, File file) throws IOException, MolgenisModelException + { + if(entities.size()>0) + { + //filter nulls + List fields = entities.get(0).getFields(); + List notNulls = new ArrayList(); + + for(String f: fields) + { + for(Entity e: entities) + { + if(e.get(f) != null) + { + notNulls.add(f); + break; + } + } + } + + //write + CsvFileWriter categoryWriter = new CsvFileWriter(file, notNulls); + categoryWriter.writeHeader(); + for(Entity e: entities) + { + categoryWriter.writeRow((org.molgenis.util.Entity)e); + } + categoryWriter.close(); + } + } +} \ No newline at end of file diff --git a/generated/test/java/org/molgenis/model/Autoid.java b/generated/test/java/org/molgenis/model/Autoid.java new file mode 100644 index 000000000..7e560aaef --- /dev/null +++ b/generated/test/java/org/molgenis/model/Autoid.java @@ -0,0 +1,23 @@ + +/* File: org.molgenis/model/Autoid.java + * Copyright: GBIC 2000-2012, all rights reserved + * Date: November 7, 2012 + * Generator: org.molgenis.generators.DataTypeGen 4.0.0-testing + * + * THIS FILE HAS BEEN GENERATED, PLEASE DO NOT EDIT! + */ + + +package org.molgenis.model; + +/** + * Autoid: . + * @version November 7, 2012 + * @author MOLGENIS generator + */ +public interface Autoid extends org.molgenis.util.Entity +{ + public Integer getId(); + public void setId(Integer id); +} + diff --git a/generated/test/java/org/molgenis/model/Category.java b/generated/test/java/org/molgenis/model/Category.java new file mode 100644 index 000000000..d569b6574 --- /dev/null +++ b/generated/test/java/org/molgenis/model/Category.java @@ -0,0 +1,404 @@ + +/* File: org.molgenis/model/Category.java + * Copyright: GBIC 2000-2012, all rights reserved + * Date: November 7, 2012 + * Generator: org.molgenis.generators.DataTypeGen 4.0.0-testing + * + * THIS FILE HAS BEEN GENERATED, PLEASE DO NOT EDIT! + */ + + +package org.molgenis.model; + +/** + * Category: . + * @version November 7, 2012 + * @author MOLGENIS generator + */ +@javax.persistence.Entity +//@org.hibernate.search.annotations.Indexed +@javax.persistence.Table(name = "Category" +) + + +@javax.xml.bind.annotation.XmlAccessorType(javax.xml.bind.annotation.XmlAccessType.FIELD) +//@EntityListeners({org.molgenis.model.db.CategoryEntityListener.class}) +public class Category extends org.molgenis.util.AbstractEntity implements org.molgenis.model.Autoid +{ + // fieldname constants + public final static String ID = "id"; + public final static String FEATURE = "feature"; + public final static String FEATURE_IDENTIFIER = "feature_Identifier"; + + //static methods + /** + * Shorthand for db.query(Category.class). + */ + public static org.molgenis.framework.db.Query query(org.molgenis.framework.db.Database db) + { + return db.query(Category.class); + } + + /** + * Shorthand for db.find(Category.class, org.molgenis.framework.db.QueryRule ... rules). + */ + public static java.util.List find(org.molgenis.framework.db.Database db, org.molgenis.framework.db.QueryRule ... rules) throws org.molgenis.framework.db.DatabaseException + { + return db.find(Category.class, rules); + } + + /** + * + */ + public static Category findById(org.molgenis.framework.db.Database db, Integer id) throws org.molgenis.framework.db.DatabaseException + { + org.molgenis.framework.db.Query q = db.query(Category.class); + q.eq(Category.ID, id); + java.util.List result = q.find(); + if(result.size()>0) return result.get(0); + else return null; + } + + + // member variables (including setters.getters for interface) + + + //id[type=int] + @javax.persistence.Id @javax.persistence.GeneratedValue(strategy = javax.persistence.GenerationType.AUTO) + @javax.persistence.Column(name="id", nullable=false) + @javax.xml.bind.annotation.XmlElement(name="id") + + //@javax.validation.constraints.NotNull + private Integer id = null; + + + //feature[type=xref] + @javax.persistence.ManyToOne(fetch=javax.persistence.FetchType.LAZY /*cascade={javax.persistence.CascadeType.MERGE, javax.persistence.CascadeType.PERSIST, javax.persistence.CascadeType.REFRESH}*/) + @javax.persistence.JoinColumn(name="feature", nullable=false) + + + + @javax.validation.constraints.NotNull + private org.molgenis.model.Feature feature = null; + @javax.persistence.Transient + private Integer feature_id = null; + @javax.persistence.Transient + private String feature_Identifier = null; + + //constructors + public Category() + { + + } + + //getters and setters + /** + * Get the id. + * @return id. + */ + public Integer getId() + { + return this.id; + } + + + /** + * Set the id. + * @param id + */ + public void setId( Integer id) + { + this.id = id; + } + + + + /** + * Get the feature. + * @return feature. + */ + public org.molgenis.model.Feature getFeature() + { + return this.feature; + } + + @Deprecated + public org.molgenis.model.Feature getFeature(org.molgenis.framework.db.Database db) + { + throw new UnsupportedOperationException(); + } + + /** + * Set the feature. + * @param feature + */ + public void setFeature( org.molgenis.model.Feature feature) + { + + this.feature = feature; + } + + + + /** + * Set foreign key for field feature. + * This will erase any foreign key objects currently set. + * FIXME: can we autoload the new object? + */ + public void setFeature_Id(Integer feature_id) + { + this.feature_id = feature_id; + } + + public void setFeature(Integer feature_id) + { + this.feature_id = feature_id; + } + + public Integer getFeature_Id() + { + + if(feature != null) + { + return feature.getId(); + } + else + { + return feature_id; + } + } + + /** + * Get a pretty label Identifier for cross reference Feature to Feature.Id. + */ + public String getFeature_Identifier() + { + //FIXME should we auto-load based on getFeature()? + if(feature != null) { + return feature.getIdentifier(); + } else { + return feature_Identifier; + } + } + + /** + * Set a pretty label for cross reference Feature to Feature.Id. + * Implies setFeature(null) until save + */ + public void setFeature_Identifier(String feature_Identifier) + { + this.feature_Identifier = feature_Identifier; + } + + + + + /** + * Generic getter. Get the property by using the name. + */ + public Object get(String name) + { + name = name.toLowerCase(); + if (name.toLowerCase().equals("id")) + return getId(); + if (name.toLowerCase().equals("feature")) + return getFeature(); + if(name.toLowerCase().equals("feature_id")) + return getFeature_Id(); + if(name.toLowerCase().equals("feature_identifier")) + return getFeature_Identifier(); + return ""; + } + + public void validate() throws org.molgenis.framework.db.DatabaseException + { + if(this.getId() == null) throw new org.molgenis.framework.db.DatabaseException("required field id is null"); + if(this.getFeature() == null) throw new org.molgenis.framework.db.DatabaseException("required field feature is null"); + } + + + + //@Implements + public void set( org.molgenis.util.Tuple tuple, boolean strict ) throws Exception + { + //optimization :-( + if(tuple instanceof org.molgenis.util.ResultSetTuple) + { + //set Id + this.setId(tuple.getInt("id")); + //set Feature + this.setFeature(tuple.getInt("feature")); + //set label Identifier for xref field Feature + this.setFeature_Identifier(tuple.getString("feature_Identifier")); + } + else if(tuple != null) + { + //set Id + if( strict || tuple.getInt("id") != null) this.setId(tuple.getInt("id")); + if( tuple.getInt("Category_id") != null) this.setId(tuple.getInt("Category_id")); + //set Feature + if( strict || tuple.getInt("feature_id") != null) this.setFeature(tuple.getInt("feature_id")); + if( tuple.getInt("Category_feature_id") != null) this.setFeature(tuple.getInt("Category_feature_id")); + //alias of xref + if( tuple.getObject("feature") != null) this.setFeature(tuple.getInt("feature")); + if( tuple.getObject("Category_feature") != null) this.setFeature(tuple.getInt("Category_feature")); + //set label for field Feature + if( strict || tuple.getObject("feature_Identifier") != null) this.setFeature_Identifier(tuple.getString("feature_Identifier")); + if( tuple.getObject("Category_feature_Identifier") != null ) this.setFeature_Identifier(tuple.getString("Category_feature_Identifier")); + } + //org.apache.log4j.Logger.getLogger("test").debug("set "+this); + } + + + + + + @Override + public String toString() + { + return this.toString(false); + } + + public String toString(boolean verbose) + { + String result = "Category("; + result+= "id='" + getId()+"' "; + result+= " feature_id='" + getFeature_Id()+"' "; + result+= " feature_identifier='" + getFeature_Identifier()+"' "; + result += ");"; + return result; + + } + + /** + * Get the names of all public properties of Category. + */ + public java.util.Vector getFields(boolean skipAutoIds) + { + java.util.Vector fields = new java.util.Vector(); + if(!skipAutoIds) + { + fields.add("id"); + } + { + fields.add("feature_id"); + } + fields.add("feature_identifier"); + return fields; + } + + public java.util.Vector getFields() + { + return getFields(false); + } + + @Override + public String getIdField() + { + return "id"; + } + + + + @Override + public java.util.List getLabelFields() + { + java.util.List result = new java.util.ArrayList(); + result.add("id"); + return result; + } + + @Deprecated + public String getFields(String sep) + { + return ("" + + "id" +sep + + "feature" + ); + } + + @Override + public Object getIdValue() + { + return get(getIdField()); + } + + + public String getXrefIdFieldName(String fieldName) { + if (fieldName.equalsIgnoreCase("feature")) { + return "id"; + } + + return null; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { return false; } + if (obj == this) { return true; } + if (obj.getClass() != getClass()) { + return false; + } + Category rhs = (Category) obj; + return new org.apache.commons.lang.builder.EqualsBuilder() + .isEquals(); + } + + @Override + public int hashCode() { + int firstNumber = this.getClass().getName().hashCode(); + int secondNumber = this.getClass().getSimpleName().hashCode(); + if(firstNumber % 2 == 0) { + firstNumber += 1; + } + if(secondNumber % 2 == 0) { + secondNumber += 1; + } + + return new org.apache.commons.lang.builder.HashCodeBuilder(firstNumber, secondNumber) + .toHashCode(); + } + + + + @Deprecated + public String getValues(String sep) + { + java.io.StringWriter out = new java.io.StringWriter(); + { + Object valueO = getId(); + String valueS; + if (valueO != null) + valueS = valueO.toString(); + else + valueS = ""; + valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); + valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); + out.write(valueS+sep); + } + { + Object valueO = getFeature(); + String valueS; + if (valueO != null) + valueS = valueO.toString(); + else + valueS = ""; + valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); + valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); + out.write(valueS); + } + return out.toString(); + } + + @Override + public Category create(org.molgenis.util.Tuple tuple) throws Exception + { + Category e = new Category(); + e.set(tuple); + return e; + } + + + +} + diff --git a/generated/test/java/org/molgenis/model/Characteristic.java b/generated/test/java/org/molgenis/model/Characteristic.java new file mode 100644 index 000000000..979e52880 --- /dev/null +++ b/generated/test/java/org/molgenis/model/Characteristic.java @@ -0,0 +1,578 @@ + +/* File: org.molgenis/model/Characteristic.java + * Copyright: GBIC 2000-2012, all rights reserved + * Date: November 7, 2012 + * Generator: org.molgenis.generators.DataTypeGen 4.0.0-testing + * + * THIS FILE HAS BEEN GENERATED, PLEASE DO NOT EDIT! + */ + + +package org.molgenis.model; + +/** + * Characteristic: . + * @version November 7, 2012 + * @author MOLGENIS generator + */ +@javax.persistence.Entity +//@org.hibernate.search.annotations.Indexed +@javax.persistence.Table(name = "Characteristic", uniqueConstraints={ @javax.persistence.UniqueConstraint( columnNames={ "Identifier" }), @javax.persistence.UniqueConstraint( columnNames={ "Name", "Identifier" } ) } +) + + +@javax.persistence.Inheritance(strategy=javax.persistence.InheritanceType.JOINED) +@javax.persistence.DiscriminatorColumn(name="DType", discriminatorType=javax.persistence.DiscriminatorType.STRING) +@javax.xml.bind.annotation.XmlAccessorType(javax.xml.bind.annotation.XmlAccessType.FIELD) +//@EntityListeners({org.molgenis.model.db.CharacteristicEntityListener.class}) +public class Characteristic extends org.molgenis.util.AbstractEntity implements org.molgenis.model.Identifiable +{ + // fieldname constants + public final static String ID = "id"; + public final static String IDENTIFIER = "Identifier"; + public final static String NAME = "Name"; + public final static String __TYPE = "__Type"; + public final static String DESCRIPTION = "description"; + + //static methods + /** + * Shorthand for db.query(Characteristic.class). + */ + public static org.molgenis.framework.db.Query query(org.molgenis.framework.db.Database db) + { + return db.query(Characteristic.class); + } + + /** + * Shorthand for db.find(Characteristic.class, org.molgenis.framework.db.QueryRule ... rules). + */ + public static java.util.List find(org.molgenis.framework.db.Database db, org.molgenis.framework.db.QueryRule ... rules) throws org.molgenis.framework.db.DatabaseException + { + return db.find(Characteristic.class, rules); + } + + /** + * + */ + public static Characteristic findById(org.molgenis.framework.db.Database db, Integer id) throws org.molgenis.framework.db.DatabaseException + { + org.molgenis.framework.db.Query q = db.query(Characteristic.class); + q.eq(Characteristic.ID, id); + java.util.List result = q.find(); + if(result.size()>0) return result.get(0); + else return null; + } + + /** + * + */ + public static Characteristic findByIdentifier(org.molgenis.framework.db.Database db, String identifier) throws org.molgenis.framework.db.DatabaseException + { + org.molgenis.framework.db.Query q = db.query(Characteristic.class); + q.eq(Characteristic.IDENTIFIER, identifier); + java.util.List result = q.find(); + if(result.size()>0) return result.get(0); + else return null; + } + + /** + * + */ + public static Characteristic findByNameIdentifier(org.molgenis.framework.db.Database db, String name, String identifier) throws org.molgenis.framework.db.DatabaseException + { + org.molgenis.framework.db.Query q = db.query(Characteristic.class); + q.eq(Characteristic.NAME, name);q.eq(Characteristic.IDENTIFIER, identifier); + java.util.List result = q.find(); + if(result.size()>0) return result.get(0); + else return null; + } + + + // member variables (including setters.getters for interface) + + + //id[type=int] + @javax.persistence.Id @javax.persistence.GeneratedValue(strategy = javax.persistence.GenerationType.AUTO) + @javax.persistence.Column(name="id", nullable=false) + @javax.xml.bind.annotation.XmlElement(name="id") + + //@javax.validation.constraints.NotNull + private Integer id = null; + + + //Identifier[type=string] +// @org.hibernate.search.annotations.Field(index=org.hibernate.search.annotations.Index.TOKENIZED, store=org.hibernate.search.annotations.Store.NO) + @javax.persistence.Column(name="Identifier", nullable=false) + @javax.xml.bind.annotation.XmlElement(name="identifier") + + + + @javax.validation.constraints.NotNull + private String identifier = null; + + + //assign name[type=string] +// @org.hibernate.search.annotations.Field(index=org.hibernate.search.annotations.Index.TOKENIZED, store=org.hibernate.search.annotations.Store.NO) + @javax.persistence.Column(name="Name", nullable=false) + @javax.xml.bind.annotation.XmlElement(name="name") + + + + @javax.validation.constraints.NotNull + private String name = null; + + + //Subtypes have to be set to allow searching[type=enum] + @javax.persistence.Column(name="DType", nullable=false) + @javax.xml.bind.annotation.XmlElement(name="__Type") + + + + @javax.validation.constraints.NotNull + private String __Type = null; + @javax.persistence.Transient + private String __Type_label = null; + @javax.persistence.Transient + private java.util.List __Type_options = new java.util.ArrayList(); + + + //description[type=text] +// @javax.persistence.Lob() + @javax.persistence.Column(name="description", length=16777216) + + + + private String description = null; + + //constructors + public Characteristic() + { + //set the type for a new instance + set__Type(this.getClass().getSimpleName()); + + //options for enum __Type + __Type_options.add(new org.molgenis.util.ValueLabel("Characteristic","Characteristic")); + __Type_options.add(new org.molgenis.util.ValueLabel("Feature","Feature")); + } + + //getters and setters + /** + * Get the id. + * @return id. + */ + public Integer getId() + { + return this.id; + } + + + /** + * Set the id. + * @param id + */ + public void setId( Integer id) + { + this.id = id; + } + + + + /** + * Get the Identifier. + * @return identifier. + */ + public String getIdentifier() + { + return this.identifier; + } + + @Deprecated + public String getIdentifier(org.molgenis.framework.db.Database db) + { + throw new UnsupportedOperationException(); + } + + /** + * Set the Identifier. + * @param identifier + */ + public void setIdentifier( String identifier) + { + + this.identifier = identifier; + } + + + + /** + * Get the assign name. + * @return name. + */ + public String getName() + { + return this.name; + } + + @Deprecated + public String getName(org.molgenis.framework.db.Database db) + { + throw new UnsupportedOperationException(); + } + + /** + * Set the assign name. + * @param name + */ + public void setName( String name) + { + + this.name = name; + } + + + + /** + * Get the Subtypes have to be set to allow searching. + * @return __Type. + */ + public String get__Type() + { + return this.__Type; + } + + @Deprecated + public String get__Type(org.molgenis.framework.db.Database db) + { + throw new UnsupportedOperationException(); + } + + /** + * Set the Subtypes have to be set to allow searching. + * @param __Type + */ + public void set__Type( String __Type) + { + + this.__Type = __Type; + } + + + /** + * Get tha label for enum __Type. + */ + public String get__TypeLabel() + { + return this.__Type_label; + } + + /** + * __Type is enum. This method returns all available enum options. + */ + public java.util.List get__TypeOptions() + { + return __Type_options; + } + + + /** + * Get the description. + * @return description. + */ + public String getDescription() + { + return this.description; + } + + @Deprecated + public String getDescription(org.molgenis.framework.db.Database db) + { + throw new UnsupportedOperationException(); + } + + /** + * Set the description. + * @param description + */ + public void setDescription( String description) + { + + this.description = description; + } + + + + + /** + * Generic getter. Get the property by using the name. + */ + public Object get(String name) + { + name = name.toLowerCase(); + if (name.toLowerCase().equals("id")) + return getId(); + if (name.toLowerCase().equals("identifier")) + return getIdentifier(); + if (name.toLowerCase().equals("name")) + return getName(); + if (name.toLowerCase().equals("__type")) + return get__Type(); + if(name.toLowerCase().equals("__type_label")) + return get__TypeLabel(); + if (name.toLowerCase().equals("description")) + return getDescription(); + return ""; + } + + public void validate() throws org.molgenis.framework.db.DatabaseException + { + if(this.getId() == null) throw new org.molgenis.framework.db.DatabaseException("required field id is null"); + if(this.getIdentifier() == null) throw new org.molgenis.framework.db.DatabaseException("required field identifier is null"); + if(this.getName() == null) throw new org.molgenis.framework.db.DatabaseException("required field name is null"); + if(this.get__Type() == null) throw new org.molgenis.framework.db.DatabaseException("required field __Type is null"); + } + + + + //@Implements + public void set( org.molgenis.util.Tuple tuple, boolean strict ) throws Exception + { + //optimization :-( + if(tuple instanceof org.molgenis.util.ResultSetTuple) + { + //set Id + this.setId(tuple.getInt("id")); + //set Identifier + this.setIdentifier(tuple.getString("Identifier")); + //set Name + this.setName(tuple.getString("Name")); + //set __Type + this.set__Type(tuple.getString("__Type")); + //set Description + this.setDescription(tuple.getString("description")); + } + else if(tuple != null) + { + //set Id + if( strict || tuple.getInt("id") != null) this.setId(tuple.getInt("id")); + if( tuple.getInt("Characteristic_id") != null) this.setId(tuple.getInt("Characteristic_id")); + //set Identifier + if( strict || tuple.getString("Identifier") != null) this.setIdentifier(tuple.getString("Identifier")); + if( tuple.getString("Characteristic_Identifier") != null) this.setIdentifier(tuple.getString("Characteristic_Identifier")); + //set Name + if( strict || tuple.getString("Name") != null) this.setName(tuple.getString("Name")); + if( tuple.getString("Characteristic_Name") != null) this.setName(tuple.getString("Characteristic_Name")); + //set __Type + if( strict || tuple.getString("__Type") != null) this.set__Type(tuple.getString("__Type")); + if( tuple.getString("Characteristic___Type") != null) this.set__Type(tuple.getString("Characteristic___Type")); + //set Description + if( strict || tuple.getString("description") != null) this.setDescription(tuple.getString("description")); + if( tuple.getString("Characteristic_description") != null) this.setDescription(tuple.getString("Characteristic_description")); + } + //org.apache.log4j.Logger.getLogger("test").debug("set "+this); + } + + + + + + @Override + public String toString() + { + return this.toString(false); + } + + public String toString(boolean verbose) + { + String result = "Characteristic("; + result+= "id='" + getId()+"' "; + result+= "identifier='" + getIdentifier()+"' "; + result+= "name='" + getName()+"' "; + result+= "__Type='" + get__Type()+"' "; + result+= "description='" + getDescription()+"'"; + result += ");"; + return result; + + } + + /** + * Get the names of all public properties of Characteristic. + */ + public java.util.Vector getFields(boolean skipAutoIds) + { + java.util.Vector fields = new java.util.Vector(); + if(!skipAutoIds) + { + fields.add("id"); + } + { + fields.add("identifier"); + } + { + fields.add("name"); + } + { + fields.add("__Type"); + } + { + fields.add("description"); + } + return fields; + } + + public java.util.Vector getFields() + { + return getFields(false); + } + + @Override + public String getIdField() + { + return "id"; + } + + + + @Override + public java.util.List getLabelFields() + { + java.util.List result = new java.util.ArrayList(); + result.add("Identifier"); + return result; + } + + @Deprecated + public String getFields(String sep) + { + return ("" + + "id" +sep + + "identifier" +sep + + "name" +sep + + "__Type" +sep + + "description" + ); + } + + @Override + public Object getIdValue() + { + return get(getIdField()); + } + + + public String getXrefIdFieldName(String fieldName) { + + return null; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { return false; } + if (obj == this) { return true; } + if (obj.getClass() != getClass()) { + return false; + } + Characteristic rhs = (Characteristic) obj; + return new org.apache.commons.lang.builder.EqualsBuilder() + //identifier + .append(identifier, rhs.getIdentifier()) + //name + .append(name, rhs.getName()) + //identifier + .append(identifier, rhs.getIdentifier()) + .isEquals(); + } + + @Override + public int hashCode() { + int firstNumber = this.getClass().getName().hashCode(); + int secondNumber = this.getClass().getSimpleName().hashCode(); + if(firstNumber % 2 == 0) { + firstNumber += 1; + } + if(secondNumber % 2 == 0) { + secondNumber += 1; + } + + return new org.apache.commons.lang.builder.HashCodeBuilder(firstNumber, secondNumber) + .append(identifier) + .append(name) + .append(identifier) + .toHashCode(); + } + + + + @Deprecated + public String getValues(String sep) + { + java.io.StringWriter out = new java.io.StringWriter(); + { + Object valueO = getId(); + String valueS; + if (valueO != null) + valueS = valueO.toString(); + else + valueS = ""; + valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); + valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); + out.write(valueS+sep); + } + { + Object valueO = getIdentifier(); + String valueS; + if (valueO != null) + valueS = valueO.toString(); + else + valueS = ""; + valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); + valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); + out.write(valueS+sep); + } + { + Object valueO = getName(); + String valueS; + if (valueO != null) + valueS = valueO.toString(); + else + valueS = ""; + valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); + valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); + out.write(valueS+sep); + } + { + Object valueO = get__Type(); + String valueS; + if (valueO != null) + valueS = valueO.toString(); + else + valueS = ""; + valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); + valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); + out.write(valueS+sep); + } + { + Object valueO = getDescription(); + String valueS; + if (valueO != null) + valueS = valueO.toString(); + else + valueS = ""; + valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); + valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); + out.write(valueS); + } + return out.toString(); + } + + @Override + public Characteristic create(org.molgenis.util.Tuple tuple) throws Exception + { + Characteristic e = new Characteristic(); + e.set(tuple); + return e; + } + + + +} + diff --git a/generated/test/java/org/molgenis/model/Feature.java b/generated/test/java/org/molgenis/model/Feature.java new file mode 100644 index 000000000..f7519b2b3 --- /dev/null +++ b/generated/test/java/org/molgenis/model/Feature.java @@ -0,0 +1,488 @@ + +/* File: org.molgenis/model/Feature.java + * Copyright: GBIC 2000-2012, all rights reserved + * Date: November 7, 2012 + * Generator: org.molgenis.generators.DataTypeGen 4.0.0-testing + * + * THIS FILE HAS BEEN GENERATED, PLEASE DO NOT EDIT! + */ + + +package org.molgenis.model; + +/** + * Feature: . + * @version November 7, 2012 + * @author MOLGENIS generator + */ +@javax.persistence.Entity +//@org.hibernate.search.annotations.Indexed +@javax.persistence.Table(name = "Feature" +) + + +@javax.xml.bind.annotation.XmlAccessorType(javax.xml.bind.annotation.XmlAccessType.FIELD) +//@EntityListeners({org.molgenis.model.db.FeatureEntityListener.class}) +public class Feature extends org.molgenis.model.Characteristic +{ + // fieldname constants + public final static String DATATYPE = "dataType"; + public final static String ID = "id"; + + //static methods + /** + * Shorthand for db.query(Feature.class). + */ + public static org.molgenis.framework.db.Query query(org.molgenis.framework.db.Database db) + { + return db.query(Feature.class); + } + + /** + * Shorthand for db.find(Feature.class, org.molgenis.framework.db.QueryRule ... rules). + */ + public static java.util.List find(org.molgenis.framework.db.Database db, org.molgenis.framework.db.QueryRule ... rules) throws org.molgenis.framework.db.DatabaseException + { + return db.find(Feature.class, rules); + } + + /** + * + */ + public static Feature findById(org.molgenis.framework.db.Database db, Integer id) throws org.molgenis.framework.db.DatabaseException + { + org.molgenis.framework.db.Query q = db.query(Feature.class); + q.eq(Feature.ID, id); + java.util.List result = q.find(); + if(result.size()>0) return result.get(0); + else return null; + } + + /** + * + */ + public static Feature findByIdentifier(org.molgenis.framework.db.Database db, String identifier) throws org.molgenis.framework.db.DatabaseException + { + org.molgenis.framework.db.Query q = db.query(Feature.class); + q.eq(Feature.IDENTIFIER, identifier); + java.util.List result = q.find(); + if(result.size()>0) return result.get(0); + else return null; + } + + /** + * + */ + public static Feature findByNameIdentifier(org.molgenis.framework.db.Database db, String name, String identifier) throws org.molgenis.framework.db.DatabaseException + { + org.molgenis.framework.db.Query q = db.query(Feature.class); + q.eq(Feature.NAME, name);q.eq(Feature.IDENTIFIER, identifier); + java.util.List result = q.find(); + if(result.size()>0) return result.get(0); + else return null; + } + + + // member variables (including setters.getters for interface) + + + //dataType[type=enum] + @javax.persistence.Column(name="dataType", nullable=false) + @javax.xml.bind.annotation.XmlElement(name="dataType") + + + + @javax.validation.constraints.NotNull + private String dataType = "string"; + @javax.persistence.Transient + private String dataType_label = null; + @javax.persistence.Transient + private java.util.List dataType_options = new java.util.ArrayList(); + + + //id[type=int] + + + //constructors + public Feature() + { + //set the type for a new instance + set__Type(this.getClass().getSimpleName()); + + //options for enum DataType + dataType_options.add(new org.molgenis.util.ValueLabel("xref","xref")); + dataType_options.add(new org.molgenis.util.ValueLabel("string","string")); + dataType_options.add(new org.molgenis.util.ValueLabel("nominal","nominal")); + dataType_options.add(new org.molgenis.util.ValueLabel("ordinal","ordinal")); + dataType_options.add(new org.molgenis.util.ValueLabel("date","date")); + dataType_options.add(new org.molgenis.util.ValueLabel("datetime","datetime")); + dataType_options.add(new org.molgenis.util.ValueLabel("int","int")); + dataType_options.add(new org.molgenis.util.ValueLabel("code","code")); + dataType_options.add(new org.molgenis.util.ValueLabel("image","image")); + dataType_options.add(new org.molgenis.util.ValueLabel("decimal","decimal")); + dataType_options.add(new org.molgenis.util.ValueLabel("bool","bool")); + dataType_options.add(new org.molgenis.util.ValueLabel("file","file")); + dataType_options.add(new org.molgenis.util.ValueLabel("log","log")); + dataType_options.add(new org.molgenis.util.ValueLabel("data","data")); + dataType_options.add(new org.molgenis.util.ValueLabel("exe","exe")); + } + + //getters and setters + /** + * Get the dataType. + * @return dataType. + */ + public String getDataType() + { + return this.dataType; + } + + @Deprecated + public String getDataType(org.molgenis.framework.db.Database db) + { + throw new UnsupportedOperationException(); + } + + /** + * Set the dataType. + * @param dataType + */ + public void setDataType( String dataType) + { + + this.dataType = dataType; + } + + + /** + * Get tha label for enum DataType. + */ + public String getDataTypeLabel() + { + return this.dataType_label; + } + + /** + * DataType is enum. This method returns all available enum options. + */ + public java.util.List getDataTypeOptions() + { + return dataType_options; + } + + + + + + + + /** + * Generic getter. Get the property by using the name. + */ + public Object get(String name) + { + name = name.toLowerCase(); + if (name.toLowerCase().equals("id")) + return getId(); + if (name.toLowerCase().equals("identifier")) + return getIdentifier(); + if (name.toLowerCase().equals("name")) + return getName(); + if (name.toLowerCase().equals("__type")) + return get__Type(); + if(name.toLowerCase().equals("__type_label")) + return get__TypeLabel(); + if (name.toLowerCase().equals("description")) + return getDescription(); + if (name.toLowerCase().equals("datatype")) + return getDataType(); + if(name.toLowerCase().equals("datatype_label")) + return getDataTypeLabel(); + return ""; + } + + public void validate() throws org.molgenis.framework.db.DatabaseException + { + if(this.getId() == null) throw new org.molgenis.framework.db.DatabaseException("required field id is null"); + if(this.getIdentifier() == null) throw new org.molgenis.framework.db.DatabaseException("required field identifier is null"); + if(this.getName() == null) throw new org.molgenis.framework.db.DatabaseException("required field name is null"); + if(this.get__Type() == null) throw new org.molgenis.framework.db.DatabaseException("required field __Type is null"); + if(this.getDataType() == null) throw new org.molgenis.framework.db.DatabaseException("required field dataType is null"); + } + + + + //@Implements + public void set( org.molgenis.util.Tuple tuple, boolean strict ) throws Exception + { + //optimization :-( + if(tuple instanceof org.molgenis.util.ResultSetTuple) + { + //set Id + this.setId(tuple.getInt("id")); + //set Identifier + this.setIdentifier(tuple.getString("Identifier")); + //set Name + this.setName(tuple.getString("Name")); + //set __Type + this.set__Type(tuple.getString("__Type")); + //set Description + this.setDescription(tuple.getString("description")); + //set DataType + this.setDataType(tuple.getString("dataType")); + } + else if(tuple != null) + { + //set Id + if( strict || tuple.getInt("id") != null) this.setId(tuple.getInt("id")); + if( tuple.getInt("Feature_id") != null) this.setId(tuple.getInt("Feature_id")); + //set Identifier + if( strict || tuple.getString("Identifier") != null) this.setIdentifier(tuple.getString("Identifier")); + if( tuple.getString("Feature_Identifier") != null) this.setIdentifier(tuple.getString("Feature_Identifier")); + //set Name + if( strict || tuple.getString("Name") != null) this.setName(tuple.getString("Name")); + if( tuple.getString("Feature_Name") != null) this.setName(tuple.getString("Feature_Name")); + //set __Type + if( strict || tuple.getString("__Type") != null) this.set__Type(tuple.getString("__Type")); + if( tuple.getString("Feature___Type") != null) this.set__Type(tuple.getString("Feature___Type")); + //set Description + if( strict || tuple.getString("description") != null) this.setDescription(tuple.getString("description")); + if( tuple.getString("Feature_description") != null) this.setDescription(tuple.getString("Feature_description")); + //set DataType + if( strict || tuple.getString("dataType") != null) this.setDataType(tuple.getString("dataType")); + if( tuple.getString("Feature_dataType") != null) this.setDataType(tuple.getString("Feature_dataType")); + } + //org.apache.log4j.Logger.getLogger("test").debug("set "+this); + } + + + + + + @Override + public String toString() + { + return this.toString(false); + } + + public String toString(boolean verbose) + { + String result = "Feature("; + result+= "id='" + getId()+"' "; + result+= "identifier='" + getIdentifier()+"' "; + result+= "name='" + getName()+"' "; + result+= "__Type='" + get__Type()+"' "; + result+= "description='" + getDescription()+"' "; + result+= "dataType='" + getDataType()+"'"; + result += ");"; + return result; + + } + + /** + * Get the names of all public properties of Feature. + */ + public java.util.Vector getFields(boolean skipAutoIds) + { + java.util.Vector fields = new java.util.Vector(); + if(!skipAutoIds) + { + fields.add("id"); + } + { + fields.add("identifier"); + } + { + fields.add("name"); + } + { + fields.add("__Type"); + } + { + fields.add("description"); + } + { + fields.add("dataType"); + } + return fields; + } + + public java.util.Vector getFields() + { + return getFields(false); + } + + @Override + public String getIdField() + { + return "id"; + } + + + + @Override + public java.util.List getLabelFields() + { + java.util.List result = new java.util.ArrayList(); + result.add("Identifier"); + return result; + } + + @Deprecated + public String getFields(String sep) + { + return ("" + + "id" +sep + + "identifier" +sep + + "name" +sep + + "__Type" +sep + + "description" +sep + + "dataType" + ); + } + + @Override + public Object getIdValue() + { + return get(getIdField()); + } + + + public String getXrefIdFieldName(String fieldName) { + + return null; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { return false; } + if (obj == this) { return true; } + if (obj.getClass() != getClass()) { + return false; + } + Feature rhs = (Feature) obj; + return new org.apache.commons.lang.builder.EqualsBuilder() + .appendSuper(super.equals(obj)) + .isEquals(); + } + + @Override + public int hashCode() { + int firstNumber = this.getClass().getName().hashCode(); + int secondNumber = this.getClass().getSimpleName().hashCode(); + if(firstNumber % 2 == 0) { + firstNumber += 1; + } + if(secondNumber % 2 == 0) { + secondNumber += 1; + } + + return new org.apache.commons.lang.builder.HashCodeBuilder(firstNumber, secondNumber) + .appendSuper(super.hashCode()) + .toHashCode(); + } + + + + @Deprecated + public String getValues(String sep) + { + java.io.StringWriter out = new java.io.StringWriter(); + { + Object valueO = getId(); + String valueS; + if (valueO != null) + valueS = valueO.toString(); + else + valueS = ""; + valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); + valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); + out.write(valueS+sep); + } + { + Object valueO = getIdentifier(); + String valueS; + if (valueO != null) + valueS = valueO.toString(); + else + valueS = ""; + valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); + valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); + out.write(valueS+sep); + } + { + Object valueO = getName(); + String valueS; + if (valueO != null) + valueS = valueO.toString(); + else + valueS = ""; + valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); + valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); + out.write(valueS+sep); + } + { + Object valueO = get__Type(); + String valueS; + if (valueO != null) + valueS = valueO.toString(); + else + valueS = ""; + valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); + valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); + out.write(valueS+sep); + } + { + Object valueO = getDescription(); + String valueS; + if (valueO != null) + valueS = valueO.toString(); + else + valueS = ""; + valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); + valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); + out.write(valueS+sep); + } + { + Object valueO = getDataType(); + String valueS; + if (valueO != null) + valueS = valueO.toString(); + else + valueS = ""; + valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); + valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); + out.write(valueS); + } + return out.toString(); + } + + @Override + public Feature create(org.molgenis.util.Tuple tuple) throws Exception + { + Feature e = new Feature(); + e.set(tuple); + return e; + } + +//1 + @javax.persistence.OneToMany(fetch=javax.persistence.FetchType.LAZY, mappedBy="feature"/*, cascade={javax.persistence.CascadeType.MERGE, javax.persistence.CascadeType.PERSIST, javax.persistence.CascadeType.REFRESH}*/) + private java.util.Collection featureCategoryCollection = new java.util.ArrayList(); + + @javax.xml.bind.annotation.XmlTransient + public java.util.Collection getFeatureCategoryCollection() + { + return featureCategoryCollection; + } + + public void setFeatureCategoryCollection(java.util.Collection collection) + { + for (org.molgenis.model.Category category : collection) { + category.setFeature(this); + } + featureCategoryCollection = collection; + } + + +} + diff --git a/generated/test/java/org/molgenis/model/Identifiable.java b/generated/test/java/org/molgenis/model/Identifiable.java new file mode 100644 index 000000000..200c01773 --- /dev/null +++ b/generated/test/java/org/molgenis/model/Identifiable.java @@ -0,0 +1,27 @@ + +/* File: org.molgenis/model/Identifiable.java + * Copyright: GBIC 2000-2012, all rights reserved + * Date: November 7, 2012 + * Generator: org.molgenis.generators.DataTypeGen 4.0.0-testing + * + * THIS FILE HAS BEEN GENERATED, PLEASE DO NOT EDIT! + */ + + +package org.molgenis.model; + +/** + * Identifiable: . + * @version November 7, 2012 + * @author MOLGENIS generator + */ +public interface Identifiable extends org.molgenis.model.Autoid +{ + public Integer getId(); + public void setId(Integer id); + public String getIdentifier(); + public void setIdentifier(String identifier); + public String getName(); + public void setName(String name); +} + diff --git a/test/java/org/molgenis/GeneratorTestCase.java b/test/java/org/molgenis/GeneratorTestCase.java new file mode 100644 index 000000000..6b649411e --- /dev/null +++ b/test/java/org/molgenis/GeneratorTestCase.java @@ -0,0 +1,147 @@ +package org.molgenis; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.tools.JavaCompiler; +import javax.tools.ToolProvider; + +import org.apache.log4j.Logger; +import org.molgenis.framework.db.DatabaseException; +import org.molgenis.generators.DataTypeGen; +import org.molgenis.generators.Generator; +import org.molgenis.model.MolgenisModel; +import org.molgenis.model.MolgenisModelException; +import org.molgenis.model.MolgenisModelParser; +import org.molgenis.model.MolgenisModelValidator; +import org.molgenis.model.elements.Entity; +import org.molgenis.model.elements.Model; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeSuite; + +public abstract class GeneratorTestCase +{ + private static final Logger LOG = Logger.getLogger(GeneratorTestCase.class); + + private static final String SRC_PATH = "generated/test/java/"; + private static final String BUILD_PATH = "build/test/classes/"; + + private static Model MODEL; + private static MolgenisOptions MODEL_OPTIONS; + + @BeforeSuite + public static void setUpBeforeSuite() throws Exception + { + // create test model + Model model = getModel(); + if (model == null || model.getModules() == null || model.getModules().isEmpty()) + { + throw new MolgenisModelException("model should contain at least one module"); + } + MODEL = model; + MODEL_OPTIONS = new MolgenisOptions(); + MODEL_OPTIONS.output_src = SRC_PATH; + + // generate model entities + List entityList = MODEL.getEntities(); + entityList = MolgenisModel.sortEntitiesByDependency(entityList, MODEL); + + DataTypeGen entityGenerator = new DataTypeGen(); + entityGenerator.generate(MODEL, MODEL_OPTIONS); + LOG.debug("generated model entities"); + System.out.println("generated model entities"); + + // compile model entities + List compileList = new ArrayList(entityList.size()); + char sep = File.separatorChar; + for (Entity entity : entityList) + { + String entityPath = entity.getModule().getName().replace('.', sep) + sep; + String generatedJavaFile = SRC_PATH + entityPath + entity.getName() + ".java"; + compileList.add(generatedJavaFile); + // compile(generatedJavaFile, BUILD_PATH); + // LOG.debug("compiled model entity: " + generatedJavaFile); + // System.out.println("compiled model entity: " + + // generatedJavaFile); + } + compile(compileList, BUILD_PATH); + } + + @BeforeClass + public void setUpBeforeClass() throws Exception + { + // generate using test model + String className = this.getClass().getName().replace('.', File.separatorChar).replace("Test", ""); + String generatedJavaFileName = SRC_PATH + className + ".java"; + + getGenerator().generate(MODEL, MODEL_OPTIONS, generatedJavaFileName); + LOG.debug("generated: " + generatedJavaFileName); + + // compile generated java file + compile(Arrays.asList(generatedJavaFileName), BUILD_PATH); + LOG.debug("compiled: " + generatedJavaFileName); + } + + protected abstract Generator getGenerator() throws MolgenisModelException; + + private static void compile(List javaPaths, String outputPath) throws IOException + { + // compile generate file + JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler(); + + File generatedClassFolder = new File(outputPath); + boolean created = generatedClassFolder.mkdirs(); + if (!created && !generatedClassFolder.exists()) + { + throw new IOException("could not create " + generatedClassFolder); + } + String classPath = System.getProperty("java.class.path") + ";src" + ";" + outputPath; + System.out.println(classPath); + + List compilerArgs = new ArrayList(); + compilerArgs.add("-cp"); + compilerArgs.add(classPath); + compilerArgs.add("-d"); + compilerArgs.add(generatedClassFolder.getPath()); + compilerArgs.add("-Xlint"); + compilerArgs.add("-g"); + compilerArgs.addAll(javaPaths); + + int returnCode = javaCompiler.run(null, null, null, compilerArgs.toArray(new String[0])); + if (returnCode != 0) throw new IOException("compilation failed: " + javaPaths); + } + + private static Model getModel() throws MolgenisModelException, DatabaseException + { + String xml = "\n" + + " \n" + + " Model to test generated code\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + " \n" + + " \n" + " \n" + + " \n" + ""; + + // TODO programmatically define model instead of parsing from XML + // generate model + Model model = MolgenisModelParser.parseDbSchema(xml); + // validate model & resolve model dependencies + MolgenisModelValidator.validate(model, new MolgenisOptions()); + return model; + } +} diff --git a/test/java/org/molgenis/generators/csv/CsvExportTest.java b/test/java/org/molgenis/generators/csv/CsvExportTest.java new file mode 100644 index 000000000..98992921b --- /dev/null +++ b/test/java/org/molgenis/generators/csv/CsvExportTest.java @@ -0,0 +1,46 @@ +package org.molgenis.generators.csv; + +import java.io.File; +import java.nio.charset.Charset; +import java.util.Arrays; + +import org.apache.commons.io.FileUtils; +import org.molgenis.GeneratorTestCase; +import org.molgenis.generators.Generator; +import org.molgenis.model.Feature; +import org.molgenis.model.MolgenisModelException; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class CsvExportTest extends GeneratorTestCase +{ + @Override + protected Generator getGenerator() throws MolgenisModelException + { + return new CsvExportGen(); + } + + @Test + public void testExportFeature() throws Exception + { + CsvExport csvExport = new CsvExport(); + File dir = new File(System.getProperty("java.io.tmpdir")); + + Feature feature1 = new Feature(); + feature1.setDataType("string"); + feature1.setName("feature1"); + + Feature feature2 = new Feature(); + feature2.setDataType("boolean"); + feature2.setName("feature2"); + + csvExport.exportAll(dir, Arrays.asList(feature1, feature2)); + + String csvFeatures = FileUtils.readFileToString( + new File(System.getProperty("java.io.tmpdir") + "/feature.txt"), Charset.forName("UTF-8")); + + String expected = "name\t__Type\tdataType\r\nfeature1\tFeature\tstring\r\nfeature2\tFeature\tboolean\r\n"; + + Assert.assertEquals(csvFeatures, expected); + } +} From 9c4ea4b2bf0250257b980f94abfccdef7adbdccf Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 7 Nov 2012 15:54:36 +0100 Subject: [PATCH 030/108] remove comments --- src/org/molgenis/generators/ForEachEntityGenerator.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/org/molgenis/generators/ForEachEntityGenerator.java b/src/org/molgenis/generators/ForEachEntityGenerator.java index 7a3ec1b51..fc2fa5e38 100644 --- a/src/org/molgenis/generators/ForEachEntityGenerator.java +++ b/src/org/molgenis/generators/ForEachEntityGenerator.java @@ -92,8 +92,6 @@ public void generate(Model model, MolgenisOptions options) throws Exception template.process(templateArgs, new OutputStreamWriter(targetOut, Charset.forName("UTF-8"))); targetOut.close(); - // logger.info("generated " + - // targetFile.getAbsolutePath()); logger.info("generated " + targetFile); } } From 0094dab93e60c9cec9d3a2343618a097c155ab4c Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 7 Nov 2012 15:55:13 +0100 Subject: [PATCH 031/108] remove comments --- test/java/org/molgenis/GeneratorTestCase.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/java/org/molgenis/GeneratorTestCase.java b/test/java/org/molgenis/GeneratorTestCase.java index 6b649411e..cd4cc2b18 100644 --- a/test/java/org/molgenis/GeneratorTestCase.java +++ b/test/java/org/molgenis/GeneratorTestCase.java @@ -52,7 +52,6 @@ public static void setUpBeforeSuite() throws Exception DataTypeGen entityGenerator = new DataTypeGen(); entityGenerator.generate(MODEL, MODEL_OPTIONS); LOG.debug("generated model entities"); - System.out.println("generated model entities"); // compile model entities List compileList = new ArrayList(entityList.size()); @@ -62,10 +61,6 @@ public static void setUpBeforeSuite() throws Exception String entityPath = entity.getModule().getName().replace('.', sep) + sep; String generatedJavaFile = SRC_PATH + entityPath + entity.getName() + ".java"; compileList.add(generatedJavaFile); - // compile(generatedJavaFile, BUILD_PATH); - // LOG.debug("compiled model entity: " + generatedJavaFile); - // System.out.println("compiled model entity: " + - // generatedJavaFile); } compile(compileList, BUILD_PATH); } From 3bb6e552322b5f345dd762d347aec8f8e73f36b1 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 7 Nov 2012 15:55:38 +0100 Subject: [PATCH 032/108] remove date/year references (keep file constant for same generator version) --- src/org/molgenis/generators/DataTypeGen.java.ftl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/org/molgenis/generators/DataTypeGen.java.ftl b/src/org/molgenis/generators/DataTypeGen.java.ftl index 906024e1e..5cd6ffcf4 100644 --- a/src/org/molgenis/generators/DataTypeGen.java.ftl +++ b/src/org/molgenis/generators/DataTypeGen.java.ftl @@ -7,8 +7,6 @@ <#-- ##--> <#--#####################################################################--> /* File: ${model.getName()}/model/${entity.getName()}.java - * Copyright: GBIC 2000-${year?c}, all rights reserved - * Date: ${date} * Generator: ${generator} ${version} * * THIS FILE HAS BEEN GENERATED, PLEASE DO NOT EDIT! From e1be02e348dd25a9e3a3ed60af1ee05370bd2745 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 7 Nov 2012 15:56:27 +0100 Subject: [PATCH 033/108] add generated test source path --- build.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.xml b/build.xml index bf839c5b7..5d1260294 100644 --- a/build.xml +++ b/build.xml @@ -6,6 +6,7 @@ + @@ -71,6 +72,7 @@ + From dbe60e6f6728a30faa508ac96e6c3e17a2d4b26a Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 7 Nov 2012 15:57:10 +0100 Subject: [PATCH 034/108] generated source without date --- generated/javaorg/molgenis/test/Autoid.java | 23 - generated/javaorg/molgenis/test/Category.java | 404 ------------ .../javaorg/molgenis/test/Characteristic.java | 578 ------------------ generated/javaorg/molgenis/test/Feature.java | 488 --------------- .../javaorg/molgenis/test/Identifiable.java | 27 - .../test/java/org/molgenis/model/Autoid.java | 2 - .../java/org/molgenis/model/Category.java | 2 - .../org/molgenis/model/Characteristic.java | 2 - .../test/java/org/molgenis/model/Feature.java | 2 - .../java/org/molgenis/model/Identifiable.java | 2 - 10 files changed, 1530 deletions(-) delete mode 100644 generated/javaorg/molgenis/test/Autoid.java delete mode 100644 generated/javaorg/molgenis/test/Category.java delete mode 100644 generated/javaorg/molgenis/test/Characteristic.java delete mode 100644 generated/javaorg/molgenis/test/Feature.java delete mode 100644 generated/javaorg/molgenis/test/Identifiable.java diff --git a/generated/javaorg/molgenis/test/Autoid.java b/generated/javaorg/molgenis/test/Autoid.java deleted file mode 100644 index 484e061cc..000000000 --- a/generated/javaorg/molgenis/test/Autoid.java +++ /dev/null @@ -1,23 +0,0 @@ - -/* File: org.molgenis/model/Autoid.java - * Copyright: GBIC 2000-2012, all rights reserved - * Date: November 6, 2012 - * Generator: org.molgenis.generators.DataTypeGen 4.0.0-testing - * - * THIS FILE HAS BEEN GENERATED, PLEASE DO NOT EDIT! - */ - - -package org.molgenis.test; - -/** - * Autoid: . - * @version November 6, 2012 - * @author MOLGENIS generator - */ -public interface Autoid extends org.molgenis.util.Entity -{ - public Integer getId(); - public void setId(Integer id); -} - diff --git a/generated/javaorg/molgenis/test/Category.java b/generated/javaorg/molgenis/test/Category.java deleted file mode 100644 index 09b65a3a7..000000000 --- a/generated/javaorg/molgenis/test/Category.java +++ /dev/null @@ -1,404 +0,0 @@ - -/* File: org.molgenis/model/Category.java - * Copyright: GBIC 2000-2012, all rights reserved - * Date: November 6, 2012 - * Generator: org.molgenis.generators.DataTypeGen 4.0.0-testing - * - * THIS FILE HAS BEEN GENERATED, PLEASE DO NOT EDIT! - */ - - -package org.molgenis.test; - -/** - * Category: . - * @version November 6, 2012 - * @author MOLGENIS generator - */ -@javax.persistence.Entity -//@org.hibernate.search.annotations.Indexed -@javax.persistence.Table(name = "Category" -) - - -@javax.xml.bind.annotation.XmlAccessorType(javax.xml.bind.annotation.XmlAccessType.FIELD) -//@EntityListeners({org.molgenis.test.db.CategoryEntityListener.class}) -public class Category extends org.molgenis.util.AbstractEntity implements org.molgenis.test.Autoid -{ - // fieldname constants - public final static String ID = "id"; - public final static String FEATURE = "feature"; - public final static String FEATURE_IDENTIFIER = "feature_Identifier"; - - //static methods - /** - * Shorthand for db.query(Category.class). - */ - public static org.molgenis.framework.db.Query query(org.molgenis.framework.db.Database db) - { - return db.query(Category.class); - } - - /** - * Shorthand for db.find(Category.class, org.molgenis.framework.db.QueryRule ... rules). - */ - public static java.util.List find(org.molgenis.framework.db.Database db, org.molgenis.framework.db.QueryRule ... rules) throws org.molgenis.framework.db.DatabaseException - { - return db.find(Category.class, rules); - } - - /** - * - */ - public static Category findById(org.molgenis.framework.db.Database db, Integer id) throws org.molgenis.framework.db.DatabaseException - { - org.molgenis.framework.db.Query q = db.query(Category.class); - q.eq(Category.ID, id); - java.util.List result = q.find(); - if(result.size()>0) return result.get(0); - else return null; - } - - - // member variables (including setters.getters for interface) - - - //id[type=int] - @javax.persistence.Id @javax.persistence.GeneratedValue(strategy = javax.persistence.GenerationType.AUTO) - @javax.persistence.Column(name="id", nullable=false) - @javax.xml.bind.annotation.XmlElement(name="id") - - //@javax.validation.constraints.NotNull - private Integer id = null; - - - //feature[type=xref] - @javax.persistence.ManyToOne(fetch=javax.persistence.FetchType.LAZY /*cascade={javax.persistence.CascadeType.MERGE, javax.persistence.CascadeType.PERSIST, javax.persistence.CascadeType.REFRESH}*/) - @javax.persistence.JoinColumn(name="feature", nullable=false) - - - - @javax.validation.constraints.NotNull - private org.molgenis.test.Feature feature = null; - @javax.persistence.Transient - private Integer feature_id = null; - @javax.persistence.Transient - private String feature_Identifier = null; - - //constructors - public Category() - { - - } - - //getters and setters - /** - * Get the id. - * @return id. - */ - public Integer getId() - { - return this.id; - } - - - /** - * Set the id. - * @param id - */ - public void setId( Integer id) - { - this.id = id; - } - - - - /** - * Get the feature. - * @return feature. - */ - public org.molgenis.test.Feature getFeature() - { - return this.feature; - } - - @Deprecated - public org.molgenis.test.Feature getFeature(org.molgenis.framework.db.Database db) - { - throw new UnsupportedOperationException(); - } - - /** - * Set the feature. - * @param feature - */ - public void setFeature( org.molgenis.test.Feature feature) - { - - this.feature = feature; - } - - - - /** - * Set foreign key for field feature. - * This will erase any foreign key objects currently set. - * FIXME: can we autoload the new object? - */ - public void setFeature_Id(Integer feature_id) - { - this.feature_id = feature_id; - } - - public void setFeature(Integer feature_id) - { - this.feature_id = feature_id; - } - - public Integer getFeature_Id() - { - - if(feature != null) - { - return feature.getId(); - } - else - { - return feature_id; - } - } - - /** - * Get a pretty label Identifier for cross reference Feature to Feature.Id. - */ - public String getFeature_Identifier() - { - //FIXME should we auto-load based on getFeature()? - if(feature != null) { - return feature.getIdentifier(); - } else { - return feature_Identifier; - } - } - - /** - * Set a pretty label for cross reference Feature to Feature.Id. - * Implies setFeature(null) until save - */ - public void setFeature_Identifier(String feature_Identifier) - { - this.feature_Identifier = feature_Identifier; - } - - - - - /** - * Generic getter. Get the property by using the name. - */ - public Object get(String name) - { - name = name.toLowerCase(); - if (name.toLowerCase().equals("id")) - return getId(); - if (name.toLowerCase().equals("feature")) - return getFeature(); - if(name.toLowerCase().equals("feature_id")) - return getFeature_Id(); - if(name.toLowerCase().equals("feature_identifier")) - return getFeature_Identifier(); - return ""; - } - - public void validate() throws org.molgenis.framework.db.DatabaseException - { - if(this.getId() == null) throw new org.molgenis.framework.db.DatabaseException("required field id is null"); - if(this.getFeature() == null) throw new org.molgenis.framework.db.DatabaseException("required field feature is null"); - } - - - - //@Implements - public void set( org.molgenis.util.Tuple tuple, boolean strict ) throws Exception - { - //optimization :-( - if(tuple instanceof org.molgenis.util.ResultSetTuple) - { - //set Id - this.setId(tuple.getInt("id")); - //set Feature - this.setFeature(tuple.getInt("feature")); - //set label Identifier for xref field Feature - this.setFeature_Identifier(tuple.getString("feature_Identifier")); - } - else if(tuple != null) - { - //set Id - if( strict || tuple.getInt("id") != null) this.setId(tuple.getInt("id")); - if( tuple.getInt("Category_id") != null) this.setId(tuple.getInt("Category_id")); - //set Feature - if( strict || tuple.getInt("feature_id") != null) this.setFeature(tuple.getInt("feature_id")); - if( tuple.getInt("Category_feature_id") != null) this.setFeature(tuple.getInt("Category_feature_id")); - //alias of xref - if( tuple.getObject("feature") != null) this.setFeature(tuple.getInt("feature")); - if( tuple.getObject("Category_feature") != null) this.setFeature(tuple.getInt("Category_feature")); - //set label for field Feature - if( strict || tuple.getObject("feature_Identifier") != null) this.setFeature_Identifier(tuple.getString("feature_Identifier")); - if( tuple.getObject("Category_feature_Identifier") != null ) this.setFeature_Identifier(tuple.getString("Category_feature_Identifier")); - } - //org.apache.log4j.Logger.getLogger("test").debug("set "+this); - } - - - - - - @Override - public String toString() - { - return this.toString(false); - } - - public String toString(boolean verbose) - { - String result = "Category("; - result+= "id='" + getId()+"' "; - result+= " feature_id='" + getFeature_Id()+"' "; - result+= " feature_identifier='" + getFeature_Identifier()+"' "; - result += ");"; - return result; - - } - - /** - * Get the names of all public properties of Category. - */ - public java.util.Vector getFields(boolean skipAutoIds) - { - java.util.Vector fields = new java.util.Vector(); - if(!skipAutoIds) - { - fields.add("id"); - } - { - fields.add("feature_id"); - } - fields.add("feature_identifier"); - return fields; - } - - public java.util.Vector getFields() - { - return getFields(false); - } - - @Override - public String getIdField() - { - return "id"; - } - - - - @Override - public java.util.List getLabelFields() - { - java.util.List result = new java.util.ArrayList(); - result.add("id"); - return result; - } - - @Deprecated - public String getFields(String sep) - { - return ("" - + "id" +sep - + "feature" - ); - } - - @Override - public Object getIdValue() - { - return get(getIdField()); - } - - - public String getXrefIdFieldName(String fieldName) { - if (fieldName.equalsIgnoreCase("feature")) { - return "id"; - } - - return null; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { return false; } - if (obj == this) { return true; } - if (obj.getClass() != getClass()) { - return false; - } - Category rhs = (Category) obj; - return new org.apache.commons.lang.builder.EqualsBuilder() - .isEquals(); - } - - @Override - public int hashCode() { - int firstNumber = this.getClass().getName().hashCode(); - int secondNumber = this.getClass().getSimpleName().hashCode(); - if(firstNumber % 2 == 0) { - firstNumber += 1; - } - if(secondNumber % 2 == 0) { - secondNumber += 1; - } - - return new org.apache.commons.lang.builder.HashCodeBuilder(firstNumber, secondNumber) - .toHashCode(); - } - - - - @Deprecated - public String getValues(String sep) - { - java.io.StringWriter out = new java.io.StringWriter(); - { - Object valueO = getId(); - String valueS; - if (valueO != null) - valueS = valueO.toString(); - else - valueS = ""; - valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); - valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); - out.write(valueS+sep); - } - { - Object valueO = getFeature(); - String valueS; - if (valueO != null) - valueS = valueO.toString(); - else - valueS = ""; - valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); - valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); - out.write(valueS); - } - return out.toString(); - } - - @Override - public Category create(org.molgenis.util.Tuple tuple) throws Exception - { - Category e = new Category(); - e.set(tuple); - return e; - } - - - -} - diff --git a/generated/javaorg/molgenis/test/Characteristic.java b/generated/javaorg/molgenis/test/Characteristic.java deleted file mode 100644 index 7edd52995..000000000 --- a/generated/javaorg/molgenis/test/Characteristic.java +++ /dev/null @@ -1,578 +0,0 @@ - -/* File: org.molgenis/model/Characteristic.java - * Copyright: GBIC 2000-2012, all rights reserved - * Date: November 6, 2012 - * Generator: org.molgenis.generators.DataTypeGen 4.0.0-testing - * - * THIS FILE HAS BEEN GENERATED, PLEASE DO NOT EDIT! - */ - - -package org.molgenis.test; - -/** - * Characteristic: . - * @version November 6, 2012 - * @author MOLGENIS generator - */ -@javax.persistence.Entity -//@org.hibernate.search.annotations.Indexed -@javax.persistence.Table(name = "Characteristic", uniqueConstraints={ @javax.persistence.UniqueConstraint( columnNames={ "Identifier" }), @javax.persistence.UniqueConstraint( columnNames={ "Name", "Identifier" } ) } -) - - -@javax.persistence.Inheritance(strategy=javax.persistence.InheritanceType.JOINED) -@javax.persistence.DiscriminatorColumn(name="DType", discriminatorType=javax.persistence.DiscriminatorType.STRING) -@javax.xml.bind.annotation.XmlAccessorType(javax.xml.bind.annotation.XmlAccessType.FIELD) -//@EntityListeners({org.molgenis.test.db.CharacteristicEntityListener.class}) -public class Characteristic extends org.molgenis.util.AbstractEntity implements org.molgenis.test.Identifiable -{ - // fieldname constants - public final static String ID = "id"; - public final static String IDENTIFIER = "Identifier"; - public final static String NAME = "Name"; - public final static String __TYPE = "__Type"; - public final static String DESCRIPTION = "description"; - - //static methods - /** - * Shorthand for db.query(Characteristic.class). - */ - public static org.molgenis.framework.db.Query query(org.molgenis.framework.db.Database db) - { - return db.query(Characteristic.class); - } - - /** - * Shorthand for db.find(Characteristic.class, org.molgenis.framework.db.QueryRule ... rules). - */ - public static java.util.List find(org.molgenis.framework.db.Database db, org.molgenis.framework.db.QueryRule ... rules) throws org.molgenis.framework.db.DatabaseException - { - return db.find(Characteristic.class, rules); - } - - /** - * - */ - public static Characteristic findById(org.molgenis.framework.db.Database db, Integer id) throws org.molgenis.framework.db.DatabaseException - { - org.molgenis.framework.db.Query q = db.query(Characteristic.class); - q.eq(Characteristic.ID, id); - java.util.List result = q.find(); - if(result.size()>0) return result.get(0); - else return null; - } - - /** - * - */ - public static Characteristic findByIdentifier(org.molgenis.framework.db.Database db, String identifier) throws org.molgenis.framework.db.DatabaseException - { - org.molgenis.framework.db.Query q = db.query(Characteristic.class); - q.eq(Characteristic.IDENTIFIER, identifier); - java.util.List result = q.find(); - if(result.size()>0) return result.get(0); - else return null; - } - - /** - * - */ - public static Characteristic findByNameIdentifier(org.molgenis.framework.db.Database db, String name, String identifier) throws org.molgenis.framework.db.DatabaseException - { - org.molgenis.framework.db.Query q = db.query(Characteristic.class); - q.eq(Characteristic.NAME, name);q.eq(Characteristic.IDENTIFIER, identifier); - java.util.List result = q.find(); - if(result.size()>0) return result.get(0); - else return null; - } - - - // member variables (including setters.getters for interface) - - - //id[type=int] - @javax.persistence.Id @javax.persistence.GeneratedValue(strategy = javax.persistence.GenerationType.AUTO) - @javax.persistence.Column(name="id", nullable=false) - @javax.xml.bind.annotation.XmlElement(name="id") - - //@javax.validation.constraints.NotNull - private Integer id = null; - - - //Identifier[type=string] -// @org.hibernate.search.annotations.Field(index=org.hibernate.search.annotations.Index.TOKENIZED, store=org.hibernate.search.annotations.Store.NO) - @javax.persistence.Column(name="Identifier", nullable=false) - @javax.xml.bind.annotation.XmlElement(name="identifier") - - - - @javax.validation.constraints.NotNull - private String identifier = null; - - - //assign name[type=string] -// @org.hibernate.search.annotations.Field(index=org.hibernate.search.annotations.Index.TOKENIZED, store=org.hibernate.search.annotations.Store.NO) - @javax.persistence.Column(name="Name", nullable=false) - @javax.xml.bind.annotation.XmlElement(name="name") - - - - @javax.validation.constraints.NotNull - private String name = null; - - - //Subtypes have to be set to allow searching[type=enum] - @javax.persistence.Column(name="DType", nullable=false) - @javax.xml.bind.annotation.XmlElement(name="__Type") - - - - @javax.validation.constraints.NotNull - private String __Type = null; - @javax.persistence.Transient - private String __Type_label = null; - @javax.persistence.Transient - private java.util.List __Type_options = new java.util.ArrayList(); - - - //description[type=text] -// @javax.persistence.Lob() - @javax.persistence.Column(name="description", length=16777216) - - - - private String description = null; - - //constructors - public Characteristic() - { - //set the type for a new instance - set__Type(this.getClass().getSimpleName()); - - //options for enum __Type - __Type_options.add(new org.molgenis.util.ValueLabel("Characteristic","Characteristic")); - __Type_options.add(new org.molgenis.util.ValueLabel("Feature","Feature")); - } - - //getters and setters - /** - * Get the id. - * @return id. - */ - public Integer getId() - { - return this.id; - } - - - /** - * Set the id. - * @param id - */ - public void setId( Integer id) - { - this.id = id; - } - - - - /** - * Get the Identifier. - * @return identifier. - */ - public String getIdentifier() - { - return this.identifier; - } - - @Deprecated - public String getIdentifier(org.molgenis.framework.db.Database db) - { - throw new UnsupportedOperationException(); - } - - /** - * Set the Identifier. - * @param identifier - */ - public void setIdentifier( String identifier) - { - - this.identifier = identifier; - } - - - - /** - * Get the assign name. - * @return name. - */ - public String getName() - { - return this.name; - } - - @Deprecated - public String getName(org.molgenis.framework.db.Database db) - { - throw new UnsupportedOperationException(); - } - - /** - * Set the assign name. - * @param name - */ - public void setName( String name) - { - - this.name = name; - } - - - - /** - * Get the Subtypes have to be set to allow searching. - * @return __Type. - */ - public String get__Type() - { - return this.__Type; - } - - @Deprecated - public String get__Type(org.molgenis.framework.db.Database db) - { - throw new UnsupportedOperationException(); - } - - /** - * Set the Subtypes have to be set to allow searching. - * @param __Type - */ - public void set__Type( String __Type) - { - - this.__Type = __Type; - } - - - /** - * Get tha label for enum __Type. - */ - public String get__TypeLabel() - { - return this.__Type_label; - } - - /** - * __Type is enum. This method returns all available enum options. - */ - public java.util.List get__TypeOptions() - { - return __Type_options; - } - - - /** - * Get the description. - * @return description. - */ - public String getDescription() - { - return this.description; - } - - @Deprecated - public String getDescription(org.molgenis.framework.db.Database db) - { - throw new UnsupportedOperationException(); - } - - /** - * Set the description. - * @param description - */ - public void setDescription( String description) - { - - this.description = description; - } - - - - - /** - * Generic getter. Get the property by using the name. - */ - public Object get(String name) - { - name = name.toLowerCase(); - if (name.toLowerCase().equals("id")) - return getId(); - if (name.toLowerCase().equals("identifier")) - return getIdentifier(); - if (name.toLowerCase().equals("name")) - return getName(); - if (name.toLowerCase().equals("__type")) - return get__Type(); - if(name.toLowerCase().equals("__type_label")) - return get__TypeLabel(); - if (name.toLowerCase().equals("description")) - return getDescription(); - return ""; - } - - public void validate() throws org.molgenis.framework.db.DatabaseException - { - if(this.getId() == null) throw new org.molgenis.framework.db.DatabaseException("required field id is null"); - if(this.getIdentifier() == null) throw new org.molgenis.framework.db.DatabaseException("required field identifier is null"); - if(this.getName() == null) throw new org.molgenis.framework.db.DatabaseException("required field name is null"); - if(this.get__Type() == null) throw new org.molgenis.framework.db.DatabaseException("required field __Type is null"); - } - - - - //@Implements - public void set( org.molgenis.util.Tuple tuple, boolean strict ) throws Exception - { - //optimization :-( - if(tuple instanceof org.molgenis.util.ResultSetTuple) - { - //set Id - this.setId(tuple.getInt("id")); - //set Identifier - this.setIdentifier(tuple.getString("Identifier")); - //set Name - this.setName(tuple.getString("Name")); - //set __Type - this.set__Type(tuple.getString("__Type")); - //set Description - this.setDescription(tuple.getString("description")); - } - else if(tuple != null) - { - //set Id - if( strict || tuple.getInt("id") != null) this.setId(tuple.getInt("id")); - if( tuple.getInt("Characteristic_id") != null) this.setId(tuple.getInt("Characteristic_id")); - //set Identifier - if( strict || tuple.getString("Identifier") != null) this.setIdentifier(tuple.getString("Identifier")); - if( tuple.getString("Characteristic_Identifier") != null) this.setIdentifier(tuple.getString("Characteristic_Identifier")); - //set Name - if( strict || tuple.getString("Name") != null) this.setName(tuple.getString("Name")); - if( tuple.getString("Characteristic_Name") != null) this.setName(tuple.getString("Characteristic_Name")); - //set __Type - if( strict || tuple.getString("__Type") != null) this.set__Type(tuple.getString("__Type")); - if( tuple.getString("Characteristic___Type") != null) this.set__Type(tuple.getString("Characteristic___Type")); - //set Description - if( strict || tuple.getString("description") != null) this.setDescription(tuple.getString("description")); - if( tuple.getString("Characteristic_description") != null) this.setDescription(tuple.getString("Characteristic_description")); - } - //org.apache.log4j.Logger.getLogger("test").debug("set "+this); - } - - - - - - @Override - public String toString() - { - return this.toString(false); - } - - public String toString(boolean verbose) - { - String result = "Characteristic("; - result+= "id='" + getId()+"' "; - result+= "identifier='" + getIdentifier()+"' "; - result+= "name='" + getName()+"' "; - result+= "__Type='" + get__Type()+"' "; - result+= "description='" + getDescription()+"'"; - result += ");"; - return result; - - } - - /** - * Get the names of all public properties of Characteristic. - */ - public java.util.Vector getFields(boolean skipAutoIds) - { - java.util.Vector fields = new java.util.Vector(); - if(!skipAutoIds) - { - fields.add("id"); - } - { - fields.add("identifier"); - } - { - fields.add("name"); - } - { - fields.add("__Type"); - } - { - fields.add("description"); - } - return fields; - } - - public java.util.Vector getFields() - { - return getFields(false); - } - - @Override - public String getIdField() - { - return "id"; - } - - - - @Override - public java.util.List getLabelFields() - { - java.util.List result = new java.util.ArrayList(); - result.add("Identifier"); - return result; - } - - @Deprecated - public String getFields(String sep) - { - return ("" - + "id" +sep - + "identifier" +sep - + "name" +sep - + "__Type" +sep - + "description" - ); - } - - @Override - public Object getIdValue() - { - return get(getIdField()); - } - - - public String getXrefIdFieldName(String fieldName) { - - return null; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { return false; } - if (obj == this) { return true; } - if (obj.getClass() != getClass()) { - return false; - } - Characteristic rhs = (Characteristic) obj; - return new org.apache.commons.lang.builder.EqualsBuilder() - //identifier - .append(identifier, rhs.getIdentifier()) - //name - .append(name, rhs.getName()) - //identifier - .append(identifier, rhs.getIdentifier()) - .isEquals(); - } - - @Override - public int hashCode() { - int firstNumber = this.getClass().getName().hashCode(); - int secondNumber = this.getClass().getSimpleName().hashCode(); - if(firstNumber % 2 == 0) { - firstNumber += 1; - } - if(secondNumber % 2 == 0) { - secondNumber += 1; - } - - return new org.apache.commons.lang.builder.HashCodeBuilder(firstNumber, secondNumber) - .append(identifier) - .append(name) - .append(identifier) - .toHashCode(); - } - - - - @Deprecated - public String getValues(String sep) - { - java.io.StringWriter out = new java.io.StringWriter(); - { - Object valueO = getId(); - String valueS; - if (valueO != null) - valueS = valueO.toString(); - else - valueS = ""; - valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); - valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); - out.write(valueS+sep); - } - { - Object valueO = getIdentifier(); - String valueS; - if (valueO != null) - valueS = valueO.toString(); - else - valueS = ""; - valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); - valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); - out.write(valueS+sep); - } - { - Object valueO = getName(); - String valueS; - if (valueO != null) - valueS = valueO.toString(); - else - valueS = ""; - valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); - valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); - out.write(valueS+sep); - } - { - Object valueO = get__Type(); - String valueS; - if (valueO != null) - valueS = valueO.toString(); - else - valueS = ""; - valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); - valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); - out.write(valueS+sep); - } - { - Object valueO = getDescription(); - String valueS; - if (valueO != null) - valueS = valueO.toString(); - else - valueS = ""; - valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); - valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); - out.write(valueS); - } - return out.toString(); - } - - @Override - public Characteristic create(org.molgenis.util.Tuple tuple) throws Exception - { - Characteristic e = new Characteristic(); - e.set(tuple); - return e; - } - - - -} - diff --git a/generated/javaorg/molgenis/test/Feature.java b/generated/javaorg/molgenis/test/Feature.java deleted file mode 100644 index 244ded4db..000000000 --- a/generated/javaorg/molgenis/test/Feature.java +++ /dev/null @@ -1,488 +0,0 @@ - -/* File: org.molgenis/model/Feature.java - * Copyright: GBIC 2000-2012, all rights reserved - * Date: November 6, 2012 - * Generator: org.molgenis.generators.DataTypeGen 4.0.0-testing - * - * THIS FILE HAS BEEN GENERATED, PLEASE DO NOT EDIT! - */ - - -package org.molgenis.test; - -/** - * Feature: . - * @version November 6, 2012 - * @author MOLGENIS generator - */ -@javax.persistence.Entity -//@org.hibernate.search.annotations.Indexed -@javax.persistence.Table(name = "Feature" -) - - -@javax.xml.bind.annotation.XmlAccessorType(javax.xml.bind.annotation.XmlAccessType.FIELD) -//@EntityListeners({org.molgenis.test.db.FeatureEntityListener.class}) -public class Feature extends org.molgenis.test.Characteristic -{ - // fieldname constants - public final static String DATATYPE = "dataType"; - public final static String ID = "id"; - - //static methods - /** - * Shorthand for db.query(Feature.class). - */ - public static org.molgenis.framework.db.Query query(org.molgenis.framework.db.Database db) - { - return db.query(Feature.class); - } - - /** - * Shorthand for db.find(Feature.class, org.molgenis.framework.db.QueryRule ... rules). - */ - public static java.util.List find(org.molgenis.framework.db.Database db, org.molgenis.framework.db.QueryRule ... rules) throws org.molgenis.framework.db.DatabaseException - { - return db.find(Feature.class, rules); - } - - /** - * - */ - public static Feature findById(org.molgenis.framework.db.Database db, Integer id) throws org.molgenis.framework.db.DatabaseException - { - org.molgenis.framework.db.Query q = db.query(Feature.class); - q.eq(Feature.ID, id); - java.util.List result = q.find(); - if(result.size()>0) return result.get(0); - else return null; - } - - /** - * - */ - public static Feature findByIdentifier(org.molgenis.framework.db.Database db, String identifier) throws org.molgenis.framework.db.DatabaseException - { - org.molgenis.framework.db.Query q = db.query(Feature.class); - q.eq(Feature.IDENTIFIER, identifier); - java.util.List result = q.find(); - if(result.size()>0) return result.get(0); - else return null; - } - - /** - * - */ - public static Feature findByNameIdentifier(org.molgenis.framework.db.Database db, String name, String identifier) throws org.molgenis.framework.db.DatabaseException - { - org.molgenis.framework.db.Query q = db.query(Feature.class); - q.eq(Feature.NAME, name);q.eq(Feature.IDENTIFIER, identifier); - java.util.List result = q.find(); - if(result.size()>0) return result.get(0); - else return null; - } - - - // member variables (including setters.getters for interface) - - - //dataType[type=enum] - @javax.persistence.Column(name="dataType", nullable=false) - @javax.xml.bind.annotation.XmlElement(name="dataType") - - - - @javax.validation.constraints.NotNull - private String dataType = "string"; - @javax.persistence.Transient - private String dataType_label = null; - @javax.persistence.Transient - private java.util.List dataType_options = new java.util.ArrayList(); - - - //id[type=int] - - - //constructors - public Feature() - { - //set the type for a new instance - set__Type(this.getClass().getSimpleName()); - - //options for enum DataType - dataType_options.add(new org.molgenis.util.ValueLabel("xref","xref")); - dataType_options.add(new org.molgenis.util.ValueLabel("string","string")); - dataType_options.add(new org.molgenis.util.ValueLabel("nominal","nominal")); - dataType_options.add(new org.molgenis.util.ValueLabel("ordinal","ordinal")); - dataType_options.add(new org.molgenis.util.ValueLabel("date","date")); - dataType_options.add(new org.molgenis.util.ValueLabel("datetime","datetime")); - dataType_options.add(new org.molgenis.util.ValueLabel("int","int")); - dataType_options.add(new org.molgenis.util.ValueLabel("code","code")); - dataType_options.add(new org.molgenis.util.ValueLabel("image","image")); - dataType_options.add(new org.molgenis.util.ValueLabel("decimal","decimal")); - dataType_options.add(new org.molgenis.util.ValueLabel("bool","bool")); - dataType_options.add(new org.molgenis.util.ValueLabel("file","file")); - dataType_options.add(new org.molgenis.util.ValueLabel("log","log")); - dataType_options.add(new org.molgenis.util.ValueLabel("data","data")); - dataType_options.add(new org.molgenis.util.ValueLabel("exe","exe")); - } - - //getters and setters - /** - * Get the dataType. - * @return dataType. - */ - public String getDataType() - { - return this.dataType; - } - - @Deprecated - public String getDataType(org.molgenis.framework.db.Database db) - { - throw new UnsupportedOperationException(); - } - - /** - * Set the dataType. - * @param dataType - */ - public void setDataType( String dataType) - { - - this.dataType = dataType; - } - - - /** - * Get tha label for enum DataType. - */ - public String getDataTypeLabel() - { - return this.dataType_label; - } - - /** - * DataType is enum. This method returns all available enum options. - */ - public java.util.List getDataTypeOptions() - { - return dataType_options; - } - - - - - - - - /** - * Generic getter. Get the property by using the name. - */ - public Object get(String name) - { - name = name.toLowerCase(); - if (name.toLowerCase().equals("id")) - return getId(); - if (name.toLowerCase().equals("identifier")) - return getIdentifier(); - if (name.toLowerCase().equals("name")) - return getName(); - if (name.toLowerCase().equals("__type")) - return get__Type(); - if(name.toLowerCase().equals("__type_label")) - return get__TypeLabel(); - if (name.toLowerCase().equals("description")) - return getDescription(); - if (name.toLowerCase().equals("datatype")) - return getDataType(); - if(name.toLowerCase().equals("datatype_label")) - return getDataTypeLabel(); - return ""; - } - - public void validate() throws org.molgenis.framework.db.DatabaseException - { - if(this.getId() == null) throw new org.molgenis.framework.db.DatabaseException("required field id is null"); - if(this.getIdentifier() == null) throw new org.molgenis.framework.db.DatabaseException("required field identifier is null"); - if(this.getName() == null) throw new org.molgenis.framework.db.DatabaseException("required field name is null"); - if(this.get__Type() == null) throw new org.molgenis.framework.db.DatabaseException("required field __Type is null"); - if(this.getDataType() == null) throw new org.molgenis.framework.db.DatabaseException("required field dataType is null"); - } - - - - //@Implements - public void set( org.molgenis.util.Tuple tuple, boolean strict ) throws Exception - { - //optimization :-( - if(tuple instanceof org.molgenis.util.ResultSetTuple) - { - //set Id - this.setId(tuple.getInt("id")); - //set Identifier - this.setIdentifier(tuple.getString("Identifier")); - //set Name - this.setName(tuple.getString("Name")); - //set __Type - this.set__Type(tuple.getString("__Type")); - //set Description - this.setDescription(tuple.getString("description")); - //set DataType - this.setDataType(tuple.getString("dataType")); - } - else if(tuple != null) - { - //set Id - if( strict || tuple.getInt("id") != null) this.setId(tuple.getInt("id")); - if( tuple.getInt("Feature_id") != null) this.setId(tuple.getInt("Feature_id")); - //set Identifier - if( strict || tuple.getString("Identifier") != null) this.setIdentifier(tuple.getString("Identifier")); - if( tuple.getString("Feature_Identifier") != null) this.setIdentifier(tuple.getString("Feature_Identifier")); - //set Name - if( strict || tuple.getString("Name") != null) this.setName(tuple.getString("Name")); - if( tuple.getString("Feature_Name") != null) this.setName(tuple.getString("Feature_Name")); - //set __Type - if( strict || tuple.getString("__Type") != null) this.set__Type(tuple.getString("__Type")); - if( tuple.getString("Feature___Type") != null) this.set__Type(tuple.getString("Feature___Type")); - //set Description - if( strict || tuple.getString("description") != null) this.setDescription(tuple.getString("description")); - if( tuple.getString("Feature_description") != null) this.setDescription(tuple.getString("Feature_description")); - //set DataType - if( strict || tuple.getString("dataType") != null) this.setDataType(tuple.getString("dataType")); - if( tuple.getString("Feature_dataType") != null) this.setDataType(tuple.getString("Feature_dataType")); - } - //org.apache.log4j.Logger.getLogger("test").debug("set "+this); - } - - - - - - @Override - public String toString() - { - return this.toString(false); - } - - public String toString(boolean verbose) - { - String result = "Feature("; - result+= "id='" + getId()+"' "; - result+= "identifier='" + getIdentifier()+"' "; - result+= "name='" + getName()+"' "; - result+= "__Type='" + get__Type()+"' "; - result+= "description='" + getDescription()+"' "; - result+= "dataType='" + getDataType()+"'"; - result += ");"; - return result; - - } - - /** - * Get the names of all public properties of Feature. - */ - public java.util.Vector getFields(boolean skipAutoIds) - { - java.util.Vector fields = new java.util.Vector(); - if(!skipAutoIds) - { - fields.add("id"); - } - { - fields.add("identifier"); - } - { - fields.add("name"); - } - { - fields.add("__Type"); - } - { - fields.add("description"); - } - { - fields.add("dataType"); - } - return fields; - } - - public java.util.Vector getFields() - { - return getFields(false); - } - - @Override - public String getIdField() - { - return "id"; - } - - - - @Override - public java.util.List getLabelFields() - { - java.util.List result = new java.util.ArrayList(); - result.add("Identifier"); - return result; - } - - @Deprecated - public String getFields(String sep) - { - return ("" - + "id" +sep - + "identifier" +sep - + "name" +sep - + "__Type" +sep - + "description" +sep - + "dataType" - ); - } - - @Override - public Object getIdValue() - { - return get(getIdField()); - } - - - public String getXrefIdFieldName(String fieldName) { - - return null; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { return false; } - if (obj == this) { return true; } - if (obj.getClass() != getClass()) { - return false; - } - Feature rhs = (Feature) obj; - return new org.apache.commons.lang.builder.EqualsBuilder() - .appendSuper(super.equals(obj)) - .isEquals(); - } - - @Override - public int hashCode() { - int firstNumber = this.getClass().getName().hashCode(); - int secondNumber = this.getClass().getSimpleName().hashCode(); - if(firstNumber % 2 == 0) { - firstNumber += 1; - } - if(secondNumber % 2 == 0) { - secondNumber += 1; - } - - return new org.apache.commons.lang.builder.HashCodeBuilder(firstNumber, secondNumber) - .appendSuper(super.hashCode()) - .toHashCode(); - } - - - - @Deprecated - public String getValues(String sep) - { - java.io.StringWriter out = new java.io.StringWriter(); - { - Object valueO = getId(); - String valueS; - if (valueO != null) - valueS = valueO.toString(); - else - valueS = ""; - valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); - valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); - out.write(valueS+sep); - } - { - Object valueO = getIdentifier(); - String valueS; - if (valueO != null) - valueS = valueO.toString(); - else - valueS = ""; - valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); - valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); - out.write(valueS+sep); - } - { - Object valueO = getName(); - String valueS; - if (valueO != null) - valueS = valueO.toString(); - else - valueS = ""; - valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); - valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); - out.write(valueS+sep); - } - { - Object valueO = get__Type(); - String valueS; - if (valueO != null) - valueS = valueO.toString(); - else - valueS = ""; - valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); - valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); - out.write(valueS+sep); - } - { - Object valueO = getDescription(); - String valueS; - if (valueO != null) - valueS = valueO.toString(); - else - valueS = ""; - valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); - valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); - out.write(valueS+sep); - } - { - Object valueO = getDataType(); - String valueS; - if (valueO != null) - valueS = valueO.toString(); - else - valueS = ""; - valueS = valueS.replaceAll("\r\n"," ").replaceAll("\n"," ").replaceAll("\r"," "); - valueS = valueS.replaceAll("\t"," ").replaceAll(sep," "); - out.write(valueS); - } - return out.toString(); - } - - @Override - public Feature create(org.molgenis.util.Tuple tuple) throws Exception - { - Feature e = new Feature(); - e.set(tuple); - return e; - } - -//1 - @javax.persistence.OneToMany(fetch=javax.persistence.FetchType.LAZY, mappedBy="feature"/*, cascade={javax.persistence.CascadeType.MERGE, javax.persistence.CascadeType.PERSIST, javax.persistence.CascadeType.REFRESH}*/) - private java.util.Collection featureCategoryCollection = new java.util.ArrayList(); - - @javax.xml.bind.annotation.XmlTransient - public java.util.Collection getFeatureCategoryCollection() - { - return featureCategoryCollection; - } - - public void setFeatureCategoryCollection(java.util.Collection collection) - { - for (org.molgenis.test.Category category : collection) { - category.setFeature(this); - } - featureCategoryCollection = collection; - } - - -} - diff --git a/generated/javaorg/molgenis/test/Identifiable.java b/generated/javaorg/molgenis/test/Identifiable.java deleted file mode 100644 index 13da6c17f..000000000 --- a/generated/javaorg/molgenis/test/Identifiable.java +++ /dev/null @@ -1,27 +0,0 @@ - -/* File: org.molgenis/model/Identifiable.java - * Copyright: GBIC 2000-2012, all rights reserved - * Date: November 6, 2012 - * Generator: org.molgenis.generators.DataTypeGen 4.0.0-testing - * - * THIS FILE HAS BEEN GENERATED, PLEASE DO NOT EDIT! - */ - - -package org.molgenis.test; - -/** - * Identifiable: . - * @version November 6, 2012 - * @author MOLGENIS generator - */ -public interface Identifiable extends org.molgenis.test.Autoid -{ - public Integer getId(); - public void setId(Integer id); - public String getIdentifier(); - public void setIdentifier(String identifier); - public String getName(); - public void setName(String name); -} - diff --git a/generated/test/java/org/molgenis/model/Autoid.java b/generated/test/java/org/molgenis/model/Autoid.java index 7e560aaef..67d7ec1bf 100644 --- a/generated/test/java/org/molgenis/model/Autoid.java +++ b/generated/test/java/org/molgenis/model/Autoid.java @@ -1,7 +1,5 @@ /* File: org.molgenis/model/Autoid.java - * Copyright: GBIC 2000-2012, all rights reserved - * Date: November 7, 2012 * Generator: org.molgenis.generators.DataTypeGen 4.0.0-testing * * THIS FILE HAS BEEN GENERATED, PLEASE DO NOT EDIT! diff --git a/generated/test/java/org/molgenis/model/Category.java b/generated/test/java/org/molgenis/model/Category.java index d569b6574..fe2750808 100644 --- a/generated/test/java/org/molgenis/model/Category.java +++ b/generated/test/java/org/molgenis/model/Category.java @@ -1,7 +1,5 @@ /* File: org.molgenis/model/Category.java - * Copyright: GBIC 2000-2012, all rights reserved - * Date: November 7, 2012 * Generator: org.molgenis.generators.DataTypeGen 4.0.0-testing * * THIS FILE HAS BEEN GENERATED, PLEASE DO NOT EDIT! diff --git a/generated/test/java/org/molgenis/model/Characteristic.java b/generated/test/java/org/molgenis/model/Characteristic.java index 979e52880..c38f5b2d3 100644 --- a/generated/test/java/org/molgenis/model/Characteristic.java +++ b/generated/test/java/org/molgenis/model/Characteristic.java @@ -1,7 +1,5 @@ /* File: org.molgenis/model/Characteristic.java - * Copyright: GBIC 2000-2012, all rights reserved - * Date: November 7, 2012 * Generator: org.molgenis.generators.DataTypeGen 4.0.0-testing * * THIS FILE HAS BEEN GENERATED, PLEASE DO NOT EDIT! diff --git a/generated/test/java/org/molgenis/model/Feature.java b/generated/test/java/org/molgenis/model/Feature.java index f7519b2b3..79098bf63 100644 --- a/generated/test/java/org/molgenis/model/Feature.java +++ b/generated/test/java/org/molgenis/model/Feature.java @@ -1,7 +1,5 @@ /* File: org.molgenis/model/Feature.java - * Copyright: GBIC 2000-2012, all rights reserved - * Date: November 7, 2012 * Generator: org.molgenis.generators.DataTypeGen 4.0.0-testing * * THIS FILE HAS BEEN GENERATED, PLEASE DO NOT EDIT! diff --git a/generated/test/java/org/molgenis/model/Identifiable.java b/generated/test/java/org/molgenis/model/Identifiable.java index 200c01773..e42241dfb 100644 --- a/generated/test/java/org/molgenis/model/Identifiable.java +++ b/generated/test/java/org/molgenis/model/Identifiable.java @@ -1,7 +1,5 @@ /* File: org.molgenis/model/Identifiable.java - * Copyright: GBIC 2000-2012, all rights reserved - * Date: November 7, 2012 * Generator: org.molgenis.generators.DataTypeGen 4.0.0-testing * * THIS FILE HAS BEEN GENERATED, PLEASE DO NOT EDIT! From a9f844356c083c7f85c316fce45c9f1c7b46fb03 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 7 Nov 2012 16:27:51 +0100 Subject: [PATCH 035/108] fix testng target (added generated source dir) --- build.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.xml b/build.xml index 5d1260294..73c9f2f38 100644 --- a/build.xml +++ b/build.xml @@ -63,7 +63,7 @@ - + From 5789a9c8d2c3b0acc329ed91aa257b039b4bf347 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Wed, 7 Nov 2012 16:53:16 +0100 Subject: [PATCH 036/108] change test so that is succeeds on unix machines (but not windows) --- test/java/org/molgenis/generators/csv/CsvExportTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/java/org/molgenis/generators/csv/CsvExportTest.java b/test/java/org/molgenis/generators/csv/CsvExportTest.java index 98992921b..f92815b58 100644 --- a/test/java/org/molgenis/generators/csv/CsvExportTest.java +++ b/test/java/org/molgenis/generators/csv/CsvExportTest.java @@ -39,7 +39,7 @@ public void testExportFeature() throws Exception String csvFeatures = FileUtils.readFileToString( new File(System.getProperty("java.io.tmpdir") + "/feature.txt"), Charset.forName("UTF-8")); - String expected = "name\t__Type\tdataType\r\nfeature1\tFeature\tstring\r\nfeature2\tFeature\tboolean\r\n"; + String expected = "name\t__Type\tdataType\nfeature1\tFeature\tstring\nfeature2\tFeature\tboolean\n"; Assert.assertEquals(csvFeatures, expected); } From 802afbb61cbc0b01fc4d64996c546b2eacb23fcd Mon Sep 17 00:00:00 2001 From: rw42 Date: Wed, 7 Nov 2012 17:53:31 +0100 Subject: [PATCH 037/108] Added generated/test/java as test src folder. --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 064740827..34892b36f 100644 --- a/pom.xml +++ b/pom.xml @@ -284,6 +284,7 @@ src + generated/test/java From 5643c99473e5582e3a3494b7ccc4ec9a4fe00703 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Thu, 8 Nov 2012 11:10:49 +0100 Subject: [PATCH 038/108] remove unused/unimplemented class --- .../molgenis/util/AbstractTupleWriter.java | 64 ------------------- 1 file changed, 64 deletions(-) delete mode 100644 src/org/molgenis/util/AbstractTupleWriter.java diff --git a/src/org/molgenis/util/AbstractTupleWriter.java b/src/org/molgenis/util/AbstractTupleWriter.java deleted file mode 100644 index ea7da15c3..000000000 --- a/src/org/molgenis/util/AbstractTupleWriter.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.molgenis.util; - -import java.util.List; - -public abstract class AbstractTupleWriter implements TupleWriter -{ - - @Override - public void writeHeader() throws Exception - { - // TODO Auto-generated method stub - - } - - @Override - public void writeRow(Entity e) throws Exception - { - // TODO Auto-generated method stub - - } - - @Override - public void writeRow(Tuple t) - { - // TODO Auto-generated method stub - - } - - @Override - public void writeValue(Object object) - { - // TODO Auto-generated method stub - - } - - @Override - public void setHeaders(List fields) - { - // TODO Auto-generated method stub - - } - - @Override - public void writeEndOfLine() - { - // TODO Auto-generated method stub - - } - - @Override - public void close() throws Exception - { - // TODO Auto-generated method stub - - } - - @Override - public void writeMatrix(List rowNames, List colNames, Object[][] elements) - { - // TODO Auto-generated method stub - - } - -} From 69c4857b6f1d3e3ac904f114756318b6829f8444 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Thu, 8 Nov 2012 11:11:04 +0100 Subject: [PATCH 039/108] remove disabled code --- src/org/molgenis/framework/server/RestInterface.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/org/molgenis/framework/server/RestInterface.java b/src/org/molgenis/framework/server/RestInterface.java index 3e5a29b5e..b4a539968 100644 --- a/src/org/molgenis/framework/server/RestInterface.java +++ b/src/org/molgenis/framework/server/RestInterface.java @@ -110,8 +110,6 @@ private static void handleRetrievalRequest(HttpServletRequest request, HttpServl { TupleWriter writer = new CsvWriter(out); - // CsvWriter writer = new CsvFileWriter( new - // File("c:/testout.txt") ); if (rulesList != null) db.find(getClassForName(entityName), writer, rulesList.toArray(new QueryRule[rulesList.size()])); else From da9aa20b8d8835b39c4bb1aed683066564bfef0c Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Thu, 8 Nov 2012 11:11:58 +0100 Subject: [PATCH 040/108] update call to CsvWriter --- .../framework/server/services/MolgenisUploadService.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/org/molgenis/framework/server/services/MolgenisUploadService.java b/src/org/molgenis/framework/server/services/MolgenisUploadService.java index a77a82253..114dd4e84 100644 --- a/src/org/molgenis/framework/server/services/MolgenisUploadService.java +++ b/src/org/molgenis/framework/server/services/MolgenisUploadService.java @@ -1,7 +1,6 @@ package org.molgenis.framework.server.services; import java.io.BufferedReader; -import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; @@ -220,7 +219,7 @@ private void processRequest(MolgenisRequest req, PrintWriter out) throws Excepti if (action.equals("ADD")) { File temp = File.createTempFile("molgenis", "tab"); - TupleWriter writer = new CsvWriter(new PrintWriter(new BufferedWriter(new FileWriter(temp)))); + TupleWriter writer = new CsvWriter(new FileWriter(temp)); if (req.getObject(INPUT_SILENT) != null && req.getBool(INPUT_SILENT) == true) { writer.close(); From cbc600e90dca04d060e3a9e4ae4bf157fa18c571 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Thu, 8 Nov 2012 11:12:37 +0100 Subject: [PATCH 041/108] add exception to method signature --- .../framework/tupletable/view/renderers/AbstractExporter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/org/molgenis/framework/tupletable/view/renderers/AbstractExporter.java b/src/org/molgenis/framework/tupletable/view/renderers/AbstractExporter.java index 9d228d28d..5a04938fc 100644 --- a/src/org/molgenis/framework/tupletable/view/renderers/AbstractExporter.java +++ b/src/org/molgenis/framework/tupletable/view/renderers/AbstractExporter.java @@ -1,5 +1,6 @@ package org.molgenis.framework.tupletable.view.renderers; +import java.io.IOException; import java.io.OutputStream; import org.molgenis.framework.tupletable.TableException; @@ -15,5 +16,5 @@ public AbstractExporter(TupleTable tupleTable) this.tupleTable = tupleTable; } - public abstract void export(OutputStream os) throws TableException; + public abstract void export(OutputStream os) throws IOException, TableException; } From c4f9bf7559ed1722b0409caceaee450cd9519f86 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Thu, 8 Nov 2012 11:13:23 +0100 Subject: [PATCH 042/108] add exception to method signature --- .../framework/tupletable/view/renderers/CsvExporter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/org/molgenis/framework/tupletable/view/renderers/CsvExporter.java b/src/org/molgenis/framework/tupletable/view/renderers/CsvExporter.java index 4736c80ca..bd90534fe 100644 --- a/src/org/molgenis/framework/tupletable/view/renderers/CsvExporter.java +++ b/src/org/molgenis/framework/tupletable/view/renderers/CsvExporter.java @@ -1,5 +1,6 @@ package org.molgenis.framework.tupletable.view.renderers; +import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; @@ -23,7 +24,7 @@ public CsvExporter(TupleTable table) } @Override - public void export(OutputStream os) throws TableException + public void export(OutputStream os) throws IOException, TableException { CsvWriter csv = new CsvWriter(os); From e0cd2068cda63c2cf77bf83ae329dbe39a4ada20 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Thu, 8 Nov 2012 11:13:58 +0100 Subject: [PATCH 043/108] add exception to method signature --- .../framework/tupletable/view/renderers/SPSSExporter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/molgenis/framework/tupletable/view/renderers/SPSSExporter.java b/src/org/molgenis/framework/tupletable/view/renderers/SPSSExporter.java index 494698d2a..319f0e32c 100644 --- a/src/org/molgenis/framework/tupletable/view/renderers/SPSSExporter.java +++ b/src/org/molgenis/framework/tupletable/view/renderers/SPSSExporter.java @@ -32,7 +32,7 @@ public SPSSExporter(TupleTable matrix) // SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy"); } - public void export(OutputStream csvOs, OutputStream spssOs, String csvFileName) throws TableException + public void export(OutputStream csvOs, OutputStream spssOs, String csvFileName) throws IOException, TableException { super.export(csvOs); writeSPSFile(spssOs, csvFileName); From 36c3373a0cc74ce27a29c6f40afe4e28a6feb6cb Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Thu, 8 Nov 2012 11:14:31 +0100 Subject: [PATCH 044/108] remove disabled code --- .../molgenis/framework/ui/FormController.java | 169 ------------------ 1 file changed, 169 deletions(-) diff --git a/src/org/molgenis/framework/ui/FormController.java b/src/org/molgenis/framework/ui/FormController.java index 14ada6d14..6528863d2 100644 --- a/src/org/molgenis/framework/ui/FormController.java +++ b/src/org/molgenis/framework/ui/FormController.java @@ -730,175 +730,6 @@ public DatabasePager getPager() return pager; } - // Actions below have been moved to form.command package - // public void doRemoveSelected(Tuple request) throws DatabaseException, - // ParseException, IOException - // { - // Database db = view.getDatabase(); - // ScreenMessage msg = null; - // try - // { - // // get ids - // List idList = request.getList("massUpdate"); - // if (idList == null || idList.size() == 0) throw new - // Exception("no items selected"); - // for (Object id : idList) - // { - // logger.info("mass removing id: " + id); - // } - // - // // find selected entities - // Query q = - // view.getDatabase().query(view.getEntityClass()).in(view.create() - // .getIdField(), idList); - // List selection = q.find(); - // - // // delete selected entities - // db.remove(selection); - // msg = new ScreenMessage("REMOVED " + selection.size() + " records", - // null, true); - // } - // catch (Exception e) - // { - // msg = new ScreenMessage("REMOVE SELECTION FAILED: " + e.getMessage(), - // null, false); - // } - // view.getMessages().add(msg); - // - // // **make sure the user sees a record**/ - // if (msg.isSuccess()) - // { - // pager.prev(); - // // resetChildren(); - // } - // } - // // FIXME move to mapper - // public void doMassUpdate(Tuple request) - // { - // List idList = request.getList("massUpdate"); - // for (Object id : idList) - // { - // logger.info("mass updating id: " + id); - // } - // - // ScreenMessage msg = null; - // Database db = view.getDatabase(); - // int row = 0; - // try - // { - // Query q = db.query(view.getEntityClass()).in(view.create().getIdField(), - // idList); - // List entities = q.find(); - // - // db.beginTx(); - // for (E e : entities) - // { - // row++; - // e.set(request, false); - // db.update(e); - // } - // db.commitTx(); - // msg = new ScreenMessage("MASS UPDATE SUCCESS: updated " + - // entities.size() + " rows", null, true); - // } - // - // catch (Exception e) - // { - // try - // { - // db.rollbackTx(); - // } - // catch (DatabaseException e1) - // { - // logger.error("doMassUpdate() Should never happen: "+e1); - // e1.printStackTrace(); - // } - // msg = new ScreenMessage("MASS UPDATE FAILED on item '" + row + "': " + - // e, null, false); - // } - // - // view.getMessages().add(msg); - // } - - // public void doCsvUpload(Tuple request) - // { - // logger.debug("doCsvUpload: " + request); - // ScreenMessage msg = null; - // try - // { - // CsvEntityReader csvReader = this.view.getCsvReader(); - // - // int updatedRows = csvReader.importCsv(view.getDatabase(), new - // CsvStringReader(request.getString("__csvdata")), request); - // //for (E entity : entities) - // // logger.debug("parsed: " + entity); - // //view.getDatabase().add(entities); - // msg = new ScreenMessage("CSV UPLOAD SUCCESS: added " + updatedRows + - // " rows", null, true); - // logger.debug("CSV UPLOAD SUCCESS: added " + updatedRows + " rows"); - // pager.resetFilters(); - // pager.last(); - // } - // catch (Exception e) - // { - // e.printStackTrace(); - // msg = new ScreenMessage("CSV UPLOAD FAILED: " + e.getMessage(), null, - // false); - // logger.error("CSV UPLOAD FAILED: " + e.getMessage()); - // } - // view.getMessages().add(msg); - // } - - // FIXME move to mapper - // public void getDataAsText(Tuple requestTuple, PrintWriter out) throws - // DatabaseException - // { - // - // if (requestTuple.getString("limit").equals("selected")) - // { - // - // Object recordsObject = requestTuple.getObject("massUpdate"); - // List records = new ArrayList(); - // - // if (recordsObject != null) - // { - // if (recordsObject.getClass().equals(Vector.class)) records = - // (Vector) recordsObject; - // else - // records.add(recordsObject.toString()); - // } - // - // if(records.size() == 0) - // { - // out.println("No records selected."); - // return; - // } - // // watch out, the "IN" operator expects an Object[] - // view.getDatabase().find(view.getEntityClass(), new CsvWriter(out), - // new QueryRule("id", Operator.IN, records)); - // } - // else if (requestTuple.getString("limit").equals("all")) - // { - // // remove limit/offset - // int oldLimit = view.getLimit(); - // int oldOffset = view.getOffset(); - // view.setLimit(0); - // view.setOffset(0); - // - // view.getDatabase().find(view.getEntityClass(), new CsvWriter(out), - // view.getRules()); - // - // // restore limit/offset - // view.setOffset(oldOffset); - // view.setLimit(oldLimit); - // } - // else - // { - // view.getDatabase().find(view.getEntityClass(), new CsvWriter(out), - // view.getRules()); - // } - // } - /** * Calculates visible columns. Needed for downloads. */ From 52bb52a3f350ca6c2832c5a5e87075b611ba8e92 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Thu, 8 Nov 2012 11:15:12 +0100 Subject: [PATCH 045/108] remove disabled code --- .../framework/ui/commands/GalaxyCommand.java | 52 ------------------- 1 file changed, 52 deletions(-) diff --git a/src/org/molgenis/framework/ui/commands/GalaxyCommand.java b/src/org/molgenis/framework/ui/commands/GalaxyCommand.java index 0645838de..b43b3df74 100644 --- a/src/org/molgenis/framework/ui/commands/GalaxyCommand.java +++ b/src/org/molgenis/framework/ui/commands/GalaxyCommand.java @@ -20,10 +20,8 @@ * * @param */ - public class GalaxyCommand extends SimpleCommand { - public GalaxyCommand(String name, ScreenController parentController) { super(name, parentController); @@ -35,11 +33,8 @@ public GalaxyCommand(String name, ScreenController parentController) { this.setLabel("Send Selected to Galaxy"); } - // this.setLabel("Send to Galaxy"); this.setIcon("generated-res/img/upload2galaxy.png"); this.setMenu("File"); - // this.setDialog(false); - } // TODO: Get proper UID. @@ -103,8 +98,6 @@ public List getActions() } @Override - // public ScreenModel.Show handleRequest(Database db, Tuple request, - // PrintWriter downloadStream) { public ScreenModel.Show handleRequest(Database db, Tuple request, OutputStream downloadStream) { logger.debug("galaxy button clicked: " + this.getController().getApplicationController().getGalaxyUrl()); @@ -112,50 +105,6 @@ public ScreenModel.Show handleRequest(Database db, Tuple request, OutputStream d return ScreenModel.Show.SHOW_MAIN; } - // @SuppressWarnings("unchecked") - // @Override - // - // public ScreenModel.Show handleRequest(Database db, Tuple request, - // PrintWriter csvDownload) - // throws ParseException, DatabaseException, IOException { - // - // public ScreenModel.Show handleRequest(Database db, Tuple request, - // OutputStream downloadStream) - // { - // logger.debug("galaxy button clicked: "+this.getController().getApplicationController().getGalaxyUrl()); - // logger.error(this.getName()); - // - // FormModel view = this.getFormScreen(); - // - // Object ids = request.getList(FormModel.INPUT_SELECTED); - // List records = new ArrayList(); - // - // if (ids != null) - // { - // if (ids instanceof List) - // { - // records = (List) ids; - // } - // else - // records.add(ids); - // } - // - // if (records.size() == 0) - // { - // csvDownload.println("No records selected."); - // return ScreenModel.Show.SHOW_MAIN; - // } - // - // List fieldsToExport = - // ((FormController)this.getController()).getVisibleColumnNames(); - // - // // watch out, the "IN" operator expects an Object[] - // db.find(view.getController().getEntityClass(), new - // CsvWriter(csvDownload), fieldsToExport, - // new QueryRule("id", Operator.IN, records)); - // return ScreenModel.Show.SHOW_MAIN; - // } - @Override public boolean isVisible() { @@ -167,5 +116,4 @@ public boolean isVisible() } return false; } - } \ No newline at end of file From 1492c0c05dc44d5cf04eeeee3258546fb6880ce2 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Thu, 8 Nov 2012 11:16:02 +0100 Subject: [PATCH 046/108] close streams --- .../java/org/molgenis/util/CsvWriterTest.java | 233 ++++++++++++++++++ test/java/org/molgenis/util/PlinkTest.java | 83 ++++--- 2 files changed, 284 insertions(+), 32 deletions(-) create mode 100644 test/java/org/molgenis/util/CsvWriterTest.java diff --git a/test/java/org/molgenis/util/CsvWriterTest.java b/test/java/org/molgenis/util/CsvWriterTest.java new file mode 100644 index 000000000..f1cf7e1da --- /dev/null +++ b/test/java/org/molgenis/util/CsvWriterTest.java @@ -0,0 +1,233 @@ +package org.molgenis.util; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.testng.AssertJUnit.assertEquals; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.Arrays; + +import org.testng.annotations.Test; + +public class CsvWriterTest +{ + @Test + public void writeHeader() throws IOException + { + StringWriter strWriter = new StringWriter(); + CsvWriter csvWriter = new CsvWriter(strWriter, Arrays.asList("h1", "h2", "h3")); + try + { + csvWriter.writeHeader(); + } + finally + { + csvWriter.close(); + } + assertEquals("h1\th2\th3\n", strWriter.toString()); + } + + @Test + public void writeMatrix() throws IOException + { + StringWriter strWriter = new StringWriter(); + CsvWriter csvWriter = new CsvWriter(strWriter, Arrays.asList("h1", "h2", "h3")); + try + { + String[][] values = new String[3][3]; + values[0][0] = "v1"; + values[0][1] = "v2"; + values[0][2] = "v3"; + values[1][0] = "v4"; + values[1][1] = "v5"; + values[1][2] = "v6"; + values[2][0] = "v7"; + values[2][1] = "v8"; + values[2][2] = "v9"; + + csvWriter.writeMatrix(Arrays.asList("r1", "r2", "r3"), Arrays.asList("c1", "c2", "c3"), values); + } + finally + { + csvWriter.close(); + } + assertEquals("\tc1\tc2\tc3\nr1\tv1\tv2\tv3\nr2\tv4\tv5\tv6\nr3\tv7\tv8\tv9\n", strWriter.toString()); + } + + @Test + public void writeMatrix_missing() throws IOException + { + StringWriter strWriter = new StringWriter(); + CsvWriter csvWriter = new CsvWriter(strWriter, Arrays.asList("h1", "h2", "h3")); + csvWriter.setMissingValue("x"); + try + { + String[][] values = new String[3][3]; + values[0][0] = "v1"; + values[0][1] = "v2"; + values[0][2] = null; + values[1][0] = "v4"; + values[1][1] = "v5"; + values[1][2] = "v6"; + values[2][0] = "v7"; + values[2][1] = "v8"; + values[2][2] = "v9"; + + csvWriter.writeMatrix(Arrays.asList("r1", "r2", "r3"), Arrays.asList("c1", "c2", "c3"), values); + } + finally + { + csvWriter.close(); + } + assertEquals("\tc1\tc2\tc3\nr1\tv1\tv2\tx\nr2\tv4\tv5\tv6\nr3\tv7\tv8\tv9\n", strWriter.toString()); + } + + @Test + public void writeRowEntity() throws IOException + { + StringWriter strWriter = new StringWriter(); + CsvWriter csvWriter = new CsvWriter(strWriter, Arrays.asList("h1", "h2", "h3")); + try + { + Entity entity = mock(Entity.class); + when(entity.get("h1")).thenReturn("v1"); + when(entity.get("h2")).thenReturn("v2"); + when(entity.get("h3")).thenReturn("v3"); + csvWriter.writeRow(entity); + } + finally + { + csvWriter.close(); + } + assertEquals("v1\tv2\tv3\n", strWriter.toString()); + } + + @Test + public void writeRowEntity_missing() throws IOException + { + StringWriter strWriter = new StringWriter(); + CsvWriter csvWriter = new CsvWriter(strWriter, Arrays.asList("h1", "h2", "h3", "h4")); + csvWriter.setMissingValue("x"); + try + { + Entity entity = mock(Entity.class); + when(entity.get("h1")).thenReturn("v1"); + when(entity.get("h2")).thenReturn("v2"); + when(entity.get("h4")).thenReturn("v4"); + csvWriter.writeRow(entity); + } + finally + { + csvWriter.close(); + } + assertEquals("v1\tv2\tx\tv4\n", strWriter.toString()); + } + + @Test + public void writeRowTuple() throws IOException + { + StringWriter strWriter = new StringWriter(); + CsvWriter csvWriter = new CsvWriter(strWriter, Arrays.asList("h1", "h2", "h3")); + try + { + Tuple tuple = new SimpleTuple(Arrays.asList("h1", "h2", "h3", "h4")); + tuple.set(0, "v1"); + tuple.set(1, "v2"); + tuple.set(2, "v3"); + tuple.set(3, "v4"); + csvWriter.writeRow(tuple); + } + finally + { + csvWriter.close(); + } + assertEquals("v1\tv2\tv3\n", strWriter.toString()); + } + + @Test + public void writeRowTuple_missing() throws IOException + { + StringWriter strWriter = new StringWriter(); + CsvWriter csvWriter = new CsvWriter(strWriter, Arrays.asList("h1", "h2", "h3", "h4")); + csvWriter.setMissingValue("x"); + try + { + Tuple tuple = new SimpleTuple(Arrays.asList("h1", "h2", "h4")); + tuple.set(0, "v1"); + tuple.set(1, "v2"); + tuple.set(2, "v4"); + csvWriter.writeRow(tuple); + } + finally + { + csvWriter.close(); + } + assertEquals("v1\tv2\tx\tv4\n", strWriter.toString()); + } + + @Test + public void writeSeparator() throws IOException + { + StringWriter strWriter = new StringWriter(); + CsvWriter csvWriter = new CsvWriter(strWriter, '~'); + try + { + csvWriter.writeSeparator(); + } + finally + { + csvWriter.close(); + } + assertEquals("~", strWriter.toString()); + } + + @Test + public void writeValue_String() throws IOException + { + StringWriter strWriter = new StringWriter(); + CsvWriter csvWriter = new CsvWriter(strWriter); + try + { + csvWriter.writeValue("s1"); + } + finally + { + csvWriter.close(); + } + assertEquals("s1", strWriter.toString()); + } + + @Test + public void writeValue_List() throws IOException + { + StringWriter strWriter = new StringWriter(); + CsvWriter csvWriter = new CsvWriter(strWriter); + try + { + csvWriter.writeValue(Arrays.asList("s1", "s2", "s3")); + } + finally + { + csvWriter.close(); + } + assertEquals("s1|s2|s3", strWriter.toString()); + } + + @Test + public void writeValue_List_separator() throws IOException + { + StringWriter strWriter = new StringWriter(); + CsvWriter csvWriter = new CsvWriter(strWriter); + csvWriter.setListSeparator('/'); + try + { + csvWriter.writeValue(Arrays.asList("s1", "s2", "s3")); + } + finally + { + csvWriter.close(); + } + assertEquals("s1/s2/s3", strWriter.toString()); + } +} diff --git a/test/java/org/molgenis/util/PlinkTest.java b/test/java/org/molgenis/util/PlinkTest.java index 5bfba8f67..81e0eaf7c 100644 --- a/test/java/org/molgenis/util/PlinkTest.java +++ b/test/java/org/molgenis/util/PlinkTest.java @@ -282,9 +282,17 @@ public void BIM_writer() throws Exception { File newBim = new File(testBim.getAbsolutePath().replace(testBim.getName(), "new.bim")); BimFileWriter w = new BimFileWriter(newBim); - w.writeAll(bimfd.getAllEntries()); - boolean filesAreEqual = DirectoryCompare.compareFileContent(testBim, newBim); - assertTrue(filesAreEqual); + try + { + w.writeAll(bimfd.getAllEntries()); + boolean filesAreEqual = DirectoryCompare.compareFileContent(testBim, newBim); + assertTrue(filesAreEqual); + } + finally + { + w.close(); + } + } @Test @@ -292,9 +300,16 @@ public void MAP_writer() throws Exception { File newMap = new File(testMap.getAbsolutePath().replace(testMap.getName(), "new.map")); MapFileWriter w = new MapFileWriter(newMap); - w.writeAll(mapfd.getAllEntries()); - boolean filesAreEqual = DirectoryCompare.compareFileContent(testMap, newMap); - assertTrue(filesAreEqual); + try + { + w.writeAll(mapfd.getAllEntries()); + boolean filesAreEqual = DirectoryCompare.compareFileContent(testMap, newMap); + assertTrue(filesAreEqual); + } + finally + { + w.close(); + } } @Test @@ -302,9 +317,16 @@ public void PED_writer() throws Exception { File newPed = new File(testPed.getAbsolutePath().replace(testPed.getName(), "new.ped")); PedFileWriter w = new PedFileWriter(newPed); - w.writeAll(pedfd.getAllEntries()); - boolean filesAreEqual = DirectoryCompare.compareFileContent(testPed, newPed); - assertTrue(filesAreEqual); + try + { + w.writeAll(pedfd.getAllEntries()); + boolean filesAreEqual = DirectoryCompare.compareFileContent(testPed, newPed); + assertTrue(filesAreEqual); + } + finally + { + w.close(); + } } @Test @@ -312,9 +334,16 @@ public void FAM_writer() throws Exception { File newFam = new File(testFam.getAbsolutePath().replace(testFam.getName(), "new.fam")); FamFileWriter w = new FamFileWriter(newFam); - w.writeAll(famfd.getAllEntries()); - boolean filesAreEqual = DirectoryCompare.compareFileContent(testFam, newFam); - assertTrue(filesAreEqual); + try + { + w.writeAll(famfd.getAllEntries()); + boolean filesAreEqual = DirectoryCompare.compareFileContent(testFam, newFam); + assertTrue(filesAreEqual); + } + finally + { + w.close(); + } } @Test @@ -322,9 +351,16 @@ public void TPED_writer() throws Exception { File newTped = new File(testTped.getAbsolutePath().replace(testTped.getName(), "new.tped")); TpedFileWriter w = new TpedFileWriter(newTped); - w.writeAll(tpedfd.getAllEntries()); - boolean filesAreEqual = DirectoryCompare.compareFileContent(testTped, newTped); - assertTrue(filesAreEqual); + try + { + w.writeAll(tpedfd.getAllEntries()); + boolean filesAreEqual = DirectoryCompare.compareFileContent(testTped, newTped); + assertTrue(filesAreEqual); + } + finally + { + w.close(); + } } @AfterClass @@ -336,21 +372,4 @@ public void close() throws IOException mapfd.close(); tpedfd.close(); } - - // private boolean stringArrEqual(String[] arr1, String[] arr2) - // { - // if (arr1.length != arr2.length) - // { - // return false; - // } - // for (int i = 0; i < arr1.length; i++) - // { - // if (!arr1[i].equals(arr2[i])) - // { - // return false; - // } - // } - // return true; - // } - } From 74c16d14d32188da45ada13e877882ac15f80906 Mon Sep 17 00:00:00 2001 From: dennishendriksen Date: Thu, 8 Nov 2012 11:18:09 +0100 Subject: [PATCH 047/108] refactor CsvWriter, fix bugs, add tests, updated method signatures --- .../generators/excel/ExcelReaderGen.java.ftl | 41 +- src/org/molgenis/util/CsvFileWriter.java | 11 +- src/org/molgenis/util/CsvWriter.java | 366 +++++++----------- src/org/molgenis/util/DatabaseWriter.java | 29 +- src/org/molgenis/util/TupleWriter.java | 27 +- src/org/molgenis/util/XlsWriter.java | 49 ++- .../util/plink/writers/BimFileWriter.java | 35 +- .../util/plink/writers/FamFileWriter.java | 39 +- .../util/plink/writers/MapFileWriter.java | 39 +- .../util/plink/writers/PedFileWriter.java | 41 +- .../util/plink/writers/TpedFileWriter.java | 41 +- 11 files changed, 385 insertions(+), 333 deletions(-) diff --git a/src/org/molgenis/generators/excel/ExcelReaderGen.java.ftl b/src/org/molgenis/generators/excel/ExcelReaderGen.java.ftl index b5ad87c0f..decf2086f 100644 --- a/src/org/molgenis/generators/excel/ExcelReaderGen.java.ftl +++ b/src/org/molgenis/generators/excel/ExcelReaderGen.java.ftl @@ -19,9 +19,9 @@ package ${package}; import java.io.File; -import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; -import java.io.PrintWriter; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; @@ -84,7 +84,7 @@ public class ${JavaName(entity)}ExcelReader return headers; } - private boolean writeSheetToFile(Sheet sheet, File file) throws FileNotFoundException{ + private boolean writeSheetToFile(Sheet sheet, File file) throws IOException{ List headers = new ArrayList(); Cell[] headerCells = sheet.getRow(0); //assume headers are on first line if(headerCells.length == 0){ @@ -99,22 +99,31 @@ public class ${JavaName(entity)}ExcelReader namelessHeaderLocations.add(i); } } - PrintWriter pw = new PrintWriter(file); - CsvWriter cw = new CsvWriter(pw, headers); - cw.setMissingValue(""); - cw.writeHeader(); - for(int rowIndex = 1; rowIndex < sheet.getRows(); rowIndex++){ - Tuple t = new SimpleTuple(); - int colIndex = 0; - for(Cell c : sheet.getRow(rowIndex)){ - if(!namelessHeaderLocations.contains(colIndex) && colIndex < headers.size() && c.getContents() != null){ - t.set(headers.get(colIndex), c.getContents()); + CsvWriter cw = new CsvWriter(new FileOutputStream(file), Charset.forName("UTF-8"), headers); + try + { + cw.setMissingValue(""); + cw.writeHeader(); + for (int rowIndex = 1; rowIndex < sheet.getRows(); rowIndex++) + { + Tuple t = new SimpleTuple(); + int colIndex = 0; + for (Cell c : sheet.getRow(rowIndex)) + { + if (!namelessHeaderLocations.contains(colIndex) && colIndex < headers.size() + && c.getContents() != null) + { + t.set(headers.get(colIndex), c.getContents()); + } + colIndex++; } - colIndex++; + cw.writeRow(t); } - cw.writeRow(t); } - cw.close(); + finally + { + cw.close(); + } return true; } } \ No newline at end of file diff --git a/src/org/molgenis/util/CsvFileWriter.java b/src/org/molgenis/util/CsvFileWriter.java index 6d7dbb1fa..51df35c40 100644 --- a/src/org/molgenis/util/CsvFileWriter.java +++ b/src/org/molgenis/util/CsvFileWriter.java @@ -1,10 +1,8 @@ package org.molgenis.util; -import java.io.BufferedWriter; import java.io.File; -import java.io.FileWriter; +import java.io.FileOutputStream; import java.io.IOException; -import java.io.PrintWriter; import java.util.List; /** @@ -14,7 +12,7 @@ public class CsvFileWriter extends CsvWriter { public CsvFileWriter(File f) throws IOException { - super(new PrintWriter(new BufferedWriter(new FileWriter(f)))); + super(new FileOutputStream(f)); } /** @@ -24,7 +22,7 @@ public CsvFileWriter(File f) throws IOException */ public CsvFileWriter(File f, List fields) throws IOException { - super(new PrintWriter(new BufferedWriter(new FileWriter(f))), fields); + super(new FileOutputStream(f), fields); } /** @@ -37,7 +35,6 @@ public CsvFileWriter(File f, List fields) throws IOException */ public CsvFileWriter(File file, List fields, boolean append) throws IOException { - super(new PrintWriter(new BufferedWriter(new FileWriter(file, append))), fields); + super(new FileOutputStream(file, append), fields); } - } diff --git a/src/org/molgenis/util/CsvWriter.java b/src/org/molgenis/util/CsvWriter.java index 308a8b194..7475d7304 100644 --- a/src/org/molgenis/util/CsvWriter.java +++ b/src/org/molgenis/util/CsvWriter.java @@ -1,17 +1,14 @@ package org.molgenis.util; +import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStream; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Arrays; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.nio.charset.Charset; import java.util.List; -import javax.servlet.http.HttpServletResponse; - import org.apache.commons.lang.StringEscapeUtils; -import org.apache.log4j.Logger; -import org.molgenis.framework.server.MolgenisResponse; /** * Delimited writer to write {@link org.molgenis.util.Tuple Tuple} or @@ -20,296 +17,231 @@ * This writer is typically used to produce a comma-separated-values (CSV) or * tab-delimited-values (TAB) file. *

- * FIXME make Tuple and Entity both inherit from the same interface so this - * writer can be simplified. */ public class CsvWriter implements TupleWriter { - /** to send log messages on progress to */ - private static final transient Logger logger = Logger.getLogger(TupleWriter.class.getSimpleName()); + private static final Charset CHARSET_UTF8 = Charset.forName("UTF-8"); + + private static final char DEFAULT_SEPARATOR = '\t'; + private static final char DEFAULT_LIST_SEPARATOR = '|'; + private static final String DEFAULT_LINE_SEPARATOR = "\n"; + /** writer the output is written to */ - protected PrintWriter writer = null; - /** separator used to separate columns, default "\t" */ - private String separator = "\t"; - /** separator used to separate lists, default "|" */ - private String listSeparator = "|"; - /** number of rows written */ - private int count = 0; - /** value to use for missing/null values such as "NULL" or "NA", default "" */ - private String missingValue = ""; + private Writer writer; + /** column separator (default: \t) */ + private char separator = DEFAULT_SEPARATOR; + /** line separator (default: \n) */ + private String lineSeparator = DEFAULT_LINE_SEPARATOR; + /** list value separator (default: |) */ + private char listSeparator = DEFAULT_LIST_SEPARATOR; + /** + * value to use for missing/null values such as "NULL" or "NA", default + * write nothing + */ + private String missingValue; /** headers to be written out */ - private List headers = new ArrayList(); + private List headers; - public CsvWriter(PrintWriter writer, List headers) + public CsvWriter(OutputStream os) { - this(writer); - this.headers = headers; + this(os, CHARSET_UTF8); } - public CsvWriter(PrintWriter writer) + public CsvWriter(OutputStream os, Charset charset) { - this.writer = writer; + this(new OutputStreamWriter(os, charset)); } - public CsvWriter(OutputStream outputStream, List headers) + public CsvWriter(OutputStream os, char separator) { - this(outputStream); - this.headers = headers; + this(os, CHARSET_UTF8, separator); } - public CsvWriter(OutputStream outputStream) + public CsvWriter(OutputStream os, Charset charset, char separator) { - this.writer = new PrintWriter(outputStream); + this(new OutputStreamWriter(os, charset), separator); } - /** - * CsvWriter that write to a molgenis response - * - * @throws IOException - */ - public CsvWriter(String name, MolgenisResponse response) throws IOException + public CsvWriter(OutputStream os, List headers) { - HttpServletResponse r = response.getResponse(); - r.setContentType("application/x-download"); - r.setHeader("Content-Disposition", "attachment; filename=" + name); - this.writer = r.getWriter(); + this(os, CHARSET_UTF8, headers); } - /** - * Write out an XGAP matrix. The inputs can be retrieved from any - * implementation of the XGAP matrix interface class. - * - * @param rowNames - * @param colNames - * @param elements - */ - public void writeMatrix(List rowNames, List colNames, Object[][] elements) + public CsvWriter(OutputStream os, Charset charset, List headers) + { + this(new OutputStreamWriter(os, charset), headers); + } + + public CsvWriter(OutputStream os, char separator, List headers) + { + this(os, CHARSET_UTF8, separator, headers); + } + + public CsvWriter(OutputStream os, Charset charset, char separator, List headers) + { + this(new OutputStreamWriter(os, charset), separator, headers); + } + + public CsvWriter(Writer writer) + { + this(writer, DEFAULT_SEPARATOR); + } + + public CsvWriter(Writer writer, char separator) { - StringBuilder colsBuilder = new StringBuilder(); - for (String col : colNames) + this(writer, separator, null); + } + + public CsvWriter(Writer writer, List headers) + { + this(writer, DEFAULT_SEPARATOR, headers); + } + + public CsvWriter(Writer writer, char separator, List headers) + { + if (writer == null) throw new IllegalArgumentException("writer is null"); + this.writer = new BufferedWriter(writer); + this.separator = separator; + this.headers = headers; + } + + public void writeMatrix(List rowNames, List colNames, Object[][] values) throws IOException + { + // write header + for (String colName : colNames) { - colsBuilder.append('\t').append(col); + writeSeparator(); + writeValue(colName); } - writer.println(colsBuilder.toString()); + writeEndOfLine(); - for (int rowIndex = 0; rowIndex < rowNames.size(); rowIndex++) + final int nrRows = rowNames.size(); + final int nrCols = colNames.size(); + for (int row = 0; row < nrRows; ++row) { - StringBuilder rowBuilder = new StringBuilder(rowNames.get(rowIndex)); - for (int colIndex = 0; colIndex < colNames.size(); colIndex++) + writeValue(rowNames.get(row)); + for (int col = 0; col < nrCols; ++col) { - if (elements[rowIndex][colIndex] == null) - { - rowBuilder.append('\t'); - } - else - { - rowBuilder.append('\t').append(elements[rowIndex][colIndex]); - } + writeSeparator(); + writeValue(values[row][col]); } - writer.println(rowBuilder.toString()); + writeEndOfLine(); } } - /* - * (non-Javadoc) - * - * @see org.molgenis.util.CsvWriter#writeHeader() - */ @Override - public void writeHeader() + public void writeHeader() throws IOException { - for (int i = 0; i < headers.size(); i++) + final int nrHeaders = this.headers.size(); + for (int i = 0; i < nrHeaders; ++i) { - if (i < headers.size() - 1) - { - writer.print(headers.get(i) + separator); - } - else - { - writer.print(headers.get(i)); - } + if (i > 0) writeSeparator(); + writeValue(this.headers.get(i)); } - writer.println(); + writeEndOfLine(); } - /* - * (non-Javadoc) - * - * @see org.molgenis.util.CsvWriter#writeRow(org.molgenis.util.Entity) - */ @Override - public void writeRow(Entity e) + public void writeRow(Entity e) throws IOException { - boolean first = true; - for (String col : headers) + final int nrHeaders = this.headers.size(); + for (int i = 0; i < nrHeaders; ++i) { - // print separator unless first element - if (first) - { - first = false; - } - else - { - writer.print(separator); - } - // print value - writeValue(e.get(col)); - + if (i > 0) writeSeparator(); + writeValue(e.get(this.headers.get(i))); } - // newline - writer.println(); - // writer.println(e.getValues(separator)); - if (++count % 10000 == 0) logger.debug("wrote line " + count + ": " + e); + writeEndOfLine(); } - /* - * (non-Javadoc) - * - * @see org.molgenis.util.CsvWriter#writeRow(org.molgenis.util.Tuple) - */ @Override - public void writeRow(Tuple t) + public void writeRow(Tuple t) throws IOException { - boolean first = true; - for (String col : headers) + final int nrHeaders = this.headers.size(); + for (int i = 0; i < nrHeaders; ++i) { - // print separator unless first element - if (first) - { - first = false; - } - else - { - writer.print(separator); - } - // print value - writeValue(t.getObject(col)); + if (i > 0) writeSeparator(); + writeValue(t.getObject(this.headers.get(i))); } - writer.println(); - if (count++ % 10000 == 0) logger.debug("wrote tuple to line " + count + ": " + t); + writeEndOfLine(); } - /* - * (non-Javadoc) - * - * @see org.molgenis.util.CsvWriter#writeValue(java.lang.Object) - */ - // public void writeRow(Object[] values) - // { - // // FIXME: this is probably unnecessarily slow - // for (int i = 0; i < values.length; i++) - // { - // if(i > 0) writer.print(separator); - // writeValue(values[i], writer); - // } - // writer.println(); - // if (count++ % 10000 == 0) logger.debug("wrote values array to line " + - // count + " "); - // } - @Override - public void writeValue(Object object) + public void writeValue(Object obj) throws IOException { - if (object == null) + if (obj == null) { - writer.print(this.missingValue); + if (this.missingValue != null) this.writer.write(this.missingValue); } - - else + else if (obj instanceof List) { - if (object instanceof List) + List list = (List) obj; + final int size = list.size(); + for (int i = 0; i < size; ++i) { - List list = (List) object; - for (int i = 0; i < list.size(); i++) + // FIXME list separator in list values not escaped + if (i > 0) this.writer.write(this.listSeparator); + Object value = list.get(i); + if (value != null) { - // FIXME, what about escaping??? - if (i != 0) writer.print(listSeparator); - if (list.get(i) != null) - { - writer.print(StringEscapeUtils.escapeCsv(list.get(i).toString())); - } - else - { - writer.print(this.getMissingValue()); - } + StringEscapeUtils.escapeCsv(this.writer, value.toString()); + } + else if (this.missingValue != null) + { + this.writer.write(this.missingValue); } - - } - else - { - // writer.print(StringEscapeUtils.escapeCsv(object.toString().trim().replace("\n", - // ""))); - writer.print(StringEscapeUtils.escapeCsv(object.toString())); } } + else + { + this.writer.write(StringEscapeUtils.escapeCsv(obj.toString())); + } + } + public void writeSeparator() throws IOException + { + this.writer.write(this.separator); + } + + @Override + public void writeEndOfLine() throws IOException + { + this.writer.write(this.lineSeparator); } - /** - * Get the String that is used for missing or null values, default 'NA'. - */ public String getMissingValue() { return missingValue; } - /** - * Set the String that is used for missingValues such as null, default 'NA'. - * - * @param missingValue - * new missing value String. - */ public void setMissingValue(String missingValue) { this.missingValue = missingValue; } - /** - * Get the separator used to separate columns that are outputed, default - * '\t'. - */ - public String getSeparator() + public char getSeparator() { return separator; } - /** - * Set the String that is used to separate columns that are outputed, - * default '\t'. - * - * @param separator - * new separator String. - */ - public void setSeparator(String separator) + public void setSeparator(char separator) { this.separator = separator; } - public void close() - { - writer.flush(); - writer.close(); - } - - public String getListSeparator() + public char getListSeparator() { return listSeparator; } - public void setListSeparator(String listSeparator) + public void setListSeparator(char listSeparator) { this.listSeparator = listSeparator; } - /* - * (non-Javadoc) - * - * @see org.molgenis.util.CsvWriter#setHeaders(java.util.List) - */ @Override - public void setHeaders(List fields) + public void setHeaders(List headers) { - this.headers = fields; + this.headers = headers; } public List getHeaders() @@ -317,25 +249,19 @@ public List getHeaders() return this.headers; } - public void writeSeparator() + public String getLineSeparator() { - this.writer.print(this.getSeparator()); + return lineSeparator; } - /* - * (non-Javadoc) - * - * @see org.molgenis.util.CsvWriter#writeEndOfLine() - */ - @Override - public void writeEndOfLine() + public void setLineSeparator(String lineSeparator) { - this.writer.println(); + this.lineSeparator = lineSeparator; } - public void setHeaders(String... fields) + @Override + public void close() throws IOException { - this.setHeaders(Arrays.asList(fields)); - + writer.close(); } } diff --git a/src/org/molgenis/util/DatabaseWriter.java b/src/org/molgenis/util/DatabaseWriter.java index cafa01c88..d2c6037a7 100644 --- a/src/org/molgenis/util/DatabaseWriter.java +++ b/src/org/molgenis/util/DatabaseWriter.java @@ -1,10 +1,12 @@ package org.molgenis.util; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.molgenis.framework.db.AbstractMapper; import org.molgenis.framework.db.Database; +import org.molgenis.framework.db.DatabaseException; /** Tuple writer that translates tuples into rows in a Database */ public class DatabaseWriter implements TupleWriter @@ -21,21 +23,27 @@ public DatabaseWriter(Database db, Class entityClass) } @Override - public void writeHeader() throws Exception + public void writeHeader() throws IOException { - // NA + throw new UnsupportedOperationException(); } @Override - public void writeRow(Entity e) throws Exception + public void writeRow(Entity e) throws IOException { if (e.getClass().equals(entityClass)) batch.add(e); if (batch.size() >= BATCH_SIZE) { - db.add(batch); + try + { + db.add(batch); + } + catch (DatabaseException e1) + { + throw new IOException(e1); + } batch.clear(); - ; } } @@ -76,11 +84,18 @@ public void writeEndOfLine() } @Override - public void close() throws Exception + public void close() throws IOException { if (batch.size() > 0) { - db.add(batch); + try + { + db.add(batch); + } + catch (DatabaseException e) + { + throw new IOException(e); + } batch.clear(); } diff --git a/src/org/molgenis/util/TupleWriter.java b/src/org/molgenis/util/TupleWriter.java index dd5a11b2f..a1ec9f3d8 100644 --- a/src/org/molgenis/util/TupleWriter.java +++ b/src/org/molgenis/util/TupleWriter.java @@ -1,8 +1,10 @@ package org.molgenis.util; +import java.io.Closeable; +import java.io.IOException; import java.util.List; -public interface TupleWriter +public interface TupleWriter extends Closeable { /** @@ -10,7 +12,7 @@ public interface TupleWriter * * @throws Exception */ - public void writeHeader() throws Exception; + public void writeHeader() throws IOException; /** * Write a row to stream. @@ -19,32 +21,23 @@ public interface TupleWriter * Entity to be written. * @throws Exception */ - public void writeRow(Entity e) throws Exception; + public void writeRow(Entity e) throws IOException; /** * Write a row to stream. * * @param t * Tuple to be written. + * @throws IOException */ - public void writeRow(Tuple t); + public void writeRow(Tuple t) throws IOException; - public void writeValue(Object object); + public void writeValue(Object object) throws IOException; public void setHeaders(List fields); - public void writeEndOfLine(); + public void writeEndOfLine() throws IOException; - /** - * Finish up and close the exported file. For example, close the workbook - * when writing to Excel, or the PrintWriter when writing to CSV. Could - * close the wrapped OutputStream. The returned OutputStream is closed after - * any download in AbstractMolgenisServlet. - * - * @throws Exception - */ - public void close() throws Exception; - - public void writeMatrix(List rowNames, List colNames, Object[][] elements); + public void writeMatrix(List rowNames, List colNames, Object[][] elements) throws IOException; } \ No newline at end of file diff --git a/src/org/molgenis/util/XlsWriter.java b/src/org/molgenis/util/XlsWriter.java index 94278e56e..2e14fc6a3 100644 --- a/src/org/molgenis/util/XlsWriter.java +++ b/src/org/molgenis/util/XlsWriter.java @@ -70,14 +70,25 @@ public XlsWriter(OutputStream outputStream) throws IOException, WriteException } @Override - public void writeHeader() throws Exception + public void writeHeader() throws IOException { // Add and store headers - for (int i = 0; i < headers.size(); i++) + try { - String header = headers.get(i); - Label l = new Label(i, 0, header, headerFormat); - sheet.addCell(l); + for (int i = 0; i < headers.size(); i++) + { + String header = headers.get(i); + Label l = new Label(i, 0, header, headerFormat); + sheet.addCell(l); + } + } + catch (RowsExceededException e) + { + throw new IOException(e); + } + catch (WriteException e) + { + throw new IOException(e); } } @@ -111,11 +122,18 @@ public void writeEndOfLine() } @Override - public void close() throws Exception + public void close() throws IOException { // close the excel file; no writing allowed anymore workbook.write(); - workbook.close(); + try + { + workbook.close(); + } + catch (WriteException e) + { + throw new IOException(e); + } } /** @@ -132,7 +150,7 @@ public void writeMatrix(List rowNames, List colNames, Object[][] } @Override - public void writeRow(Entity e) throws Exception + public void writeRow(Entity e) throws IOException { for (int i = 0; i < headers.size(); i++) { @@ -157,7 +175,7 @@ public void writeRow(Entity e) throws Exception } else { - throw new Exception("List contains null value(s)"); + throw new IOException("List contains null value(s)"); } } @@ -169,7 +187,18 @@ public void writeRow(Entity e) throws Exception } Label l = new Label(i, rowIndex, contentsBuilder.toString(), cellFormat); - sheet.addCell(l); + try + { + sheet.addCell(l); + } + catch (RowsExceededException e1) + { + throw new IOException(e1); + } + catch (WriteException e1) + { + throw new IOException(e1); + } } rowIndex++; } diff --git a/src/org/molgenis/util/plink/writers/BimFileWriter.java b/src/org/molgenis/util/plink/writers/BimFileWriter.java index 0f5384dca..fce751625 100644 --- a/src/org/molgenis/util/plink/writers/BimFileWriter.java +++ b/src/org/molgenis/util/plink/writers/BimFileWriter.java @@ -1,6 +1,9 @@ package org.molgenis.util.plink.writers; +import java.io.Closeable; import java.io.File; +import java.io.IOException; +import java.util.Arrays; import java.util.List; import org.molgenis.util.CsvFileWriter; @@ -9,36 +12,41 @@ /** * Write BIM file entries to a selected location. */ -public class BimFileWriter +public class BimFileWriter implements Closeable { private CsvFileWriter writer; public BimFileWriter(File bimFile) throws Exception { writer = new CsvFileWriter(bimFile); - writer.setHeaders(BimEntry.bimHeader()); + writer.setHeaders(Arrays.asList(BimEntry.bimHeader())); } /** * Close the underlying writer. */ - public void close() + @Override + public void close() throws IOException { writer.close(); } /** * Write a single entry. + * + * @throws IOException */ - public void writeSingle(BimEntry bim) + public void writeSingle(BimEntry bim) throws IOException { writer.writeRow(BimEntry.bimToTuple(bim)); } /** * Write multiple entries in order. + * + * @throws IOException */ - public void writeMulti(List bims) + public void writeMulti(List bims) throws IOException { for (BimEntry bim : bims) { @@ -48,14 +56,21 @@ public void writeMulti(List bims) /** * Write all entries and close the writer. + * + * @throws IOException */ - public void writeAll(List bims) + public void writeAll(List bims) throws IOException { - for (BimEntry bim : bims) + try { - writer.writeRow(BimEntry.bimToTuple(bim)); + for (BimEntry bim : bims) + { + writer.writeRow(BimEntry.bimToTuple(bim)); + } + } + finally + { + writer.close(); } - writer.close(); } - } diff --git a/src/org/molgenis/util/plink/writers/FamFileWriter.java b/src/org/molgenis/util/plink/writers/FamFileWriter.java index 78f28c1b1..494a2d5f3 100644 --- a/src/org/molgenis/util/plink/writers/FamFileWriter.java +++ b/src/org/molgenis/util/plink/writers/FamFileWriter.java @@ -1,6 +1,9 @@ package org.molgenis.util.plink.writers; +import java.io.Closeable; import java.io.File; +import java.io.IOException; +import java.util.Arrays; import java.util.List; import org.molgenis.util.CsvFileWriter; @@ -9,37 +12,44 @@ /** * Write MAP file entries to a selected location. */ -public class FamFileWriter +public class FamFileWriter implements Closeable { private CsvFileWriter writer; public FamFileWriter(File famFile) throws Exception { writer = new CsvFileWriter(famFile); - writer.setHeaders(FamEntry.famHeader()); - writer.setSeparator(" "); + writer.setHeaders(Arrays.asList(FamEntry.famHeader())); + writer.setSeparator(' '); } /** * Close the underlying writer. + * + * @throws IOException */ - public void close() + @Override + public void close() throws IOException { writer.close(); } /** * Write a single entry. + * + * @throws IOException */ - public void writeSingle(FamEntry fam) + public void writeSingle(FamEntry fam) throws IOException { writer.writeRow(FamEntry.famToTuple(fam)); } /** * Write multiple entries in order. + * + * @throws IOException */ - public void writeMulti(List fams) + public void writeMulti(List fams) throws IOException { for (FamEntry fam : fams) { @@ -49,14 +59,21 @@ public void writeMulti(List fams) /** * Write all entries and close the writer. + * + * @throws IOException */ - public void writeAll(List fams) + public void writeAll(List fams) throws IOException { - for (FamEntry fam : fams) + try { - writer.writeRow(FamEntry.famToTuple(fam)); + for (FamEntry fam : fams) + { + writer.writeRow(FamEntry.famToTuple(fam)); + } + } + finally + { + writer.close(); } - writer.close(); } - } diff --git a/src/org/molgenis/util/plink/writers/MapFileWriter.java b/src/org/molgenis/util/plink/writers/MapFileWriter.java index cb694fd63..772ee58f8 100644 --- a/src/org/molgenis/util/plink/writers/MapFileWriter.java +++ b/src/org/molgenis/util/plink/writers/MapFileWriter.java @@ -1,6 +1,9 @@ package org.molgenis.util.plink.writers; +import java.io.Closeable; import java.io.File; +import java.io.IOException; +import java.util.Arrays; import java.util.List; import org.molgenis.util.CsvFileWriter; @@ -9,37 +12,44 @@ /** * Write MAP file entries to a selected location. */ -public class MapFileWriter +public class MapFileWriter implements Closeable { private CsvFileWriter writer; public MapFileWriter(File mapFile) throws Exception { writer = new CsvFileWriter(mapFile); - writer.setHeaders(MapEntry.mapHeader()); - writer.setSeparator(" "); + writer.setHeaders(Arrays.asList(MapEntry.mapHeader())); + writer.setSeparator(' '); } /** * Close the underlying writer. + * + * @throws IOException */ - public void close() + @Override + public void close() throws IOException { writer.close(); } /** * Write a single entry. + * + * @throws IOException */ - public void writeSingle(MapEntry map) + public void writeSingle(MapEntry map) throws IOException { writer.writeRow(MapEntry.mapToTuple(map)); } /** * Write multiple entries in order. + * + * @throws IOException */ - public void writeMulti(List maps) + public void writeMulti(List maps) throws IOException { for (MapEntry map : maps) { @@ -49,14 +59,21 @@ public void writeMulti(List maps) /** * Write all entries and close the writer. + * + * @throws IOException */ - public void writeAll(List maps) + public void writeAll(List maps) throws IOException { - for (MapEntry map : maps) + try { - writer.writeRow(MapEntry.mapToTuple(map)); + for (MapEntry map : maps) + { + writer.writeRow(MapEntry.mapToTuple(map)); + } + } + finally + { + writer.close(); } - writer.close(); } - } diff --git a/src/org/molgenis/util/plink/writers/PedFileWriter.java b/src/org/molgenis/util/plink/writers/PedFileWriter.java index 4df95daea..44933b666 100644 --- a/src/org/molgenis/util/plink/writers/PedFileWriter.java +++ b/src/org/molgenis/util/plink/writers/PedFileWriter.java @@ -1,6 +1,9 @@ package org.molgenis.util.plink.writers; +import java.io.Closeable; import java.io.File; +import java.io.IOException; +import java.util.Arrays; import java.util.List; import org.molgenis.util.CsvFileWriter; @@ -9,38 +12,45 @@ /** * Write MAP file entries to a selected location. */ -public class PedFileWriter +public class PedFileWriter implements Closeable { private CsvFileWriter writer; public PedFileWriter(File pedFile) throws Exception { writer = new CsvFileWriter(pedFile); - writer.setHeaders(PedEntry.pedHeader()); - writer.setSeparator(" "); - writer.setListSeparator(" "); + writer.setHeaders(Arrays.asList(PedEntry.pedHeader())); + writer.setSeparator(' '); + writer.setListSeparator(' '); } /** * Close the underlying writer. + * + * @throws IOException */ - public void close() + @Override + public void close() throws IOException { writer.close(); } /** * Write a single entry. + * + * @throws IOException */ - public void writeSingle(PedEntry ped) + public void writeSingle(PedEntry ped) throws IOException { writer.writeRow(PedEntry.pedToTuple(ped)); } /** * Write multiple entries in order. + * + * @throws IOException */ - public void writeMulti(List peds) + public void writeMulti(List peds) throws IOException { for (PedEntry ped : peds) { @@ -50,14 +60,21 @@ public void writeMulti(List peds) /** * Write all entries and close the writer. + * + * @throws IOException */ - public void writeAll(List peds) + public void writeAll(List peds) throws IOException { - for (PedEntry ped : peds) + try { - writer.writeRow(PedEntry.pedToTuple(ped)); + for (PedEntry ped : peds) + { + writer.writeRow(PedEntry.pedToTuple(ped)); + } + } + finally + { + writer.close(); } - writer.close(); } - } diff --git a/src/org/molgenis/util/plink/writers/TpedFileWriter.java b/src/org/molgenis/util/plink/writers/TpedFileWriter.java index 878707547..b4a04fe9a 100644 --- a/src/org/molgenis/util/plink/writers/TpedFileWriter.java +++ b/src/org/molgenis/util/plink/writers/TpedFileWriter.java @@ -1,6 +1,9 @@ package org.molgenis.util.plink.writers; +import java.io.Closeable; import java.io.File; +import java.io.IOException; +import java.util.Arrays; import java.util.List; import org.molgenis.util.CsvFileWriter; @@ -9,38 +12,45 @@ /** * Write MAP file entries to a selected location. */ -public class TpedFileWriter +public class TpedFileWriter implements Closeable { private CsvFileWriter writer; public TpedFileWriter(File tpedFile) throws Exception { writer = new CsvFileWriter(tpedFile); - writer.setHeaders(TpedEntry.tpedHeader()); - writer.setSeparator(" "); - writer.setListSeparator(" "); + writer.setHeaders(Arrays.asList(TpedEntry.tpedHeader())); + writer.setSeparator(' '); + writer.setListSeparator(' '); } /** * Close the underlying writer. + * + * @throws IOException */ - public void close() + @Override + public void close() throws IOException { writer.close(); } /** * Write a single entry. + * + * @throws IOException */ - public void writeSingle(TpedEntry tped) + public void writeSingle(TpedEntry tped) throws IOException { writer.writeRow(TpedEntry.tpedToTuple(tped)); } /** * Write multiple entries in order. + * + * @throws IOException */ - public void writeMulti(List tpeds) + public void writeMulti(List tpeds) throws IOException { for (TpedEntry tped : tpeds) { @@ -50,14 +60,21 @@ public void writeMulti(List tpeds) /** * Write all entries and close the writer. + * + * @throws IOException */ - public void writeAll(List tpeds) + public void writeAll(List tpeds) throws IOException { - for (TpedEntry tped : tpeds) + try { - writer.writeRow(TpedEntry.tpedToTuple(tped)); + for (TpedEntry tped : tpeds) + { + writer.writeRow(TpedEntry.tpedToTuple(tped)); + } + } + finally + { + writer.close(); } - writer.close(); } - } From 8ebc907023cb194e915a9c75600621c4a05ab1a8 Mon Sep 17 00:00:00 2001 From: erwinwinder Date: Thu, 8 Nov 2012 11:56:27 +0100 Subject: [PATCH 048/108] Prevent downloading of system entites, return 404 not found --- .../services/MolgenisDownloadService.java | 52 ++++++++++++++----- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/src/org/molgenis/framework/server/services/MolgenisDownloadService.java b/src/org/molgenis/framework/server/services/MolgenisDownloadService.java index a645bffde..03e309a06 100644 --- a/src/org/molgenis/framework/server/services/MolgenisDownloadService.java +++ b/src/org/molgenis/framework/server/services/MolgenisDownloadService.java @@ -85,21 +85,42 @@ public void handleRequest(MolgenisRequest req, MolgenisResponse res) throws Pars showAvailableDownloads(out, db, req); out.println(""); } - else if (req.getRequest().getQueryString() != null - && req.getRequest().getQueryString().equals("__showQueryDialogue=true")) + else { - out.println(""); - out.println(authStatus.getPrintMe()); - if (req.getDatabase().getLogin().isAuthenticated()) + // Check if this entity exists and is downloadable + List downloadableEntities = getDownloadableEntities(db); + boolean found = false; + for (int i = 0; i < downloadableEntities.size() && !found; i++) { - out.println(MolgenisServiceAuthenticationHelper.displayLogoutForm()); + String name = downloadableEntities.get(i).getName(); + if ((name != null) && name.equals(entityName)) + { + found = true; + } + } + + if (!found) + { + res.getResponse().sendError(404);// NOT FOUND + return; + } + + if (req.getRequest().getQueryString() != null + && req.getRequest().getQueryString().equals("__showQueryDialogue=true")) + { + out.println(""); + out.println(authStatus.getPrintMe()); + if (req.getDatabase().getLogin().isAuthenticated()) + { + out.println(MolgenisServiceAuthenticationHelper.displayLogoutForm()); + } + showFilterableDownload(out, entityName, db); + out.println(""); + } + else + { + executeQuery(out, req, db, entityName); } - showFilterableDownload(out, entityName, db); - out.println(""); - } - else - { - executeQuery(out, req, db, entityName); } } } @@ -165,7 +186,7 @@ private void showAvailableDownloads(PrintWriter out, Database db, MolgenisReques out.println("You can download these data:
"); out.println(""); - for (org.molgenis.model.elements.Entity eClass : db.getMetaData().getEntities(false, false)) + for (org.molgenis.model.elements.Entity eClass : getDownloadableEntities(db)) { String name = eClass.getName(); Class klazz = db.getClassForName(name); @@ -187,6 +208,11 @@ private void showAvailableDownloads(PrintWriter out, Database db, MolgenisReques out.println("
"); } + private List getDownloadableEntities(Database db) throws DatabaseException + { + return db.getMetaData().getEntities(false, false); + } + private List createQueryRules(MolgenisRequest req, Class klazz) throws Exception { List rulesList = new ArrayList(); From 82e3ec2699afb727de520b66f94b2a7ff6a434da Mon Sep 17 00:00:00 2001 From: erwinwinder Date: Thu, 8 Nov 2012 11:57:07 +0100 Subject: [PATCH 049/108] Add unit test for MolgenisDownloadService --- .classpath | 2 + lib/spring-core-3.1.2.RELEASE.jar | Bin 0 -> 449649 bytes lib/spring-test-3.1.2.RELEASE.jar | Bin 0 -> 229461 bytes src/org/molgenis/mock/MockDatabase.java | 392 ++++++++++++++++++ src/org/molgenis/mock/MockLogin.java | 150 +++++++ .../services/MolgenisDownloadServiceTest.java | 105 +++++ 6 files changed, 649 insertions(+) create mode 100644 lib/spring-core-3.1.2.RELEASE.jar create mode 100644 lib/spring-test-3.1.2.RELEASE.jar create mode 100644 src/org/molgenis/mock/MockDatabase.java create mode 100644 src/org/molgenis/mock/MockLogin.java create mode 100644 test/java/org/molgenis/framework/server/services/MolgenisDownloadServiceTest.java diff --git a/.classpath b/.classpath index 76bb685d1..441afbffd 100644 --- a/.classpath +++ b/.classpath @@ -545,5 +545,7 @@ + + diff --git a/lib/spring-core-3.1.2.RELEASE.jar b/lib/spring-core-3.1.2.RELEASE.jar new file mode 100644 index 0000000000000000000000000000000000000000..8d054dd97d29fe197063d84c3336f9ff1ee558fd GIT binary patch literal 449649 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℘tVCj9G^Zv44k*L!)?~vz+mtB(R4tgRhUytJg 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

?hNOGA?Rm3ew_ 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®Scgo%8m3qq7#AQGIc=qrwc$^4KC5VkWm!P0 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