Implemented DTO constructions for the annotation description service.
Change-Id: I3b68d6525106dd449ca38e63448973b65ced3cfc
diff --git a/src/main/java/de/ids_mannheim/korap/dao/AnnotationDao.java b/src/main/java/de/ids_mannheim/korap/dao/AnnotationDao.java
index a6394aa..366546e 100644
--- a/src/main/java/de/ids_mannheim/korap/dao/AnnotationDao.java
+++ b/src/main/java/de/ids_mannheim/korap/dao/AnnotationDao.java
@@ -10,8 +10,6 @@
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import de.ids_mannheim.korap.entity.AnnotationPair;
@@ -27,7 +25,6 @@
@Component
public class AnnotationDao {
- private static Logger jlog = LoggerFactory.getLogger(AnnotationDao.class);
@PersistenceContext
private EntityManager entityManager;
@@ -56,20 +53,29 @@
* layer in the given foundry. If foundry is empty, retrieves data
* for all foundry and layer pairs.
*
- * @param foundry a foundry code
- * @param layer a layer code
+ * @param foundry
+ * a foundry code
+ * @param layer
+ * a layer code
* @return a list of foundry-layer pairs.
*/
public List<AnnotationPair> getAnnotationDescriptions (String foundry,
String layer) {
-
+
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Object> query = criteriaBuilder.createQuery();
Root<AnnotationPair> annotationPair = query.from(AnnotationPair.class);
annotationPair.fetch("annotation1");
annotationPair.fetch("annotation2");
annotationPair.fetch("values");
+
+ // EM: Hibernate bug in join n:m (see AnnotationPair.values).
+ // There should not be any redundant AnnotationPair.
+ // The redundancy can be alsp avoided with fetch=FetchType.EAGER
+ // because Hibernate does 2 selects.
+ query.distinct(true);
query = query.select(annotationPair);
+
if (!foundry.isEmpty()) {
Predicate foundryPredicate = criteriaBuilder.equal(
annotationPair.get("annotation1").get("code"), foundry);
@@ -85,7 +91,6 @@
}
}
- query.distinct(true);
Query q = entityManager.createQuery(query);
return q.getResultList();
}
diff --git a/src/main/java/de/ids_mannheim/korap/dao/ResourceDao.java b/src/main/java/de/ids_mannheim/korap/dao/ResourceDao.java
index 0dd68f8..0a14a3c 100644
--- a/src/main/java/de/ids_mannheim/korap/dao/ResourceDao.java
+++ b/src/main/java/de/ids_mannheim/korap/dao/ResourceDao.java
@@ -9,8 +9,6 @@
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import de.ids_mannheim.korap.entity.Resource;
@@ -24,8 +22,6 @@
@Component
public class ResourceDao {
-// private static Logger jlog = LoggerFactory.getLogger(ResourceDao.class);
-
@PersistenceContext
private EntityManager entityManager;
diff --git a/src/main/java/de/ids_mannheim/korap/dto/FoundryDto.java b/src/main/java/de/ids_mannheim/korap/dto/FoundryDto.java
new file mode 100644
index 0000000..4d3ea6d
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/dto/FoundryDto.java
@@ -0,0 +1,32 @@
+package de.ids_mannheim.korap.dto;
+
+import java.util.List;
+import java.util.Map;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Data transfer object for annotation descriptions (e.g. for
+ * Kalamar).
+ *
+ * @author margaretha
+ *
+ */
+@Getter
+@Setter
+public class FoundryDto {
+
+ private String code;
+ private String description;
+ private List<Layer> layers;
+
+ @Getter
+ @Setter
+ public class Layer {
+ private String code;
+ private String description;
+ // EM: pairs of annotation values and their description
+ private Map<String, String> tags;
+ }
+}
diff --git a/src/main/java/de/ids_mannheim/korap/dto/LayerDto.java b/src/main/java/de/ids_mannheim/korap/dto/LayerDto.java
index 4a6c0c7..b9884d8 100644
--- a/src/main/java/de/ids_mannheim/korap/dto/LayerDto.java
+++ b/src/main/java/de/ids_mannheim/korap/dto/LayerDto.java
@@ -3,6 +3,12 @@
import lombok.Getter;
import lombok.Setter;
+/**
+ * Data transfer object for layer description (e.g. for KorapSRU).
+ *
+ * @author margaretha
+ *
+ */
@Getter
@Setter
public class LayerDto {
diff --git a/src/main/java/de/ids_mannheim/korap/dto/ResourceDto.java b/src/main/java/de/ids_mannheim/korap/dto/ResourceDto.java
index 45575df..3a14423 100644
--- a/src/main/java/de/ids_mannheim/korap/dto/ResourceDto.java
+++ b/src/main/java/de/ids_mannheim/korap/dto/ResourceDto.java
@@ -5,6 +5,11 @@
import lombok.Getter;
import lombok.Setter;
+/** Data transfer object for resource description (e.g. for KorapSRU).
+ *
+ * @author margaretha
+ *
+ */
@Setter
@Getter
public class ResourceDto {
diff --git a/src/main/java/de/ids_mannheim/korap/dto/converter/AnnotationConverter.java b/src/main/java/de/ids_mannheim/korap/dto/converter/AnnotationConverter.java
index 61a6f63..c049c2a 100644
--- a/src/main/java/de/ids_mannheim/korap/dto/converter/AnnotationConverter.java
+++ b/src/main/java/de/ids_mannheim/korap/dto/converter/AnnotationConverter.java
@@ -1,34 +1,139 @@
package de.ids_mannheim.korap.dto.converter;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import org.springframework.stereotype.Component;
+import de.ids_mannheim.korap.dto.FoundryDto;
+import de.ids_mannheim.korap.dto.FoundryDto.Layer;
import de.ids_mannheim.korap.dto.LayerDto;
+import de.ids_mannheim.korap.entity.Annotation;
import de.ids_mannheim.korap.entity.AnnotationPair;
+/**
+ * AnnotationConverter prepares data transfer objects (DTOs) from
+ * entities. The DTOs, for instance, are serialized into JSON in the
+ * controller classes and then sent as the response entity.
+ *
+ * @author margaretha
+ *
+ */
@Component
public class AnnotationConverter {
- public List<LayerDto> convertToLayerDto (List<AnnotationPair> layers) {
- List<LayerDto> layerDtos = new ArrayList<LayerDto>(layers.size());
+ /**
+ * Returns layer descriptions in a list of {@link LayerDto}s.
+ *
+ * @param pairs
+ * a list of {@link AnnotationPair}s
+ * @return a list of {@link LayerDto}s
+ */
+ public List<LayerDto> convertToLayerDto (List<AnnotationPair> pairs) {
+ List<LayerDto> layerDtos = new ArrayList<LayerDto>(pairs.size());
LayerDto dto;
String foundry, layer;
- for (AnnotationPair l : layers) {
+ for (AnnotationPair p : pairs) {
dto = new LayerDto();
- dto.setId(l.getId());
- dto.setDescription(l.getEnglishDescription());
+ dto.setId(p.getId());
+ dto.setDescription(p.getDescription());
- foundry = l.getAnnotation1().getCode();
+ foundry = p.getAnnotation1().getCode();
dto.setFoundry(foundry);
- layer = l.getAnnotation2().getCode();
+ layer = p.getAnnotation2().getCode();
dto.setLayer(layer);
dto.setCode(foundry + "/" + layer);
layerDtos.add(dto);
}
-
+
return layerDtos;
}
+
+
+ /**
+ * Returns foundry description in {@link FoundryDto}s
+ *
+ * @param pairs
+ * a list of {@link AnnotationPair}s
+ * @param language
+ * @return a list of {@link FoundryDto}s
+ */
+ public List<FoundryDto> convertToFoundryDto (List<AnnotationPair> pairs,
+ String language) {
+ List<FoundryDto> foundryDtos = new ArrayList<FoundryDto>(pairs.size());
+ Map<String, List<AnnotationPair>> foundryMap = createFoundryMap(pairs);
+
+ for (String key : foundryMap.keySet()) {
+ List<AnnotationPair> foundries = foundryMap.get(key);
+ List<Layer> layers = new ArrayList<Layer>(foundries.size());
+ FoundryDto dto = null;
+
+ for (AnnotationPair f : foundries) {
+ if (dto == null) {
+ Annotation foundry = f.getAnnotation1();
+ dto = new FoundryDto();
+ if (language.equals("de")){
+ dto.setDescription(foundry.getGermanDescription());
+ }
+ else{
+ dto.setDescription(foundry.getDescription());
+ }
+ dto.setCode(foundry.getCode());
+ }
+
+ Annotation layer = f.getAnnotation2();
+ Map<String, String> tags = new HashMap<>();
+ for (Annotation value : f.getValues()) {
+ if (language.equals("de")){
+ tags.put(value.getCode(), value.getGermanDescription());
+ }
+ else{
+ tags.put(value.getCode(), value.getDescription());
+ }
+ }
+
+ Layer l = dto.new Layer();
+ l.setCode(layer.getCode());
+
+ if (language.equals("de")){
+ l.setDescription(layer.getGermanDescription());
+ }
+ else{
+ l.setDescription(layer.getDescription());
+ }
+
+ l.setTags(tags);
+ layers.add(l);
+ }
+
+ dto.setLayers(layers);
+ foundryDtos.add(dto);
+ }
+
+ return foundryDtos;
+ }
+
+
+ private Map<String, List<AnnotationPair>> createFoundryMap (
+ List<AnnotationPair> pairs) {
+ Map<String, List<AnnotationPair>> foundries =
+ new HashMap<String, List<AnnotationPair>>();
+ for (AnnotationPair p : pairs) {
+ String foundryCode = p.getAnnotation1().getCode();
+ if (foundries.containsKey(foundryCode)) {
+ foundries.get(foundryCode).add(p);
+ }
+ else {
+ List<AnnotationPair> foundryList =
+ new ArrayList<AnnotationPair>();
+ foundryList.add(p);
+ foundries.put(foundryCode, foundryList);
+ }
+ }
+
+ return foundries;
+ }
}
diff --git a/src/main/java/de/ids_mannheim/korap/entity/Annotation.java b/src/main/java/de/ids_mannheim/korap/entity/Annotation.java
index e63eb74..1fbe3fb 100644
--- a/src/main/java/de/ids_mannheim/korap/entity/Annotation.java
+++ b/src/main/java/de/ids_mannheim/korap/entity/Annotation.java
@@ -1,5 +1,6 @@
package de.ids_mannheim.korap.entity;
+import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
@@ -21,10 +22,14 @@
private String code;
private String type;
private String description;
+ @Column(name = "de_description")
+ private String germanDescription;
+
@Override
public String toString () {
return "id=" + id + ", code= " + code + ", type= " + type
- + ", description=" + description;
+ + ", description=" + description + ", germanDescription="
+ + germanDescription;
}
}
diff --git a/src/main/java/de/ids_mannheim/korap/entity/AnnotationPair.java b/src/main/java/de/ids_mannheim/korap/entity/AnnotationPair.java
index ee69da0..ca580c3 100644
--- a/src/main/java/de/ids_mannheim/korap/entity/AnnotationPair.java
+++ b/src/main/java/de/ids_mannheim/korap/entity/AnnotationPair.java
@@ -11,8 +11,9 @@
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
-import javax.persistence.OneToOne;
+import javax.persistence.ManyToOne;
import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
@@ -23,7 +24,8 @@
@Setter
@Getter
@Entity
-@Table(name = "annotation_pair")
+@Table(name = "annotation_pair",
+ uniqueConstraints=@UniqueConstraint(columnNames={"annotation1","annotation2"}))
public class AnnotationPair {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@@ -32,34 +34,33 @@
private int annotationId1;
@Column(name = "annotation2")
private int annotationId2;
- @Column(name = "de_description")
- private String germanDescription;
@Column(name = "description")
- private String englishDescription;
+ private String description;
@Fetch(FetchMode.SELECT)
- @OneToOne
+ @ManyToOne //(fetch=FetchType.LAZY)
@JoinColumn(name = "annotation1", insertable = false, updatable = false)
private Annotation annotation1;
@Fetch(FetchMode.SELECT)
- @OneToOne
+ @ManyToOne //(fetch=FetchType.LAZY)
@JoinColumn(name = "annotation2", insertable = false, updatable = false)
private Annotation annotation2;
- @ManyToMany(fetch=FetchType.LAZY)
+ @ManyToMany(fetch=FetchType.LAZY) //(fetch=FetchType.EAGER)
@JoinTable(
name="annotation_pair_value",
joinColumns=@JoinColumn(name="pair_id", referencedColumnName="id"),
- inverseJoinColumns=@JoinColumn(name="value_id", referencedColumnName="id")
+ inverseJoinColumns=@JoinColumn(name="value_id", referencedColumnName="id"),
+ uniqueConstraints = @UniqueConstraint(columnNames = {
+ "pair_id", "value_id" })
)
private Set<Annotation> values;
@Override
public String toString () {
return "id=" + id + ", annotation1=" + annotationId1 + ", annotation2="
- + annotationId1 + ", description=" + englishDescription
- + ", germanDescription= " + germanDescription
+ + annotationId1 + ", description=" + description
+ ", annotation1= "+ annotation1
+ ", annotation2= "+ annotation2;
diff --git a/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java b/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
index 982026e..889f9f5 100644
--- a/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
+++ b/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
@@ -1,12 +1,9 @@
package de.ids_mannheim.korap.exceptions;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import de.ids_mannheim.korap.config.ConfigLoader;
-
-import java.io.IOException;
import java.util.Properties;
+import de.ids_mannheim.korap.config.ConfigLoader;
+
/**
* @author hanl
* @date 07/09/2014
@@ -23,16 +20,18 @@
public static final int ILLEGAL_ARGUMENT = 104;
public static final int MISSING_ARGUMENT = 105;
public static final int CONNECTION_ERROR = 106;
- public static final int PARAMETER_INVALID = 107;
+ public static final int INVALID_ARGUMENT = 107;
public static final int NOT_SUPPORTED = 108;
-
+
/**
* 300 status codes for query language and serialization
*/
public static final int NO_QUERY = 301;
public static final int SERIALIZATION_FAILED = 302;
-
+ public static final int MISSING_ATTRIBUTE = 303;
+ public static final int INVALID_ATTRIBUTE = 304;
+ public static final int UNSUPPORTED_VALUE = 305;
/**
* 400 status codes for authorization and rewrite functions
diff --git a/src/main/java/de/ids_mannheim/korap/web/service/full/AnnotationService.java b/src/main/java/de/ids_mannheim/korap/web/service/full/AnnotationService.java
index 93afb41..917baa4 100644
--- a/src/main/java/de/ids_mannheim/korap/web/service/full/AnnotationService.java
+++ b/src/main/java/de/ids_mannheim/korap/web/service/full/AnnotationService.java
@@ -21,6 +21,7 @@
import com.sun.jersey.spi.container.ResourceFilters;
import de.ids_mannheim.korap.dao.AnnotationDao;
+import de.ids_mannheim.korap.dto.FoundryDto;
import de.ids_mannheim.korap.dto.LayerDto;
import de.ids_mannheim.korap.dto.converter.AnnotationConverter;
import de.ids_mannheim.korap.entity.AnnotationPair;
@@ -62,16 +63,15 @@
@Path("layers")
public Response getLayers () {
List<AnnotationPair> layers = annotationDao.getAllFoundryLayerPairs();
+ jlog.debug("/layers " + layers.toString());
List<LayerDto> layerDto = annotationConverter.convertToLayerDto(layers);
String result = JsonUtils.toJSON(layerDto);
- jlog.debug("/layers " + layers.toString());
return Response.ok(result).build();
}
/**
- * Returns descriptions and values of the given foundry-layer
- * pairs.
+ * Returns a list of foundry descriptions.
*
* @param codes
* foundry-layer code or a Kleene-star
@@ -82,10 +82,12 @@
@POST
@Path("description")
@Consumes(MediaType.APPLICATION_JSON)
- public Response getAnnotations (String json) {
+ public Response getFoundryDescriptions (String json) {
JsonNode node = JsonUtils.readTree(json);
- if (node == null) { throw KustvaktResponseHandler.throwit(
- StatusCodes.MISSING_ARGUMENT, "Missing a json string."); }
+ if (node == null) {
+ throw KustvaktResponseHandler.throwit(StatusCodes.MISSING_ARGUMENT,
+ "Missing a json string.");
+ }
String language;
if (!node.has("language")) {
@@ -96,6 +98,11 @@
if (language == null || language.isEmpty()) {
language = "en";
}
+ else if (!(language.equals("en") || language.equals("de"))) {
+ throw KustvaktResponseHandler.throwit(
+ StatusCodes.UNSUPPORTED_VALUE, "Unsupported value:",
+ language);
+ }
}
List<String> codes;
@@ -103,11 +110,13 @@
codes = JsonUtils.convert(node.get("codes"), List.class);
}
catch (IOException | NullPointerException e) {
- throw KustvaktResponseHandler.throwit(StatusCodes.PARAMETER_INVALID,
- "Bad parameter:", json);
+ throw KustvaktResponseHandler.throwit(StatusCodes.INVALID_ARGUMENT,
+ "Bad argument:", json);
}
- if (codes == null) { throw KustvaktResponseHandler
- .throwit(StatusCodes.MISSING_ARGUMENT); }
+ if (codes == null) {
+ throw KustvaktResponseHandler.throwit(StatusCodes.MISSING_ATTRIBUTE,
+ "Missing attribute:", "codes");
+ }
List<AnnotationPair> annotationPairs = null;
String foundry = "", layer = "";
@@ -131,7 +140,7 @@
else {
jlog.error("Annotation code is wrong: " + annotationCode);
throw KustvaktResponseHandler.throwit(
- StatusCodes.PARAMETER_INVALID, "Bad parameter:",
+ StatusCodes.INVALID_ATTRIBUTE, "Bad attribute:",
code);
}
@@ -141,8 +150,10 @@
}
if (annotationPairs != null && !annotationPairs.isEmpty()) {
- String result = JsonUtils.toJSON(annotationPairs);
+ List<FoundryDto> dtos = annotationConverter
+ .convertToFoundryDto(annotationPairs, language);
jlog.debug("/layers " + annotationPairs.toString());
+ String result = JsonUtils.toJSON(dtos);
return Response.ok(result).build();
}
else {
diff --git a/src/main/resources/db/new-mysql/V1__create_tables.sql b/src/main/resources/db/new-mysql/V1__create_tables.sql
index e68a45f..8217c27 100644
--- a/src/main/resources/db/new-mysql/V1__create_tables.sql
+++ b/src/main/resources/db/new-mysql/V1__create_tables.sql
@@ -4,6 +4,7 @@
code VARCHAR(20) NOT NULL,
type VARCHAR(20) NOT NULL,
description VARCHAR(100) NOT NULL,
+ de_description VARCHAR(100),
UNIQUE INDEX unique_index (code, type)
);
@@ -12,7 +13,6 @@
annotation1 INTEGER NOT NULL,
annotation2 INTEGER NOT NULL,
description VARCHAR(300) NOT NULL,
- de_description VARCHAR(300),
UNIQUE INDEX unique_index (annotation1, annotation2),
FOREIGN KEY (annotation1)
REFERENCES annotation (id)