package edu.harvard.iq.dataverse; import edu.harvard.iq.dataverse.search.SolrField; import java.util.Collection; import java.io.Serializable; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import javax.faces.model.SelectItem; import javax.persistence.*; /** * Defines the meaning and constraints of a metadata field and its values. * @author Stephen Kraffmiller */ @NamedQueries({ @NamedQuery(name="DatasetFieldType.findByName", query= "SELECT dsfType FROM DatasetFieldType dsfType WHERE dsfType.name=:name"), @NamedQuery(name = "DatasetFieldType.findAllFacetable", query= "select dsfType from DatasetFieldType dsfType WHERE dsfType.facetable = true and dsfType.title != '' order by dsfType.id"), @NamedQuery(name = "DatasetFieldType.findFacetableByMetadaBlock", query= "select dsfType from DatasetFieldType dsfType WHERE dsfType.facetable = true and dsfType.title != '' and dsfType.metadataBlock.id = :metadataBlockId order by dsfType.id") }) @Entity @Table(indexes = {@Index(columnList="metadatablock_id"),@Index(columnList="parentdatasetfieldtype_id")}) public class DatasetFieldType implements Serializable, Comparable<DatasetFieldType> { /** * The set of possible metatypes of the field. Used for validation and layout. */ public enum FieldType { TEXT, TEXTBOX, DATE, EMAIL, URL, FLOAT, INT, NONE }; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; public Long getId() { return this.id; } public void setId(Long id) { this.id = id; } public String getIdString(){ return id.toString(); } /** * The internal, DDI-like name, no spaces, etc. */ @Column(name = "name", columnDefinition = "TEXT", nullable = false) private String name; /** * A longer, human-friendlier name. Punctuation allowed. */ @Column(name = "title", columnDefinition = "TEXT") private String title; /** * A user-friendly Description; will be used for * mouse-overs, etc. */ @Column(name = "description", columnDefinition = "TEXT") private String description; /** * Metatype of the field. */ @Enumerated(EnumType.STRING) @Column( nullable=false ) private FieldType fieldType; /** * Whether the value must be taken from a controlled vocabulary. */ private boolean allowControlledVocabulary; /** * A watermark to be displayed in the UI. */ private String watermark; @OneToMany(mappedBy = "datasetFieldType") private Set<DataverseFacet> dataverseFacets; @OneToMany(mappedBy = "datasetFieldType") private Set<DataverseFieldTypeInputLevel> dataverseFieldTypeInputLevels; @Transient private String searchValue; @Transient private List<String> listValues; @Transient private Map<String, ControlledVocabularyValue> controlledVocabularyValuesByStrValue; @Transient private boolean requiredDV; public void setRequiredDV(boolean requiredDV){ this.requiredDV = requiredDV; } public boolean isRequiredDV(){ return this.requiredDV; } @Transient private boolean include; public void setInclude(boolean include){ this.include = include; } public boolean isInclude(){ return this.include; } @Transient private List<SelectItem> optionSelectItems; public List<SelectItem> getOptionSelectItems() { return optionSelectItems; } public void setOptionSelectItems(List<SelectItem> optionSelectItems) { this.optionSelectItems = optionSelectItems; } public DatasetFieldType() {} public DatasetFieldType(String name, FieldType fieldType, boolean allowMultiples) { this.name = name; this.fieldType = fieldType; this.allowMultiples = allowMultiples; childDatasetFieldTypes = new LinkedList<>(); } private int displayOrder; private String displayFormat; public int getDisplayOrder() { return this.displayOrder; } public void setDisplayOrder(int displayOrder) { this.displayOrder = displayOrder; } public String getDisplayFormat() { return displayFormat; } public void setDisplayFormat(String displayFormat) { this.displayFormat = displayFormat; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public boolean isAllowControlledVocabulary() { return allowControlledVocabulary; } public void setAllowControlledVocabulary(boolean allowControlledVocabulary) { this.allowControlledVocabulary = allowControlledVocabulary; } /** * Determines whether an instance of this field type may have multiple * values. */ private boolean allowMultiples; public boolean isAllowMultiples() { return this.allowMultiples; } public void setAllowMultiples(boolean allowMultiples) { this.allowMultiples = allowMultiples; } public FieldType getFieldType() { return fieldType; } public void setFieldType(FieldType fieldType) { this.fieldType = fieldType; } public String getWatermark() { return watermark; } public void setWatermark(String watermark) { this.watermark = watermark; } /** * Determines whether this field type may be used as a facet. */ private boolean facetable; public boolean isFacetable() { return facetable; } public void setFacetable(boolean facetable) { this.facetable = facetable; } /** * Determines whether this field type is displayed in the form when creating * the Dataset (or only later when editing after the initial creation). */ private boolean displayOnCreate; public boolean isDisplayOnCreate() { return displayOnCreate; } public void setDisplayOnCreate(boolean displayOnCreate) { this.displayOnCreate = displayOnCreate; } public boolean isControlledVocabulary() { return controlledVocabularyValues != null && !controlledVocabularyValues.isEmpty(); } /** * The {@code MetadataBlock} this field type belongs to. */ @ManyToOne(cascade = CascadeType.MERGE) private MetadataBlock metadataBlock; public MetadataBlock getMetadataBlock() { return metadataBlock; } public void setMetadataBlock(MetadataBlock metadataBlock) { this.metadataBlock = metadataBlock; } /** * The list of controlled vocabulary terms that may be used as values for * fields of this field type. */ @OneToMany(mappedBy = "datasetFieldType", cascade = {CascadeType.REMOVE, CascadeType.MERGE, CascadeType.PERSIST}) @OrderBy("displayOrder ASC") private Collection<ControlledVocabularyValue> controlledVocabularyValues; public Collection<ControlledVocabularyValue> getControlledVocabularyValues() { return this.controlledVocabularyValues; } public void setControlledVocabularyValues(Collection<ControlledVocabularyValue> controlledVocabularyValues) { this.controlledVocabularyValues = controlledVocabularyValues; } public ControlledVocabularyValue getControlledVocabularyValue( String strValue ) { if ( ! isControlledVocabulary() ) { throw new IllegalStateException("getControlledVocabularyValue() called on a non-controlled vocabulary type."); } if ( controlledVocabularyValuesByStrValue == null ) { controlledVocabularyValuesByStrValue = new TreeMap<>(); for ( ControlledVocabularyValue cvv : getControlledVocabularyValues() ) { controlledVocabularyValuesByStrValue.put( cvv.getStrValue(), cvv); } } return controlledVocabularyValuesByStrValue.get(strValue); } /** * Collection of field types that are children of this field type. * A field type may consist of one or more child field types, but only one * parent. */ @OneToMany(mappedBy = "parentDatasetFieldType", cascade = {CascadeType.REMOVE, CascadeType.MERGE, CascadeType.PERSIST}) @OrderBy("displayOrder ASC") private Collection<DatasetFieldType> childDatasetFieldTypes; public Collection<DatasetFieldType> getChildDatasetFieldTypes() { return this.childDatasetFieldTypes; } public void setChildDatasetFieldTypes(Collection<DatasetFieldType> childDatasetFieldTypes) { this.childDatasetFieldTypes = childDatasetFieldTypes; } @ManyToOne(cascade = CascadeType.MERGE) private DatasetFieldType parentDatasetFieldType; public DatasetFieldType getParentDatasetFieldType() { return parentDatasetFieldType; } public void setParentDatasetFieldType(DatasetFieldType parentDatasetFieldType) { this.parentDatasetFieldType = parentDatasetFieldType; } public Set<DataverseFacet> getDataverseFacets() { return dataverseFacets; } public void setDataverseFacets(Set<DataverseFacet> dataverseFacets) { this.dataverseFacets = dataverseFacets; } public Set<DataverseFieldTypeInputLevel> getDataverseFieldTypeInputLevels() { return dataverseFieldTypeInputLevels; } public void setDataverseFieldTypeInputLevels(Set<DataverseFieldTypeInputLevel> dataverseFieldTypeInputLevels) { this.dataverseFieldTypeInputLevels = dataverseFieldTypeInputLevels; } public String getSearchValue() { return searchValue; } public void setSearchValue(String searchValue) { this.searchValue = searchValue; } public List<String> getListValues() { return listValues; } public void setListValues(List<String> listValues) { this.listValues = listValues; } /** * Determines whether fields of this field type are always required. A * dataverse may set some fields required, but only if this is false. */ private boolean required; public boolean isRequired() { return this.required; } public void setRequired(boolean required) { this.required = required; } private boolean advancedSearchFieldType; public boolean isAdvancedSearchFieldType() { return this.advancedSearchFieldType; } public void setAdvancedSearchFieldType(boolean advancedSearchFieldType) { this.advancedSearchFieldType = advancedSearchFieldType; } public boolean isPrimitive() { return this.childDatasetFieldTypes.isEmpty(); } public boolean isCompound() { return !this.childDatasetFieldTypes.isEmpty(); } public boolean isChild() { return this.parentDatasetFieldType != null; } public boolean isSubField() { return this.parentDatasetFieldType != null; } public boolean isHasChildren() { return !this.childDatasetFieldTypes.isEmpty(); } public boolean isHasRequiredChildren() { if (this.childDatasetFieldTypes.isEmpty()){ return false; } else { for (DatasetFieldType dsftC : this.childDatasetFieldTypes){ if (dsftC.isRequired()) return true; } } return false; } public boolean isHasParent() { return this.parentDatasetFieldType != null; } public int hashCode() { int hash = 0; hash += (this.id != null ? this.id.hashCode() : 0); return hash; } public boolean equals(Object object) { // TODO: Warning - this method won't work in the case the id fields are not set if (!(object instanceof DatasetFieldType)) { return false; } DatasetFieldType other = (DatasetFieldType) object; if (this.id != other.id && (this.id == null || !this.id.equals(other.id))) { return false; } return true; } /** * List of fields that use this field type. If this field type is removed, * these fields will be removed too. */ @OneToMany(mappedBy = "datasetFieldType", cascade = {CascadeType.REMOVE, CascadeType.MERGE, CascadeType.PERSIST}) private List<DatasetField> datasetFields; public List<DatasetField> getDatasetFields() { return datasetFields; } public void setDatasetFields(List<DatasetField> datasetFieldValues) { this.datasetFields = datasetFieldValues; } @OneToMany(mappedBy = "datasetField", cascade = {CascadeType.REMOVE, CascadeType.MERGE, CascadeType.PERSIST}) private List<DatasetFieldDefaultValue> datasetFieldDefaultValues; public List<DatasetFieldDefaultValue> getDatasetFieldDefaultValues() { return datasetFieldDefaultValues; } public void setDatasetFieldDefaultValues(List<DatasetFieldDefaultValue> datasetFieldDefaultValues) { this.datasetFieldDefaultValues = datasetFieldDefaultValues; } @Override public int compareTo(DatasetFieldType o) { return Integer.compare(this.getDisplayOrder(), (o.getDisplayOrder())); } public String getDisplayName() { if (isHasParent() && !parentDatasetFieldType.getTitle().equals(title)) { return parentDatasetFieldType.getTitle() + " " + title; } else { return title; } } public SolrField getSolrField() { SolrField.SolrType solrType = SolrField.SolrType.TEXT_EN; if (fieldType != null) { /** * @todo made more decisions based on fieldType: index as dates, * integers, and floats so we can do range queries etc. */ if (fieldType.equals(FieldType.DATE)) { solrType = SolrField.SolrType.DATE; } else if (fieldType.equals(FieldType.EMAIL)) { solrType = SolrField.SolrType.EMAIL; } Boolean parentAllowsMultiplesBoolean = false; if (isHasParent()) { if (getParentDatasetFieldType() != null) { DatasetFieldType parent = getParentDatasetFieldType(); parentAllowsMultiplesBoolean = parent.isAllowMultiples(); } } boolean makeSolrFieldMultivalued; // http://stackoverflow.com/questions/5800762/what-is-the-use-of-multivalued-field-type-in-solr if (allowMultiples || parentAllowsMultiplesBoolean) { makeSolrFieldMultivalued = true; } else { makeSolrFieldMultivalued = false; } return new SolrField(name, solrType, makeSolrFieldMultivalued, facetable); } else { /** * @todo: clean this up */ String oddValue = name + getTmpNullFieldTypeIdentifier(); boolean makeSolrFieldMultivalued = false; SolrField solrField = new SolrField(oddValue, solrType, makeSolrFieldMultivalued, facetable); return solrField; } } // help us identify fields that have null fieldType values public String getTmpNullFieldTypeIdentifier() { return "NullFieldType_s"; } @Override public String toString() { return "[DatasetFieldType name:" + getName() + " id:" + getId() + "]"; } }