/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package edu.harvard.iq.dataverse;
/**
*
* @author skraffmiller
*/
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.persistence.Transient;
import org.apache.commons.lang.StringUtils;
@Entity
@ValidateDatasetFieldType
@Table(indexes = {@Index(columnList="datasetfieldtype_id"),@Index(columnList="datasetversion_id"),
@Index(columnList="parentdatasetfieldcompoundvalue_id"),@Index(columnList="template_id")})
public class DatasetField implements Serializable {
private static final long serialVersionUID = 1L;
public static final String NA_VALUE = "N/A";
/**
* Orders dataset fields by their display order.
*/
public static final Comparator<DatasetField> DisplayOrder = new Comparator<DatasetField>() {
@Override
public int compare(DatasetField o1, DatasetField o2) {
return Integer.compare( o1.getDatasetFieldType().getDisplayOrder(),
o2.getDatasetFieldType().getDisplayOrder() );
}};
public static DatasetField createNewEmptyDatasetField(DatasetFieldType dsfType, Object dsv) {
DatasetField dsfv = createNewEmptyDatasetField(dsfType);
//TODO - a better way to handle this?
if (dsv.getClass().getName().equals("edu.harvard.iq.dataverse.DatasetVersion")){
dsfv.setDatasetVersion((DatasetVersion)dsv);
} else {
dsfv.setTemplate((Template)dsv);
}
return dsfv;
}
// originally this was an overloaded method, but we renamed it to get around an issue with Bean Validation
// (that looked t overloaded methods, when it meant to look at overriden methods
public static DatasetField createNewEmptyChildDatasetField(DatasetFieldType dsfType, DatasetFieldCompoundValue compoundValue) {
DatasetField dsfv = createNewEmptyDatasetField(dsfType);
dsfv.setParentDatasetFieldCompoundValue(compoundValue);
return dsfv;
}
private static DatasetField createNewEmptyDatasetField(DatasetFieldType dsfType) {
DatasetField dsfv = new DatasetField();
dsfv.setDatasetFieldType(dsfType);
if (dsfType.isPrimitive()) {
if (!dsfType.isControlledVocabulary()) {
dsfv.getDatasetFieldValues().add(new DatasetFieldValue(dsfv));
}
} else { // compound field
dsfv.getDatasetFieldCompoundValues().add(DatasetFieldCompoundValue.createNewEmptyDatasetFieldCompoundValue(dsfv));
}
return dsfv;
}
/**
* Groups a list of fields by the block they belong to.
*
* @param fields well, duh.
* @return a map, mapping each block to the fields that belong to it.
*/
public static Map<MetadataBlock, List<DatasetField>> groupByBlock(List<DatasetField> fields) {
Map<MetadataBlock, List<DatasetField>> retVal = new HashMap<>();
for (DatasetField f : fields) {
MetadataBlock metadataBlock = f.getDatasetFieldType().getMetadataBlock();
List<DatasetField> lst = retVal.get(metadataBlock);
if (lst == null) {
retVal.put(metadataBlock, new LinkedList<>(Collections.singleton(f)));
} else {
lst.add(f);
}
}
return retVal;
}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@ManyToOne
@JoinColumn(nullable = false)
private DatasetFieldType datasetFieldType;
public DatasetFieldType getDatasetFieldType() {
return datasetFieldType;
}
public void setDatasetFieldType(DatasetFieldType datasetField) {
this.datasetFieldType = datasetField;
}
@ManyToOne
private DatasetVersion datasetVersion;
public DatasetVersion getDatasetVersion() {
return datasetVersion;
}
public void setDatasetVersion(DatasetVersion datasetVersion) {
this.datasetVersion = datasetVersion;
}
@ManyToOne
private Template template;
public Template getTemplate() {
return template;
}
public void setTemplate(Template template) {
this.template = template;
}
@ManyToOne(cascade = CascadeType.MERGE)
private DatasetFieldCompoundValue parentDatasetFieldCompoundValue;
public DatasetFieldCompoundValue getParentDatasetFieldCompoundValue() {
return parentDatasetFieldCompoundValue;
}
public void setParentDatasetFieldCompoundValue(DatasetFieldCompoundValue parentDatasetFieldCompoundValue) {
this.parentDatasetFieldCompoundValue = parentDatasetFieldCompoundValue;
}
@OneToMany(mappedBy = "parentDatasetField", orphanRemoval = true, cascade = {CascadeType.REMOVE, CascadeType.MERGE, CascadeType.PERSIST})
@OrderBy("displayOrder ASC")
private List<DatasetFieldCompoundValue> datasetFieldCompoundValues = new ArrayList();
public List<DatasetFieldCompoundValue> getDatasetFieldCompoundValues() {
return datasetFieldCompoundValues;
}
public void setDatasetFieldCompoundValues(List<DatasetFieldCompoundValue> datasetFieldCompoundValues) {
this.datasetFieldCompoundValues = datasetFieldCompoundValues;
}
@OneToMany(mappedBy = "datasetField", orphanRemoval = true, cascade = {CascadeType.REMOVE, CascadeType.MERGE, CascadeType.PERSIST})
@OrderBy("displayOrder ASC")
private List<DatasetFieldValue> datasetFieldValues = new ArrayList();
public List<DatasetFieldValue> getDatasetFieldValues() {
return this.datasetFieldValues;
}
public void setDatasetFieldValues(List<DatasetFieldValue> datasetFieldValues) {
this.datasetFieldValues = datasetFieldValues;
}
@ManyToMany(cascade = {CascadeType.MERGE})
@JoinTable(indexes = {@Index(columnList="datasetfield_id"),@Index(columnList="controlledvocabularyvalues_id")})
private List<ControlledVocabularyValue> controlledVocabularyValues = new ArrayList();
public List<ControlledVocabularyValue> getControlledVocabularyValues() {
return controlledVocabularyValues;
}
public void setControlledVocabularyValues(List<ControlledVocabularyValue> controlledVocabularyValues) {
this.controlledVocabularyValues = controlledVocabularyValues;
}
// HELPER METHODS
public DatasetFieldValue getSingleValue() {
if (!datasetFieldValues.isEmpty()) {
return datasetFieldValues.get(0);
} else {
return new DatasetFieldValue(this);
}
}
public void setSingleValue(String value) {
if (datasetFieldValues.isEmpty()) {
datasetFieldValues.add(new DatasetFieldValue(this));
}
datasetFieldValues.get(0).setValue(value);
}
public ControlledVocabularyValue getSingleControlledVocabularyValue() {
if (!controlledVocabularyValues.isEmpty()) {
return controlledVocabularyValues.get(0);
} else {
return null;
}
}
public void setSingleControlledVocabularyValue(ControlledVocabularyValue cvv) {
if (!controlledVocabularyValues.isEmpty()) {
controlledVocabularyValues.set(0, cvv);
} else {
controlledVocabularyValues.add(cvv);
}
}
public String getValue() {
if (!datasetFieldValues.isEmpty()) {
return datasetFieldValues.get(0).getValue();
} else if (controlledVocabularyValues != null && !controlledVocabularyValues.isEmpty()) {
if (controlledVocabularyValues.get(0) != null){
return controlledVocabularyValues.get(0).getStrValue();
}
}
return null;
}
public String getDisplayValue() {
String returnString = "";
for (String value : getValues()) {
if(value == null) value="";
returnString += (returnString.equals("") ? "" : "; ") + value.trim();
}
return returnString;
}
public String getCompoundDisplayValue() {
String returnString = "";
for (DatasetFieldCompoundValue dscv : datasetFieldCompoundValues) {
for (DatasetField dsf : dscv.getChildDatasetFields()) {
for (String value : dsf.getValues()) {
if (!(value == null)) {
returnString += (returnString.equals("") ? "" : "; ") + value.trim();
}
}
}
}
return returnString;
}
public List<String> getValues() {
List returnList = new ArrayList();
if (!datasetFieldValues.isEmpty()) {
for (DatasetFieldValue dsfv : datasetFieldValues) {
returnList.add(dsfv.getValue());
}
} else {
for (ControlledVocabularyValue cvv : controlledVocabularyValues) {
if (cvv != null && cvv.getStrValue() != null) {
returnList.add(cvv.getStrValue());
}
}
}
return returnList;
}
public List<String> getValuesWithoutNaValues() {
List<String> returnList = getValues();
returnList.removeAll(Arrays.asList(NA_VALUE));
return returnList;
}
public boolean isEmpty() {
return isEmpty(false);
}
public boolean isEmptyForDisplay() {
return isEmpty(true);
}
private boolean isEmpty(boolean forDisplay) {
if (datasetFieldType.isPrimitive()) { // primitive
for (String value : getValues()) {
if (!StringUtils.isBlank(value) && !(forDisplay && DatasetField.NA_VALUE.equals(value))) {
return false;
}
}
} else { // compound
for (DatasetFieldCompoundValue cv : datasetFieldCompoundValues) {
for (DatasetField subField : cv.getChildDatasetFields()) {
if (!subField.isEmpty(forDisplay)) {
return false;
}
}
}
}
return true;
}
@Transient
private String validationMessage;
public String getValidationMessage() {
return validationMessage;
}
public void setValidationMessage(String validationMessage) {
this.validationMessage = validationMessage;
}
@Transient
private Boolean required;
public boolean isRequired() {
if (required == null) {
required = false;
if (this.datasetFieldType.isPrimitive() && this.datasetFieldType.isRequired()) {
required = true;
}
if (this.datasetFieldType.isHasRequiredChildren()) {
required = true;
}
Dataverse dv = getDataverse();
while (!dv.isMetadataBlockRoot()) {
if (dv.getOwner() == null) {
break; // we are at the root; which by defintion is metadata blcok root, regarldess of the value
}
dv = dv.getOwner();
}
List<DataverseFieldTypeInputLevel> dftilListFirst = dv.getDataverseFieldTypeInputLevels();
if (!getDatasetFieldType().isHasChildren()) {
for (DataverseFieldTypeInputLevel dsftil : dftilListFirst) {
if (dsftil.getDatasetFieldType().equals(this.datasetFieldType)) {
required = dsftil.isRequired();
}
}
}
if (getDatasetFieldType().isHasChildren() && (!dftilListFirst.isEmpty())) {
for (DatasetFieldType child : getDatasetFieldType().getChildDatasetFieldTypes()) {
for (DataverseFieldTypeInputLevel dftilTest : dftilListFirst) {
if (child.equals(dftilTest.getDatasetFieldType())) {
if (dftilTest.isRequired()) {
required = true;
}
}
}
}
}
}
// logger.fine("at return " + this.datasetFieldType.getDisplayName() + " " + required);
return required;
}
public Dataverse getDataverse() {
if (datasetVersion != null) {
return datasetVersion.getDataset().getOwner();
} else if (parentDatasetFieldCompoundValue != null) {
return parentDatasetFieldCompoundValue.getParentDatasetField().getDataverse();
} else if (template != null) {
return template.getDataverse();
} else {
throw new IllegalStateException("DatasetField is in an illegal state: no dataset version, compound value, or template is set as its parent.");
}
}
@Transient
private boolean include;
public void setInclude(boolean include){
this.include = include;
}
public boolean isInclude(){
return this.include;
}
@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof DatasetField)) {
return false;
}
DatasetField other = (DatasetField) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
public String toString() {
return "edu.harvard.iq.dataverse.DatasetField[ id=" + id + " ]";
}
public DatasetField copy(Object version) {
return copy(version, null);
}
// originally this was an overloaded method, but we renamed it to get around an issue with Bean Validation
// (that looked t overloaded methods, when it meant to look at overriden methods
public DatasetField copyChild(DatasetFieldCompoundValue parent) {
return copy(null, parent);
}
private DatasetField copy(Object version, DatasetFieldCompoundValue parent) {
DatasetField dsf = new DatasetField();
dsf.setDatasetFieldType(datasetFieldType);
if (version != null) {
if (version.getClass().getName().equals("edu.harvard.iq.dataverse.DatasetVersion")) {
dsf.setDatasetVersion((DatasetVersion) version);
} else {
dsf.setTemplate((Template) version);
}
}
dsf.setParentDatasetFieldCompoundValue(parent);
dsf.setControlledVocabularyValues(controlledVocabularyValues);
for (DatasetFieldValue dsfv : datasetFieldValues) {
dsf.getDatasetFieldValues().add(dsfv.copy(dsf));
}
for (DatasetFieldCompoundValue compoundValue : datasetFieldCompoundValues) {
dsf.getDatasetFieldCompoundValues().add(compoundValue.copy(dsf));
}
return dsf;
}
public boolean removeBlankDatasetFieldValues() {
if (this.getDatasetFieldType().isPrimitive()) {
if (!this.getDatasetFieldType().isControlledVocabulary()) {
Iterator<DatasetFieldValue> dsfvIt = this.getDatasetFieldValues().iterator();
while (dsfvIt.hasNext()) {
DatasetFieldValue dsfv = dsfvIt.next();
if (StringUtils.isBlank(dsfv.getValue())) {
dsfvIt.remove();
}
}
if (this.getDatasetFieldValues().isEmpty()) {
return true;
}
} else { // controlled vocab
if (this.getControlledVocabularyValues().isEmpty()) {
return true;
}
}
} else if (this.getDatasetFieldType().isCompound()) {
Iterator<DatasetFieldCompoundValue> cvIt = this.getDatasetFieldCompoundValues().iterator();
while (cvIt.hasNext()) {
DatasetFieldCompoundValue cv = cvIt.next();
Iterator<DatasetField> dsfIt = cv.getChildDatasetFields().iterator();
while (dsfIt.hasNext()) {
if (dsfIt.next().removeBlankDatasetFieldValues()) {
dsfIt.remove();
}
}
if (cv.getChildDatasetFields().isEmpty()) {
cvIt.remove();
}
}
if (this.getDatasetFieldCompoundValues().isEmpty()) {
return true;
}
}
return false;
}
public void setValueDisplayOrder() {
if (this.getDatasetFieldType().isPrimitive() && !this.getDatasetFieldType().isControlledVocabulary()) {
for (int i = 0; i < datasetFieldValues.size(); i++) {
datasetFieldValues.get(i).setDisplayOrder(i);
}
} else if (this.getDatasetFieldType().isCompound()) {
for (int i = 0; i < datasetFieldCompoundValues.size(); i++) {
DatasetFieldCompoundValue compoundValue = datasetFieldCompoundValues.get(i);
compoundValue.setDisplayOrder(i);
for (DatasetField dsf : compoundValue.getChildDatasetFields()) {
dsf.setValueDisplayOrder();
}
}
}
}
public void addDatasetFieldValue(int index) {
datasetFieldValues.add(index, new DatasetFieldValue(this));
}
public void removeDatasetFieldValue(int index) {
datasetFieldValues.remove(index);
}
public void addDatasetFieldCompoundValue(int index) {
datasetFieldCompoundValues.add(index, DatasetFieldCompoundValue.createNewEmptyDatasetFieldCompoundValue(this));
}
public void removeDatasetFieldCompoundValue(int index) {
datasetFieldCompoundValues.remove(index);
}
/**
* If this is a FieldType.TEXT or FieldType.TEXTBOX, then run it through the markup checker
*
* @return
*/
public boolean needsTextCleaning(){
if (this.getDatasetFieldType() == null || this.getDatasetFieldType().getFieldType() == null){
return false;
}
if (this.datasetFieldType.getFieldType().equals(DatasetFieldType.FieldType.TEXT)){
return true;
} else if (this.datasetFieldType.getFieldType().equals(DatasetFieldType.FieldType.TEXTBOX)){
return true;
}
return false;
} // end: needsTextCleaning
}