diff --git a/src/main/java/caosdb/datetime/SemiCompleteDateTime.java b/src/main/java/caosdb/datetime/SemiCompleteDateTime.java index 9082e5bf94ee26d4701f2417c8f5cc771cfa7e1e..55a7f5d567f1ec5be9902135f45393699195d549 100644 --- a/src/main/java/caosdb/datetime/SemiCompleteDateTime.java +++ b/src/main/java/caosdb/datetime/SemiCompleteDateTime.java @@ -24,7 +24,6 @@ package caosdb.datetime; import caosdb.server.datatype.AbstractDatatype.Table; import java.util.TimeZone; -import org.jdom2.Attribute; import org.jdom2.Element; public class SemiCompleteDateTime extends FragmentDateTime implements Interval { @@ -47,11 +46,6 @@ public class SemiCompleteDateTime extends FragmentDateTime implements Interval { throw new UnsupportedOperationException("This DateTime cannot be stored to the database."); } - @Override - public void addToAttribute(final Attribute a) { - throw new UnsupportedOperationException("This DateTime cannot be added to an xml attribute."); - } - @Override public String toDateTimeString(final TimeZone tz) { final StringBuilder sb = new StringBuilder(); diff --git a/src/main/java/caosdb/datetime/UTCDateTime.java b/src/main/java/caosdb/datetime/UTCDateTime.java index 72a55faaa1d7822816a8ceab875e8ec8928f1529..215de67befe403aa6a202ed178244b3de898d23e 100644 --- a/src/main/java/caosdb/datetime/UTCDateTime.java +++ b/src/main/java/caosdb/datetime/UTCDateTime.java @@ -27,7 +27,6 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.TimeZone; -import org.jdom2.Attribute; import org.jdom2.Element; public class UTCDateTime implements Interval { @@ -292,11 +291,6 @@ public class UTCDateTime implements Interval { return sb.toString(); } - @Override - public void addToAttribute(final Attribute a) { - a.setValue(toDateTimeString(TimeZone.getDefault())); - } - @Override public String toString() { throw new NullPointerException("toString method!!!"); diff --git a/src/main/java/caosdb/server/database/backend/transaction/InsertEntityProperties.java b/src/main/java/caosdb/server/database/backend/transaction/InsertEntityProperties.java index 05e4dbce0087ce8073d124136419f760a66d581c..a777d4206c4dc0822a4f27912a3ec708c03edd56 100644 --- a/src/main/java/caosdb/server/database/backend/transaction/InsertEntityProperties.java +++ b/src/main/java/caosdb/server/database/backend/transaction/InsertEntityProperties.java @@ -30,6 +30,7 @@ import caosdb.server.database.proto.FlatProperty; import caosdb.server.datatype.AbstractCollectionDatatype; import caosdb.server.datatype.AbstractDatatype.Table; import caosdb.server.datatype.CollectionValue; +import caosdb.server.datatype.IndexedSingleValue; import caosdb.server.datatype.SingleValue; import caosdb.server.entity.EntityInterface; import caosdb.server.entity.Role; @@ -37,6 +38,7 @@ import caosdb.server.entity.StatementStatus; import caosdb.server.entity.wrapper.Property; import java.util.ArrayList; import java.util.Deque; +import java.util.Iterator; import java.util.List; public class InsertEntityProperties extends BackendTransaction { @@ -111,22 +113,33 @@ public class InsertEntityProperties extends BackendTransaction { if (property.getValue() instanceof CollectionValue) { // insert collection of values final CollectionValue v = (CollectionValue) property.getValue(); + Iterator<IndexedSingleValue> iterator = v.iterator(); + final SingleValue firstValue = iterator.next(); // insert 2nd to nth item for (int i = 1; i < v.size(); i++) { - final SingleValue vi = v.get(i); - table = (vi != null ? vi.getTable() : Table.null_data); + final SingleValue vi = iterator.next(); fp.idx = i; - fp.value = (vi != null ? vi.toDatabaseString() : null); + if (vi == null) { + fp.value = null; + table = Table.null_data; + } else { + fp.value = vi.toDatabaseString(); + table = vi.getTable(); + } t.execute( domain, (entity != null ? entity : property.getDomain()), fp, table, unit_sig); } // insert first item - final SingleValue vi = v.get(0); fp.idx = 0; - fp.value = vi.toDatabaseString(); - table = (vi != null ? vi.getTable() : Table.null_data); + if (firstValue == null) { + fp.value = null; + table = Table.null_data; + } else { + fp.value = firstValue.toDatabaseString(); + table = firstValue.getTable(); + } } else { // insert single value diff --git a/src/main/java/caosdb/server/datatype/AbstractEnumValue.java b/src/main/java/caosdb/server/datatype/AbstractEnumValue.java index 9dd3a687614537be8cb97b870143af1dc031bf3b..33303f22eccbca7fcffafdb9737d4e0880dd98ca 100644 --- a/src/main/java/caosdb/server/datatype/AbstractEnumValue.java +++ b/src/main/java/caosdb/server/datatype/AbstractEnumValue.java @@ -24,7 +24,6 @@ package caosdb.server.datatype; import caosdb.server.datatype.AbstractDatatype.Table; import com.google.common.base.Objects; -import org.jdom2.Attribute; import org.jdom2.Element; public abstract class AbstractEnumValue implements SingleValue { @@ -50,11 +49,6 @@ public abstract class AbstractEnumValue implements SingleValue { return this.value.toString(); } - @Override - public final void addToAttribute(final Attribute a) { - a.setValue(this.value.toString()); - } - @Override public boolean equals(final Object obj) { if (obj instanceof AbstractEnumValue) { diff --git a/src/main/java/caosdb/server/datatype/CollectionValue.java b/src/main/java/caosdb/server/datatype/CollectionValue.java index 65c8fa5f45a604edd61b1b5c190a9879f6db0086..3e70de7322af17cf27232446c1e4c984e73ae73a 100644 --- a/src/main/java/caosdb/server/datatype/CollectionValue.java +++ b/src/main/java/caosdb/server/datatype/CollectionValue.java @@ -22,30 +22,30 @@ */ package caosdb.server.datatype; -import java.util.LinkedList; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; import org.jdom2.Element; -public class CollectionValue extends LinkedList<IndexedSingleValue> implements Value { +public class CollectionValue implements Value, Iterable<IndexedSingleValue> { - private static final long serialVersionUID = 1L; + private List<IndexedSingleValue> list = new ArrayList<>(); @Override public void addToElement(final Element e) { for (final SingleValue v : this) { final Element valueElem = new Element("Value"); - if (v != null) { - v.addToElement(valueElem); - } + v.addToElement(valueElem); e.addContent(valueElem); } } - @Override public boolean add(final IndexedSingleValue e) { if (e == null) { - return super.add(new IndexedSingleValue(null)); + return list.add(new IndexedSingleValue(null)); } - return super.add(e); + return list.add(e); } public void add(final SingleValue v) { @@ -67,4 +67,17 @@ public class CollectionValue extends LinkedList<IndexedSingleValue> implements V throw new IllegalArgumentException("Expected a SingleValue."); } } + + @Override + public Iterator<IndexedSingleValue> iterator() { + return list.iterator(); + } + + public void sort() { + Collections.sort(list); + } + + public int size() { + return list.size(); + } } diff --git a/src/main/java/caosdb/server/datatype/GenericValue.java b/src/main/java/caosdb/server/datatype/GenericValue.java index 8e8f1dd41e49305ddba2090b1f897a35a3765f93..cffd62bbcbbb2dfffab2a7c55b2b7861b2bd9aae 100644 --- a/src/main/java/caosdb/server/datatype/GenericValue.java +++ b/src/main/java/caosdb/server/datatype/GenericValue.java @@ -24,7 +24,6 @@ package caosdb.server.datatype; import caosdb.server.datatype.AbstractDatatype.Table; import com.google.common.base.Objects; -import org.jdom2.Attribute; import org.jdom2.Element; public class GenericValue implements SingleValue { @@ -61,7 +60,12 @@ public class GenericValue implements SingleValue { @Override public void addToElement(final Element e) { - e.addContent(this.value.toString()); + if (this.value instanceof String && ((String) this.value).isEmpty()) { + Element empty = new Element("EmptyString"); + e.addContent(empty); + } else { + e.addContent(this.value.toString()); + } } @Override @@ -74,11 +78,6 @@ public class GenericValue implements SingleValue { return this.value.toString(); } - @Override - public void addToAttribute(final Attribute a) { - a.setValue(this.value.toString()); - } - @Override public String toString() { return toDatabaseString(); diff --git a/src/main/java/caosdb/server/datatype/IndexedSingleValue.java b/src/main/java/caosdb/server/datatype/IndexedSingleValue.java index 018582285b0bcd4955db1de7310c66a47132501b..f33db43ef0ca8aa50bc9fcca54a232965e98f29a 100644 --- a/src/main/java/caosdb/server/datatype/IndexedSingleValue.java +++ b/src/main/java/caosdb/server/datatype/IndexedSingleValue.java @@ -23,7 +23,6 @@ package caosdb.server.datatype; import caosdb.server.datatype.AbstractDatatype.Table; -import org.jdom2.Attribute; import org.jdom2.Element; public class IndexedSingleValue implements SingleValue, Comparable<IndexedSingleValue> { @@ -63,13 +62,6 @@ public class IndexedSingleValue implements SingleValue, Comparable<IndexedSingle return this.wrapped.toDatabaseString(); } - @Override - public void addToAttribute(final Attribute a) { - if (this.wrapped != null) { - this.wrapped.addToAttribute(a); - } - } - public void setIndex(final int i) { this.index = i; } diff --git a/src/main/java/caosdb/server/datatype/ListDatatype.java b/src/main/java/caosdb/server/datatype/ListDatatype.java index e61a1a3bf229b9c240f2d2318ca126c0f758f5c0..877901458dcf94fee17a9f5ac8c69f1026609322 100644 --- a/src/main/java/caosdb/server/datatype/ListDatatype.java +++ b/src/main/java/caosdb/server/datatype/ListDatatype.java @@ -23,7 +23,6 @@ package caosdb.server.datatype; import caosdb.server.entity.Message; -import java.util.Collections; public class ListDatatype extends AbstractCollectionDatatype { @@ -38,9 +37,13 @@ public class ListDatatype extends AbstractCollectionDatatype { final CollectionValue ret = new CollectionValue(); if (value instanceof CollectionValue) { final CollectionValue colValue = (CollectionValue) value; - Collections.sort(colValue); + colValue.sort(); for (final IndexedSingleValue singleValue : colValue) { - ret.add(singleValue.getIndex(), getDatatype().parseValue(singleValue.getWrapped())); + if (singleValue.getWrapped() == null) { + ret.add(singleValue.getIndex(), null); + } else { + ret.add(singleValue.getIndex(), getDatatype().parseValue(singleValue.getWrapped())); + } } } else { ret.add(getDatatype().parseValue(value)); diff --git a/src/main/java/caosdb/server/datatype/ReferenceValue.java b/src/main/java/caosdb/server/datatype/ReferenceValue.java index 17fea7658963aaaafc96547d2afba202904fd508..25fa0b381cb436236dd6e3676970cf7e54c17776 100644 --- a/src/main/java/caosdb/server/datatype/ReferenceValue.java +++ b/src/main/java/caosdb/server/datatype/ReferenceValue.java @@ -26,7 +26,6 @@ import caosdb.server.datatype.AbstractDatatype.Table; import caosdb.server.entity.EntityInterface; import caosdb.server.entity.Message; import caosdb.server.utils.ServerMessages; -import org.jdom2.Attribute; import org.jdom2.Element; public class ReferenceValue implements SingleValue { @@ -122,11 +121,6 @@ public class ReferenceValue implements SingleValue { return toString(); } - @Override - public void addToAttribute(final Attribute a) { - a.setValue(toString()); - } - @Override public boolean equals(final Object obj) { if (obj instanceof ReferenceValue) { diff --git a/src/main/java/caosdb/server/datatype/SingleValue.java b/src/main/java/caosdb/server/datatype/SingleValue.java index 60f370ed82735eb4a9ef946800c96c4c4735c231..2ae17a6122035387f31466a74c0468d9fde80e36 100644 --- a/src/main/java/caosdb/server/datatype/SingleValue.java +++ b/src/main/java/caosdb/server/datatype/SingleValue.java @@ -22,13 +22,9 @@ */ package caosdb.server.datatype; -import org.jdom2.Attribute; - public interface SingleValue extends Value { public AbstractDatatype.Table getTable(); public String toDatabaseString(); - - public void addToAttribute(Attribute a); } diff --git a/src/main/java/caosdb/server/datatype/TimespanDatatype.java b/src/main/java/caosdb/server/datatype/TimespanDatatype.java deleted file mode 100644 index bdf838007779cc873319af877016a86e7f222a9a..0000000000000000000000000000000000000000 --- a/src/main/java/caosdb/server/datatype/TimespanDatatype.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * ** header v3.0 - * This file is a part of the CaosDB Project. - * - * Copyright (C) 2018 Research Group Biomedical Physics, - * Max-Planck-Institute for Dynamics and Self-Organization Göttingen - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - * - * ** end header - */ -package caosdb.server.datatype; - -import caosdb.server.datatype.AbstractDatatype.Table; -import caosdb.server.entity.Message; -import org.jdom2.Attribute; -import org.jdom2.Element; - -@DatatypeDefinition(name = "Timespan") -public class TimespanDatatype extends AbstractDatatype { - - public static final Message TIMESPAN_NOT_PARSABLE = new Message(219, "Timespan not parsable."); - - @Override - public Timespan parseValue(final Object value) throws Message { - Timespan ret = null; - try { - ret = Timespan.parse(value.toString()); - } catch (final NumberFormatException e) { - // TODO define correct error message - throw TIMESPAN_NOT_PARSABLE; - } - return ret; - } -} - -class Timespan implements SingleValue { - public enum Unit { - YEARS, - MONTHS, - WEEKS, - DAYS, - HOURS, - MINUTES, - SECONDS, - USERDEFINED - }; - - static Timespan parse(final String str) throws NumberFormatException { - - final Timespan ts = new Timespan(); - String[] parts; - if (str.matches("^\\s*[0-9]+(\\.[0-9]+)?([eE]-?[0-9]+)?\\s*[A-Za-z]+\\s*$")) { - parts = new String[2]; - parts[0] = str.split("\\s*?[A-Za-z]*$")[0]; - parts[1] = str.split("^[0-9]+(\\.[0-9]+)?([eE]-?[0-9]+)?\\s*")[1]; - - if (parts[1].equalsIgnoreCase(Unit.YEARS.toString()) || parts[1].equalsIgnoreCase("y")) { - ts.setUnit(Unit.YEARS); - ts.factor = 31556925.261; - } else if (parts[1].equalsIgnoreCase(Unit.MONTHS.toString()) || parts[1].equals("M")) { - ts.setUnit(Unit.MONTHS); - ts.factor = 31556925.261 / 12; - } else if (parts[1].equalsIgnoreCase(Unit.WEEKS.toString()) - || parts[1].equalsIgnoreCase("w")) { - ts.setUnit(Unit.WEEKS); - ts.factor = 7 * 86400.0; - } else if (parts[1].equalsIgnoreCase(Unit.DAYS.toString()) - || parts[1].equalsIgnoreCase("d")) { - ts.setUnit(Unit.DAYS); - ts.factor = 86400.0; - } else if (parts[1].equalsIgnoreCase(Unit.HOURS.toString()) - || parts[1].equalsIgnoreCase("h")) { - ts.setUnit(Unit.HOURS); - ts.factor = 3600.0; - } else if (parts[1].equalsIgnoreCase(Unit.MINUTES.toString()) || parts[1].equals("m")) { - ts.setUnit(Unit.MINUTES); - ts.factor = 60.0; - } else if (parts[1].equalsIgnoreCase(Unit.SECONDS.toString()) - || parts[1].equalsIgnoreCase("s")) { - ts.setUnit(Unit.SECONDS); - } else { - ts.setUnit(Unit.USERDEFINED); - } - ts.value = Double.parseDouble(parts[0]); - } else if (str.matches("^\\s*[0-9]+(\\.[0-9]+)?([eE]-?[0-9]+)?\\s*$")) { - ts.setUnit(null); - ts.value = Double.parseDouble(str); - - } else { - throw new NumberFormatException("Couldn't parse String to Timespan."); - } - return ts; - } - - private double value = 0; - private double factor = 1; - - private Unit unit = null; - - public double toSeconds() { - return value * factor; - } - - @Override - public String toString() { - return Double.toString(value); - } - - public String getUnit() { - return unit.toString(); - } - - private void setUnit(final Unit unit) { - this.unit = unit; - } - - @Override - public void addToElement(Element e) { - e.addContent(toString()); - } - - @Override - public Table getTable() { - return Table.double_data; - } - - @Override - public String toDatabaseString() { - return toString(); - } - - @Override - public void addToAttribute(Attribute a) { - a.setValue(toString()); - } -} diff --git a/src/main/java/caosdb/server/entity/Entity.java b/src/main/java/caosdb/server/entity/Entity.java index 19cd930706e3f3aa691853b020f000191ed8d142..5c997d7ff8f9f98191ba768c82569a79ca056cf2 100644 --- a/src/main/java/caosdb/server/entity/Entity.java +++ b/src/main/java/caosdb/server/entity/Entity.java @@ -794,10 +794,15 @@ public class Entity extends AbstractObservable implements EntityInterface { final CollectionValue vals = new CollectionValue(); int pidx = 0; for (final Element pe : element.getChildren()) { - if (pe.getName().equalsIgnoreCase("Value")) { + if (pe.getName().equalsIgnoreCase("EmptyString")) { + // special case: empty string which cannot be distinguished from null + // values otherwise. + setValue(new GenericValue("")); + } else if (pe.getName().equalsIgnoreCase("Value")) { // Parse sub-elements which represent VALUES of this entity. - - if (pe.getText() != null && pe.getTextTrim() != "") { + if (pe.getChild("EmptyString") != null) { + vals.add(new GenericValue("")); + } else if (pe.getText() != null && pe.getTextTrim() != "") { vals.add(new GenericValue(pe.getTextTrim())); } else { vals.add(null); diff --git a/src/main/java/caosdb/server/entity/xml/PropertyToElementStrategy.java b/src/main/java/caosdb/server/entity/xml/PropertyToElementStrategy.java index 5fe0c0c504efdf66260a3ee36c64c279b9928d87..a88b47cbd411d1b62764a2d6cc130b9ff7d04cae 100644 --- a/src/main/java/caosdb/server/entity/xml/PropertyToElementStrategy.java +++ b/src/main/java/caosdb/server/entity/xml/PropertyToElementStrategy.java @@ -47,9 +47,8 @@ public class PropertyToElementStrategy extends EntityToElementStrategy { switch (MagicTypes.getType(entity.getId())) { case UNIT: if (setFieldStrategy.isToBeSet("unit") && v != null) { - final Attribute a = new Attribute("unit", ""); + final Attribute a = new Attribute("unit", ((SingleValue) v).toDatabaseString()); element.setAttribute(a); - ((SingleValue) v).addToAttribute(a); } return element; default: diff --git a/src/main/java/caosdb/server/query/CQLParser.g4 b/src/main/java/caosdb/server/query/CQLParser.g4 index a545871d9bf82de720da6dbfab712fbaab245da7..af981d6a2d027892c144070323aa924a5d7674a5 100644 --- a/src/main/java/caosdb/server/query/CQLParser.g4 +++ b/src/main/java/caosdb/server/query/CQLParser.g4 @@ -441,7 +441,7 @@ minmax returns [String agg] ; value returns [String str] -: +: number {$str = $text;} | datetime {$str = $text;} | atom {$str = $atom.ep.toString();} @@ -483,7 +483,7 @@ single_quoted returns [Query.Pattern ep] locals [StringBuffer sb, int patternTyp r = SINGLE_QUOTE_STAR {$sb.append($r.text); $patternType = Query.Pattern.TYPE_LIKE;} | s = ~SINGLE_QUOTE_END {$sb.append($s.text);} - )+? + )*? SINGLE_QUOTE_END ; @@ -503,6 +503,6 @@ double_quoted returns [Query.Pattern ep] locals [StringBuffer sb, int patternTyp r = DOUBLE_QUOTE_STAR {$sb.append($r.text); $patternType = Query.Pattern.TYPE_LIKE;} | s = ~DOUBLE_QUOTE_END {$sb.append($s.text);} - )+? + )*? DOUBLE_QUOTE_END ; diff --git a/src/test/java/caosdb/server/query/TestCQL.java b/src/test/java/caosdb/server/query/TestCQL.java index 6f311ad2b4425bda054c10e5956e39d7d088d6c4..54d4339b2fa5b41eb6ad3f4823be0b48e131fd56 100644 --- a/src/test/java/caosdb/server/query/TestCQL.java +++ b/src/test/java/caosdb/server/query/TestCQL.java @@ -28,6 +28,15 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; + +import caosdb.server.CaosDBServer; +import caosdb.server.database.access.Access; +import caosdb.server.database.backend.implementation.MySQL.ConnectionException; +import caosdb.server.database.exceptions.TransactionException; +import caosdb.server.query.CQLParser.CqContext; +import caosdb.server.query.Query.Pattern; +import caosdb.server.query.Query.QueryException; +import caosdb.server.utils.Initialization; import java.io.IOException; import java.sql.SQLException; import java.util.LinkedList; @@ -38,14 +47,6 @@ import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.tree.ParseTree; import org.junit.BeforeClass; import org.junit.Test; -import caosdb.server.CaosDBServer; -import caosdb.server.database.access.Access; -import caosdb.server.database.backend.implementation.MySQL.ConnectionException; -import caosdb.server.database.exceptions.TransactionException; -import caosdb.server.query.CQLParser.CqContext; -import caosdb.server.query.Query.Pattern; -import caosdb.server.query.Query.QueryException; -import caosdb.server.utils.Initialization; public class TestCQL { @@ -235,6 +236,8 @@ public class TestCQL { String referenceByLikePattern = "FIND ENTITY WHICH IS REFERENCED BY *name*"; + String emptyTextValue = "FIND ENTITY WITH prop=''"; + @Test public void testQuery1() throws InterruptedException, SQLException, ConnectionException, QueryException { @@ -6219,6 +6222,7 @@ public class TestCQL { assertEquals("SAT(SimulationData/2016\\_single/2018-01-10/%)", storedAt.toString()); } + @Test public void testFilePathInQuotes2() { CQLLexer lexer = new CQLLexer(CharStreams.fromString(query_filepath_quotes_2)); final CommonTokenStream tokens = new CommonTokenStream(lexer); @@ -6246,11 +6250,23 @@ public class TestCQL { final ParseTree satFilter = whichclause.getChild(1).getChild(0); assertEquals(2, satFilter.getChildCount()); assertEquals("IS STORED AT", satFilter.getChild(0).getText()); - assertEquals("'/SimulationData/2016_single/2018-01-10/**'", satFilter.getChild(1).getText()); + assertEquals("/SimulationData/2016_single/2018-01-10/**", satFilter.getChild(1).getText()); assertTrue(sfq.filter instanceof StoredAt); final StoredAt storedAt = (StoredAt) sfq.filter; assertTrue(storedAt.pattern_matching); - assertEquals("SAT(SimulationData/2016\\_single/2018-01-10/%%)", storedAt.toString()); + assertEquals("SAT(SimulationData/2016\\_single/2018-01-10/%)", storedAt.toString()); + } + + @Test + public void testEmptyTextValue() { + CQLLexer lexer = new CQLLexer(CharStreams.fromString(emptyTextValue)); + final CommonTokenStream tokens = new CommonTokenStream(lexer); + + final CQLParser parser = new CQLParser(tokens); + final CqContext sfq = parser.cq(); + + EntityFilterInterface pov = sfq.filter; + assertEquals("POV(prop,=,)", pov.toString()); } }