org.tdar.core.bean.resource.InformationResource.java Source code

Java tutorial

Introduction

Here is the source code for org.tdar.core.bean.resource.InformationResource.java

Source

package org.tdar.core.bean.resource;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Index;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.lucene.analysis.KeywordAnalyzer;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Type;
import org.hibernate.search.annotations.Analyze;
import org.hibernate.search.annotations.Analyzer;
import org.hibernate.search.annotations.DynamicBoost;
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.FieldBridge;
import org.hibernate.search.annotations.Fields;
import org.hibernate.search.annotations.IndexedEmbedded;
import org.hibernate.search.annotations.Norms;
import org.hibernate.search.annotations.Store;
import org.hibernate.validator.constraints.Length;
import org.tdar.core.bean.BulkImportField;
import org.tdar.core.bean.FieldLength;
import org.tdar.core.bean.FileProxy;
import org.tdar.core.bean.Obfuscatable;
import org.tdar.core.bean.citation.RelatedComparativeCollection;
import org.tdar.core.bean.citation.SourceCollection;
import org.tdar.core.bean.coverage.CoverageDate;
import org.tdar.core.bean.coverage.LatitudeLongitudeBox;
import org.tdar.core.bean.entity.Creator;
import org.tdar.core.bean.entity.Institution;
import org.tdar.core.bean.entity.Person;
import org.tdar.core.bean.entity.ResourceCreator;
import org.tdar.core.bean.entity.ResourceCreatorRole;
import org.tdar.core.bean.keyword.CultureKeyword;
import org.tdar.core.bean.keyword.GeographicKeyword;
import org.tdar.core.bean.keyword.InvestigationType;
import org.tdar.core.bean.keyword.MaterialKeyword;
import org.tdar.core.bean.keyword.OtherKeyword;
import org.tdar.core.bean.keyword.SiteNameKeyword;
import org.tdar.core.bean.keyword.SiteTypeKeyword;
import org.tdar.core.bean.keyword.TemporalKeyword;
import org.tdar.core.bean.resource.datatable.DataTableColumn;
import org.tdar.core.exception.TdarValidationException;
import org.tdar.search.index.analyzer.AutocompleteAnalyzer;
import org.tdar.search.index.analyzer.LowercaseWhiteSpaceStandardAnalyzer;
import org.tdar.search.index.analyzer.NonTokenizingLowercaseKeywordAnalyzer;
import org.tdar.search.index.analyzer.TdarCaseSensitiveStandardAnalyzer;
import org.tdar.search.index.boost.InformationResourceBoostStrategy;
import org.tdar.search.index.bridge.PersistentReaderBridge;
import org.tdar.search.index.bridge.StringMapBridge;
import org.tdar.search.index.bridge.TdarPaddedNumberBridge;
import org.tdar.search.query.QueryFieldNames;
import org.tdar.utils.jaxb.converters.JaxbPersistableConverter;
import org.tdar.utils.json.JsonLookupFilter;

import com.fasterxml.jackson.annotation.JsonView;

/**
 * $Id$
 * <p>
 * Represents a Resource with a file payload and additional metadata that can be one of the following:
 * </p>
 * <ol>
 * <li>Image
 * <li>Dataset file (Access, Excel)
 * <li>Document (PDF)
 * </ol>
 * 
 * @author <a href='Allen.Lee@asu.edu'>Allen Lee</a>
 * @version $Revision$
 */
@Entity
@Table(name = "information_resource", indexes = { @Index(name = "infores_projid", columnList = "project_id, id"),
        @Index(name = "infores_provid", columnList = "provider_institution_id"),
        @Index(name = "ires_copyright", columnList = "copyright_holder_id"),
        // FIXME: prod database has two indexes on these fields with different names. Remove if they are redundant(as well as the @Index annotation)
        // @Index(name = "ires_provicer", columnList={"provider_institution_id"}),
        // @Index(name = "infores_provid", columnList={"provider_institution_id"}),
        @Index(name = "ires_publisher", columnList = "publisher_id") })
@DynamicBoost(impl = InformationResourceBoostStrategy.class)
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class InformationResource extends Resource {

    public static final String LICENSE_TEXT = "LICENSE_TEXT";
    public static final String LICENSE_TYPE = "LICENSE_TYPE";
    public static final String COPYRIGHT_HOLDER = "COPYRIGHT_HOLDER";

    private static final long serialVersionUID = -1534799746444826257L;

    public InformationResource() {

    }

    @Deprecated
    public InformationResource(Long id, String title) {
        setId(id);
        setTitle(title);
    }

    @Deprecated
    public InformationResource(Long id, String title, ResourceType type) {
        setId(id);
        setTitle(title);
        setResourceType(type);
    }

    @ManyToOne(optional = true, cascade = { CascadeType.MERGE, CascadeType.DETACH })
    // @ContainedIn /* DISABLED TO MANAGE PERFORMANCE ISSUES*/
    private Project project;

    @Transient
    private Long projectId;

    // @ManyToMany
    // @JoinTable(name = "information_resource_related_citation", joinColumns = @JoinColumn(name = "information_resource_id"), inverseJoinColumns = @JoinColumn(
    // name = "document_id"))
    // private Set<Document> relatedCitations = new HashSet<Document>();
    //
    // @ManyToMany(cascade = CascadeType.ALL)
    // @XStreamOmitField
    // @JoinTable(name = "information_resource_source_citation", joinColumns = @JoinColumn(name = "information_resource_id"), inverseJoinColumns = @JoinColumn(
    // name = "document_id"))
    // private Set<Document> sourceCitations = new HashSet<Document>();

    // FIXME: cascade "delete" ?
    @OneToMany(mappedBy = "informationResource", cascade = { CascadeType.PERSIST, CascadeType.MERGE,
            CascadeType.REFRESH })
    @OrderBy("sequenceNumber asc")
    @IndexedEmbedded
    @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL, region = "org.tdar.core.bean.resource.InformationResource.informationResourceFiles")
    private Set<InformationResourceFile> informationResourceFiles = new LinkedHashSet<>();

    @BulkImportField(key = "METADATA_LANGUAGE")
    @Enumerated(EnumType.STRING)
    @Field(norms = Norms.NO, store = Store.YES, analyzer = @Analyzer(impl = TdarCaseSensitiveStandardAnalyzer.class))
    @Column(name = "metadata_language", length = FieldLength.FIELD_LENGTH_100)
    private Language metadataLanguage;

    @BulkImportField(key = "RESOURCE_LANGUAGE")
    @Enumerated(EnumType.STRING)
    @Field(norms = Norms.NO, store = Store.YES, analyzer = @Analyzer(impl = TdarCaseSensitiveStandardAnalyzer.class))
    @Column(name = "resource_language", length = FieldLength.FIELD_LENGTH_100)
    private Language resourceLanguage;

    @Enumerated(EnumType.STRING)
    @Field(norms = Norms.NO, store = Store.YES, analyzer = @Analyzer(impl = TdarCaseSensitiveStandardAnalyzer.class))
    @Column(name = "license_type", length = FieldLength.FIELD_LENGTH_128)
    @BulkImportField(key = LICENSE_TYPE, required = true)
    private LicenseType licenseType;

    @Column(name = "license_text")
    @BulkImportField(key = LICENSE_TEXT)
    @Type(type = "org.hibernate.type.StringClobType")
    @Lob
    private String licenseText;

    @BulkImportField(key = "DOI")
    @Field
    @Analyzer(impl = NonTokenizingLowercaseKeywordAnalyzer.class)
    @Length(max = FieldLength.FIELD_LENGTH_255)
    @Column(name = "external_doi")
    private String doi;

    @Column(name = "external_reference", nullable = true)
    @XmlTransient
    private boolean externalReference;

    @BulkImportField(key = "COPY_LOCATION")
    @Column(name = "copy_location")
    @Length(max = FieldLength.FIELD_LENGTH_255)
    private String copyLocation;

    @Column(name = "last_uploaded")
    @Temporal(TemporalType.TIMESTAMP)
    private Date lastUploaded;

    // currently just a 4 digit year.
    @Column(name = "date_created")
    @BulkImportField(key = "YEAR", required = true, order = -10)
    @FieldBridge(impl = TdarPaddedNumberBridge.class)
    @Field(norms = Norms.NO, store = Store.YES, analyze = Analyze.NO)
    @JsonView(JsonLookupFilter.class)
    private Integer date = -1;

    @Column(name = "date_created_normalized")
    @FieldBridge(impl = TdarPaddedNumberBridge.class)
    @Field(norms = Norms.NO, store = Store.YES, name = QueryFieldNames.DATE_CREATED_DECADE, analyze = Analyze.NO)
    @XmlTransient
    private Integer dateNormalized = -1;

    // The institution providing this InformationResource
    @ManyToOne(optional = true, cascade = { CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.MERGE,
            CascadeType.DETACH })
    @JoinColumn(name = "provider_institution_id")
    @IndexedEmbedded
    @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
    private Institution resourceProviderInstitution;

    @ManyToOne(optional = true, cascade = { CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.MERGE,
            CascadeType.DETACH })
    @BulkImportField(key = "PUBLISHER")
    @JoinColumn(name = "publisher_id")
    @IndexedEmbedded
    @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
    private Institution publisher;

    @BulkImportField(key = "PUBLISHER_LOCATION")
    @Column(name = "publisher_location")
    @Length(max = FieldLength.FIELD_LENGTH_255)
    private String publisherLocation;

    @JoinColumn(name = "copyright_holder_id")
    @ManyToOne(optional = true, cascade = { CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.MERGE,
            CascadeType.DETACH })
    @BulkImportField(key = COPYRIGHT_HOLDER, required = true, implementedSubclasses = { Person.class,
            Institution.class }, order = 1)
    @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
    private Creator copyrightHolder;

    public static final String INVESTIGATION_TYPE_INHERITANCE_TOGGLE = "inheriting_investigation_information";
    public static final String SITE_NAME_INHERITANCE_TOGGLE = "inheriting_site_information";
    public static final String MATERIAL_TYPE_INHERITANCE_TOGGLE = "inheriting_material_information";
    public static final String OTHER_INHERITANCE_TOGGLE = "inheriting_other_information";
    public static final String GEOGRAPHIC_INHERITANCE_TOGGLE = "inheriting_spatial_information";
    public static final String CULTURE_INHERITANCE_TOGGLE = "inheriting_cultural_information";
    public static final String TEMPORAL_INHERITANCE_TOGGLE = "inheriting_temporal_information";

    // downward inheritance sections
    @Column(name = INVESTIGATION_TYPE_INHERITANCE_TOGGLE, nullable = false, columnDefinition = "boolean default FALSE")
    private boolean inheritingInvestigationInformation = false;
    @Column(name = SITE_NAME_INHERITANCE_TOGGLE, nullable = false, columnDefinition = "boolean default FALSE")
    private boolean inheritingSiteInformation = false;
    @Column(name = MATERIAL_TYPE_INHERITANCE_TOGGLE, nullable = false, columnDefinition = "boolean default FALSE")
    private boolean inheritingMaterialInformation = false;
    @Column(name = OTHER_INHERITANCE_TOGGLE, nullable = false, columnDefinition = "boolean default FALSE")
    private boolean inheritingOtherInformation = false;
    @Column(name = CULTURE_INHERITANCE_TOGGLE, nullable = false, columnDefinition = "boolean default FALSE")
    private boolean inheritingCulturalInformation = false;

    @Column(name = GEOGRAPHIC_INHERITANCE_TOGGLE, nullable = false, columnDefinition = "boolean default FALSE")
    private boolean inheritingSpatialInformation = false;

    @Column(name = TEMPORAL_INHERITANCE_TOGGLE, nullable = false, columnDefinition = "boolean default FALSE")
    private boolean inheritingTemporalInformation = false;

    @Column(name = "inheriting_note_information", nullable = false, columnDefinition = "boolean default FALSE")
    private boolean inheritingNoteInformation = false;

    @Column(name = "inheriting_identifier_information", nullable = false, columnDefinition = "boolean default FALSE")
    private boolean inheritingIdentifierInformation = false;

    @Column(name = "inheriting_collection_information", nullable = false, columnDefinition = "boolean default FALSE")
    private boolean inheritingCollectionInformation = false;

    @Column(name = "inheriting_individual_institutional_credit", nullable = false, columnDefinition = "boolean default FALSE")
    private boolean inheritingIndividualAndInstitutionalCredit = false;

    @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
    @ManyToOne(optional = true)
    private DataTableColumn mappedDataKeyColumn;

    @Column
    @Length(max = FieldLength.FIELD_LENGTH_255)
    private String mappedDataKeyValue;

    @Transient
    @XmlTransient
    private Map<DataTableColumn, String> relatedDatasetData = new HashMap<>();

    public Language getMetadataLanguage() {
        return metadataLanguage;
    }

    public void setMetadataLanguage(Language metadataLanguage) {
        this.metadataLanguage = metadataLanguage;
    }

    public Language getResourceLanguage() {
        return resourceLanguage;
    }

    public void setResourceLanguage(Language resourceLanguage) {
        this.resourceLanguage = resourceLanguage;
    }

    @XmlElement(name = "copyrightHolderRef")
    @XmlJavaTypeAdapter(JaxbPersistableConverter.class)
    public Creator getCopyrightHolder() {
        return copyrightHolder;
    }

    public void setCopyrightHolder(Creator copyrightHolder) {
        this.copyrightHolder = copyrightHolder;
    }

    @XmlElement(name = "licenseType")
    public LicenseType getLicenseType() {
        return licenseType;
    }

    public void setLicenseType(LicenseType licenseType) {
        this.licenseType = licenseType;
        if (licenseType != LicenseType.OTHER) {
            setLicenseText(null);
        }
    }

    @XmlElement(name = "licenseText")
    public String getLicenseText() {
        if (licenseType == LicenseType.OTHER) {
            return licenseText;
        } else {
            return null;
        }
    }

    public void setLicenseText(String licenseText) {
        this.licenseText = licenseText;
    }

    public Integer getDate() {
        return date;
    }

    public void setDate(Integer dateCreated) {
        this.date = dateCreated;
        if (dateCreated != null) {
            this.dateNormalized = Math.round(dateCreated.floatValue() / 10f) * 10;
        } else {
            this.dateNormalized = null;
        }
    }

    public Integer getDateNormalized() {
        return dateNormalized;
    }

    @Deprecated
    public void setDateNormalized(Integer dateCreatedNormalized) {
        this.dateNormalized = dateCreatedNormalized;
    }

    public Institution getResourceProviderInstitution() {
        return resourceProviderInstitution;
    }

    public void setResourceProviderInstitution(Institution resourceProviderInstitution) {
        this.resourceProviderInstitution = resourceProviderInstitution;
    }

    @XmlElement(name = "projectRef")
    @XmlJavaTypeAdapter(JaxbPersistableConverter.class)
    public Project getProject() {
        if (project == null) {
            return Project.NULL;
        }
        return project;
    }

    @Field(name = QueryFieldNames.PROJECT_ID)
    @Analyzer(impl = KeywordAnalyzer.class)
    public Long getProjectId() {
        if (projectId == null) {
            projectId = getProject().getId();
        }
        return Long.valueOf(-1L).equals(projectId) ? null : projectId;
    }

    @Deprecated
    public void setProjectId(Long projectId) {
        // FIXME: jtd - added this method to assist w/ sensoryData xml creation export. In any other scenario you should probably be using setProject() to
        // implicitly set projectId.
        this.projectId = projectId;
    }

    @Transient
    // @Boost(1.5f)
    @Fields({ @Field(name = QueryFieldNames.PROJECT_TITLE),
            @Field(name = QueryFieldNames.PROJECT_TITLE_AUTO, norms = Norms.NO, store = Store.YES, analyzer = @Analyzer(impl = AutocompleteAnalyzer.class)) })
    public String getProjectTitle() {
        return getProject().getTitleSort();
    }

    @Transient
    @Field(name = QueryFieldNames.PROJECT_TITLE_SORT, norms = Norms.NO, store = Store.YES, analyze = Analyze.NO)
    public String getProjectTitleSort() {
        return getProject().getTitleSort() + " - " + getTitle();
    }

    public void setProject(Project project) {
        if (project == Project.NULL) {
            this.project = null;
        } else {
            this.project = project;
        }
    }

    /**
     * Returns true if this resource is an externally referenced resource,
     * signifying that there is no uploaded file. The URL should then
     * 
     * @return
     */
    public boolean isExternalReference() {
        return externalReference;
    }

    public void setExternalReference(boolean externalReference) {
        this.externalReference = externalReference;
    }

    public Date getLastUploaded() {
        return lastUploaded;
    }

    public void setLastUploaded(Date lastUploaded) {
        this.lastUploaded = lastUploaded;
    }

    // public Set<Document> getRelatedCitations() {
    // return relatedCitations;
    // }
    //
    // public void setRelatedCitations(Set<Document> relatedCitations) {
    // this.relatedCitations = relatedCitations;
    // }
    //
    // public Set<Document> getSourceCitations() {
    // return sourceCitations;
    // }
    //
    // public void setSourceCitations(Set<Document> sourceCitations) {
    // this.sourceCitations = sourceCitations;
    // }

    public int getTotalNumberOfFiles() {
        return informationResourceFiles.size();
    }

    public int getTotalNumberOfActiveFiles() {
        int count = 0;
        for (InformationResourceFile file : informationResourceFiles) {
            if (file.isDeleted()) {
                continue;
            }
            count++;
        }
        return count;
    }

    @Override
    public String getFormattedSourceInformation() {
        StringBuilder sb = new StringBuilder();

        appendIfNotBlank(sb, getPublisherLocation(), ".", "");
        appendIfNotBlank(sb, getPublisherName(), ":", "");

        if ((getDate() != null) && (getDate().intValue() != -1)) {
            appendIfNotBlank(sb, getDate().toString(), ".", "");
        }
        appendIfNotBlank(sb, getCopyLocation(), ".", "");
        return sb.toString();
    }

    @XmlElementWrapper(name = "informationResourceFiles")
    @XmlElement(name = "informationResourceFile")
    public Set<InformationResourceFile> getInformationResourceFiles() {
        return informationResourceFiles;
    }

    @XmlTransient
    public InformationResourceFile getFirstInformationResourceFile() {
        if (getInformationResourceFiles().isEmpty()) {
            return null;
        }
        return informationResourceFiles.iterator().next();
    }

    @XmlTransient
    public Set<InformationResourceFile> getActiveInformationResourceFiles() {
        HashSet<InformationResourceFile> files = new HashSet<>();
        for (InformationResourceFile file : informationResourceFiles) {
            if (!file.isDeleted()) {
                files.add(file);
            }
        }
        return files;
    }

    public void setInformationResourceFiles(Set<InformationResourceFile> informationResourceFiles) {
        this.informationResourceFiles = informationResourceFiles;
    }

    public void add(InformationResourceFile informationResourceFile) {
        getInformationResourceFiles().add(informationResourceFile);
        logger.debug("adding information resource file: {} ({})", informationResourceFile,
                informationResourceFiles.size());
    }

    @XmlTransient
    public Collection<InformationResourceFileVersion> getLatestVersions() {
        // FIXME: this method will become increasingly expensive as the number of files increases
        ArrayList<InformationResourceFileVersion> latest = new ArrayList<InformationResourceFileVersion>();
        for (InformationResourceFile irfile : getInformationResourceFiles()) {
            latest.addAll(irfile.getLatestVersions());
        }
        return latest;
    }

    public Collection<InformationResourceFileVersion> getLatestVersions(String type) {
        return getLatestVersions(VersionType.valueOf(type));
    }

    public Collection<InformationResourceFileVersion> getLatestVersions(VersionType type) {
        ArrayList<InformationResourceFileVersion> latest = new ArrayList<InformationResourceFileVersion>();
        for (InformationResourceFile irfile : getInformationResourceFiles()) {
            InformationResourceFileVersion irfileVersion = irfile.getCurrentVersion(type);
            if (irfileVersion != null) {
                latest.add(irfileVersion);
            }
        }
        return latest;
    }

    @XmlTransient
    public InformationResourceFileVersion getLatestUploadedVersion() {
        Collection<InformationResourceFileVersion> latestUploadedVersions = getLatestUploadedVersions();
        if (CollectionUtils.isEmpty(latestUploadedVersions)) {
            logger.warn("No latest uploaded version for {}", this);
            return null;
        }
        return getLatestUploadedVersions().iterator().next();
    }

    @XmlTransient
    public Collection<InformationResourceFileVersion> getLatestUploadedVersions() {
        return getLatestVersions(VersionType.UPLOADED);
    }

    @Field(store = Store.NO)
    @FieldBridge(impl = PersistentReaderBridge.class)
    @Analyzer(impl = LowercaseWhiteSpaceStandardAnalyzer.class)
    @Transient
    // @Boost(0.5f)
    @XmlTransient
    public List<InformationResourceFileVersion> getContent() {
        logger.trace("getContent");
        List<InformationResourceFile> files = getPublicFiles();
        if (CollectionUtils.isEmpty(files)) {
            return null;
        }
        // List<InputStream> streams = new ArrayList<InputStream>();
        List<InformationResourceFileVersion> fileURIs = new ArrayList<InformationResourceFileVersion>();
        for (InformationResourceFile irFile : files) {
            try {
                if (irFile.getRestriction().isRestricted()) {
                    continue;
                }
                InformationResourceFileVersion indexableVersion = irFile.getIndexableVersion();
                fileURIs.add(indexableVersion);
            } catch (Exception e) {
                logger.trace("an exception occurred while reading file: {} ", e);
            }
        }
        return fileURIs;
    }

    @Field(norms = Norms.NO, store = Store.YES, name = QueryFieldNames.RESOURCE_ACCESS_TYPE, analyzer = @Analyzer(impl = TdarCaseSensitiveStandardAnalyzer.class))
    @Transient
    public ResourceAccessType getResourceAccessType() {
        int totalFiles = getNonDeletedFiles().size();
        int publicFiles = getPublicFiles().size();
        if (totalFiles > 0) {
            if (publicFiles == 0) {
                return ResourceAccessType.RESTRICTED;
            }
            if (publicFiles == totalFiles) {
                return ResourceAccessType.PUBLICALLY_ACCESSIBLE;
            }
            return ResourceAccessType.PARTIALLY_RESTRICTED;
        }
        return ResourceAccessType.CITATION;
    }

    @Transient
    @XmlTransient
    public boolean isPublicallyAccessible() {
        return getResourceAccessType() == ResourceAccessType.PUBLICALLY_ACCESSIBLE;
    }

    @Transient
    public boolean getContainsFiles() {
        return hasFiles();
    }

    @Transient
    public boolean hasFiles() {
        return getInformationResourceFiles().size() > 0;
    }

    public boolean isInheritingInvestigationInformation() {
        return inheritingInvestigationInformation;
    }

    public void setInheritingInvestigationInformation(boolean inheritingInvestigationInformation) {
        this.inheritingInvestigationInformation = inheritingInvestigationInformation;
    }

    public boolean isInheritingSiteInformation() {
        return inheritingSiteInformation;
    }

    public void setInheritingSiteInformation(boolean inheritingSiteInformation) {
        this.inheritingSiteInformation = inheritingSiteInformation;
    }

    public boolean isInheritingMaterialInformation() {
        return inheritingMaterialInformation;
    }

    public void setInheritingMaterialInformation(boolean inheritingMaterialInformation) {
        this.inheritingMaterialInformation = inheritingMaterialInformation;
    }

    public boolean isInheritingOtherInformation() {
        return inheritingOtherInformation;
    }

    public void setInheritingOtherInformation(boolean inheritingOtherInformation) {
        this.inheritingOtherInformation = inheritingOtherInformation;
    }

    public boolean isInheritingCulturalInformation() {
        return inheritingCulturalInformation;
    }

    public void setInheritingCulturalInformation(boolean inheritingCulturalInformation) {
        this.inheritingCulturalInformation = inheritingCulturalInformation;
    }

    public boolean isInheritingSpatialInformation() {
        return inheritingSpatialInformation;
    }

    public void setInheritingSpatialInformation(boolean inheritingSpatialInformation) {
        this.inheritingSpatialInformation = inheritingSpatialInformation;
    }

    public boolean isInheritingTemporalInformation() {
        return inheritingTemporalInformation;
    }

    public void setInheritingTemporalInformation(boolean inheritingTemporalInformation) {
        this.inheritingTemporalInformation = inheritingTemporalInformation;
    }

    public String getCopyLocation() {
        return copyLocation;
    }

    public void setCopyLocation(String copyLocation) {
        this.copyLocation = copyLocation;
    }

    @IndexedEmbedded
    @Override
    public Set<InvestigationType> getActiveInvestigationTypes() {
        return isProjectVisible() && isInheritingInvestigationInformation() ? project.getInvestigationTypes()
                : getInvestigationTypes();
    }

    @IndexedEmbedded
    @Override
    public Set<ResourceCreator> getActiveIndividualAndInstitutionalCredit() {
        return isProjectVisible() && isInheritingIndividualAndInstitutionalCredit()
                ? project.getIndividualAndInstitutionalCredit()
                : getIndividualAndInstitutionalCredit();
    }

    @IndexedEmbedded
    @Override
    public Set<ResourceCreator> getActiveResourceCreators() {
        Set<ResourceCreator> local = new HashSet<ResourceCreator>(super.getResourceCreators());
        if (isProjectVisible() && isInheritingIndividualAndInstitutionalCredit()) {
            local.addAll(project.getIndividualAndInstitutionalCredit());
        }
        return local;
    }

    @Transient
    @XmlTransient
    public boolean isProjectVisible() {
        // FIXME: indexing was dying when project below was replaced with getProject()
        return getProject().isActive() || getProject().isDraft();
    }

    @IndexedEmbedded
    @Override
    public Set<SiteNameKeyword> getActiveSiteNameKeywords() {
        return isProjectVisible() && isInheritingSiteInformation() ? project.getSiteNameKeywords()
                : getSiteNameKeywords();
    }

    @IndexedEmbedded
    @Override
    public Set<SourceCollection> getActiveSourceCollections() {
        return isProjectVisible() && isInheritingCollectionInformation() ? project.getSourceCollections()
                : getSourceCollections();
    }

    @IndexedEmbedded
    @Override
    public Set<RelatedComparativeCollection> getActiveRelatedComparativeCollections() {
        return isProjectVisible() && isInheritingCollectionInformation()
                ? project.getRelatedComparativeCollections()
                : getRelatedComparativeCollections();
    }

    @IndexedEmbedded
    @Override
    public Set<SiteTypeKeyword> getActiveSiteTypeKeywords() {
        return isProjectVisible() && isInheritingSiteInformation() ? project.getSiteTypeKeywords()
                : getSiteTypeKeywords();
    }

    public Set<SiteTypeKeyword> getActiveApprovedSiteTypeKeywords() {
        return isProjectVisible() && isInheritingSiteInformation() ? project.getApprovedSiteTypeKeywords()
                : getApprovedSiteTypeKeywords();
    }

    public Set<SiteTypeKeyword> getActiveUncontrolledSiteTypeKeywords() {
        return isProjectVisible() && isInheritingSiteInformation() ? project.getUncontrolledSiteTypeKeywords()
                : getUncontrolledSiteTypeKeywords();
    }

    @Override
    @IndexedEmbedded
    public Set<MaterialKeyword> getActiveMaterialKeywords() {
        return isProjectVisible() && isInheritingMaterialInformation() ? project.getMaterialKeywords()
                : getMaterialKeywords();
    }

    @Override
    @IndexedEmbedded(targetElement = OtherKeyword.class)
    public Set<OtherKeyword> getActiveOtherKeywords() {
        return isProjectVisible() && isInheritingOtherInformation() ? project.getOtherKeywords()
                : getOtherKeywords();
    }

    @Override
    @IndexedEmbedded
    public Set<CultureKeyword> getActiveCultureKeywords() {
        return isProjectVisible() && isInheritingCulturalInformation() ? project.getCultureKeywords()
                : getCultureKeywords();
    }

    public Set<CultureKeyword> getActiveApprovedCultureKeywords() {
        return isProjectVisible() && isInheritingCulturalInformation() ? project.getApprovedCultureKeywords()
                : getApprovedCultureKeywords();
    }

    @Override
    public Set<ResourceNote> getActiveResourceNotes() {
        return isProjectVisible() && isInheritingNoteInformation() ? project.getResourceNotes()
                : getResourceNotes();
    }

    @Override
    public Set<ResourceAnnotation> getActiveResourceAnnotations() {
        return isProjectVisible() && isInheritingIdentifierInformation() ? project.getResourceAnnotations()
                : getResourceAnnotations();
    }

    public Set<CultureKeyword> getActiveUncontrolledCultureKeywords() {
        return isProjectVisible() && isInheritingCulturalInformation() ? project.getUncontrolledCultureKeywords()
                : getUncontrolledCultureKeywords();
    }

    @Override
    @IndexedEmbedded
    public Set<GeographicKeyword> getActiveGeographicKeywords() {
        return isProjectVisible() && isInheritingSpatialInformation() ? project.getGeographicKeywords()
                : getGeographicKeywords();
    }

    @Override
    @IndexedEmbedded
    public Set<LatitudeLongitudeBox> getActiveLatitudeLongitudeBoxes() {
        return isProjectVisible() && isInheritingSpatialInformation() ? project.getLatitudeLongitudeBoxes()
                : getLatitudeLongitudeBoxes();
    }

    @Override
    @IndexedEmbedded
    public Set<TemporalKeyword> getActiveTemporalKeywords() {
        return isProjectVisible() && isInheritingTemporalInformation() ? project.getTemporalKeywords()
                : getTemporalKeywords();
    }

    @Override
    @IndexedEmbedded
    public Set<CoverageDate> getActiveCoverageDates() {
        return isProjectVisible() && isInheritingTemporalInformation() ? project.getCoverageDates()
                : getCoverageDates();
    }

    @Transient
    @Override
    public boolean hasConfidentialFiles() {
        return !getConfidentialFiles().isEmpty();
    }

    @Transient
    @Override
    public boolean hasEmbargoedFiles() {
        for (InformationResourceFile file : getConfidentialFiles()) {
            if (file.isEmbargoed()) {
                return true;
            }
        }
        return false;
    }

    @Transient
    public List<InformationResourceFile> getFilesWithRestrictions(boolean confidential) {
        List<InformationResourceFile> confidentialFiles = new ArrayList<InformationResourceFile>();
        List<InformationResourceFile> publicFiles = new ArrayList<InformationResourceFile>();
        for (InformationResourceFile irFile : getNonDeletedFiles()) {
            if (irFile.isPublic()) {
                publicFiles.add(irFile);
            } else {
                confidentialFiles.add(irFile);
            }
        }
        if (confidential) {
            return confidentialFiles;
        } else {
            return publicFiles;
        }
    }

    @Transient
    public List<InformationResourceFile> getConfidentialFiles() {
        return getFilesWithRestrictions(true);
    }

    @Override
    @XmlTransient
    public String getAdditonalKeywords() {
        StringBuilder sb = new StringBuilder();
        sb.append(getCopyLocation()).append(" ").append(date);
        if (getResourceProviderInstitution() != null) {
            sb.append(" ").append(getResourceProviderInstitution().getName());
        }
        sb.append(" ").append(getPublisherName());
        sb.append(" ").append(getDoi());
        if (MapUtils.isNotEmpty(relatedDatasetData)) {
            for (String v : relatedDatasetData.values()) {
                sb.append(v);
                sb.append(" ");
            }
        }

        if (CollectionUtils.isNotEmpty(getActiveInformationResourceFiles())) {
            for (InformationResourceFile file : getActiveInformationResourceFiles()) {
                sb.append(file.getFilename());
                sb.append(" ");
                sb.append(file.getDescription());
                sb.append(" ");
            }
        }

        // if (getProject() != null) {
        // getProject().getTitle();
        // }
        return sb.toString();
    }

    @Transient
    @XmlTransient
    public List<InformationResourceFile> getPublicFiles() {
        return getFilesWithRestrictions(false);
    }

    @Override
    public boolean isValidForController() {
        if (date == null) {
            throw new TdarValidationException("informationResource.created_date_required",
                    Arrays.asList(getResourceType()));
        }
        return super.isValidForController();
    }

    public DataTableColumn getMappedDataKeyColumn() {
        return mappedDataKeyColumn;
    }

    public void setMappedDataKeyColumn(DataTableColumn mappedDataKeyColumn) {
        this.mappedDataKeyColumn = mappedDataKeyColumn;
    }

    public String getMappedDataKeyValue() {
        return mappedDataKeyValue;
    }

    public void setMappedDataKeyValue(String mappedDataKeyValue) {
        this.mappedDataKeyValue = mappedDataKeyValue;
    }

    @Field(norms = Norms.YES, store = Store.NO)
    @FieldBridge(impl = StringMapBridge.class)
    public Map<DataTableColumn, String> getRelatedDatasetData() {
        return relatedDatasetData;
    }

    public void setRelatedDatasetData(Map<DataTableColumn, String> relatedDatasetData) {
        this.relatedDatasetData = relatedDatasetData;
    }

    @Transient
    @XmlTransient
    public boolean isInheritingSomeMetadata() {
        return (inheritingCulturalInformation || inheritingInvestigationInformation || inheritingMaterialInformation
                || inheritingOtherInformation || inheritingSiteInformation || inheritingSpatialInformation
                || inheritingTemporalInformation || inheritingIdentifierInformation || inheritingNoteInformation
                || inheritingIndividualAndInstitutionalCredit);
    }

    @Transient
    @Override
    public Set<Obfuscatable> obfuscate() {
        // don't claim to inherit data from Projects which are inactive
        Set<Obfuscatable> toObfuscate = super.obfuscate();
        if (!isProjectVisible()) {
            setProject(Project.NULL);
            // setting the project to null should be enough...
            setInheritingCulturalInformation(false);
            setInheritingIndividualAndInstitutionalCredit(false);
            setInheritingInvestigationInformation(false);
            setInheritingMaterialInformation(false);
            setInheritingOtherInformation(false);
            setInheritingSiteInformation(false);
            setInheritingSpatialInformation(false);
            setInheritingTemporalInformation(false);
            setInheritingIdentifierInformation(false);
            setInheritingNoteInformation(false);
        } else {
            toObfuscate.add(getProject());
        }
        toObfuscate.add(resourceProviderInstitution);
        toObfuscate.add(publisher);
        return toObfuscate;
    }

    @Override
    @XmlTransient
    public List<String> getCreatorRoleIdentifiers() {
        List<String> list = super.getCreatorRoleIdentifiers();
        list.add(ResourceCreator.getCreatorRoleIdentifier(getResourceProviderInstitution(),
                ResourceCreatorRole.RESOURCE_PROVIDER));
        list.add(ResourceCreator.getCreatorRoleIdentifier(getPublisher(), ResourceCreatorRole.PUBLISHER));
        return list;
    }

    @Override
    @XmlTransient
    public List<Creator> getRelatedCreators() {
        List<Creator> creators = super.getRelatedCreators();
        creators.add(getResourceProviderInstitution());
        creators.add(getPublisher());
        return creators;
    }

    public boolean isInheritingNoteInformation() {
        return inheritingNoteInformation;
    }

    public void setInheritingNoteInformation(boolean inheritingNoteInformation) {
        this.inheritingNoteInformation = inheritingNoteInformation;
    }

    public boolean isInheritingIdentifierInformation() {
        return inheritingIdentifierInformation;
    }

    public void setInheritingIdentifierInformation(boolean inheritingIdentifierInformation) {
        this.inheritingIdentifierInformation = inheritingIdentifierInformation;
    }

    public boolean isInheritingCollectionInformation() {
        return inheritingCollectionInformation;
    }

    public void setInheritingCollectionInformation(boolean inheritingCollectionInformation) {
        this.inheritingCollectionInformation = inheritingCollectionInformation;
    }

    // shortcut for non-deleted, visible files
    @Transient
    @XmlTransient
    @IndexedEmbedded
    public List<InformationResourceFile> getVisibleFilesWithThumbnails() {
        ArrayList<InformationResourceFile> visibleFiles = new ArrayList<InformationResourceFile>();
        for (InformationResourceFile irfile : getVisibleFiles()) {
            if (logger.isTraceEnabled()) {
                logger.debug("{}", irfile.getLatestThumbnail());
            }
            if (irfile.getLatestThumbnail() != null) {
                visibleFiles.add(irfile);
            }
        }
        return visibleFiles;
    }

    // shortcut for non-deleted, visible files
    @Transient
    @XmlTransient
    public List<InformationResourceFile> getVisibleFiles() {
        ArrayList<InformationResourceFile> visibleFiles = new ArrayList<InformationResourceFile>();
        for (InformationResourceFile irfile : getInformationResourceFiles()) {
            if (logger.isTraceEnabled()) {
                logger.trace("{} ({} {} )", irfile, irfile.isViewable(), irfile.isDeleted());
            }
            if (irfile.isViewable() && !irfile.isDeleted()) {
                visibleFiles.add(irfile);
            }
        }
        return visibleFiles;
    }

    private transient InformationResourceFileVersion primaryThumbnail = null;
    private transient Boolean hasPrimaryThumbnail = null;

    // get the latest version of the first non-deleted thumbnail (or null)
    @Transient
    @XmlTransient
    public InformationResourceFileVersion getPrimaryThumbnail() {
        if (hasPrimaryThumbnail != null) {
            return primaryThumbnail;
        }
        hasPrimaryThumbnail = Boolean.FALSE;
        for (InformationResourceFile firstVisible : getVisibleFilesWithThumbnails()) {
            hasPrimaryThumbnail = Boolean.TRUE;
            primaryThumbnail = firstVisible.getLatestThumbnail();
        }
        return primaryThumbnail;
    }

    @Transient
    @XmlTransient
    public List<InformationResourceFile> getNonDeletedFiles() {
        List<InformationResourceFile> files = new ArrayList<InformationResourceFile>();
        for (InformationResourceFile irf : getInformationResourceFiles()) {
            if (!irf.isDeleted()) {
                files.add(irf);
            }
        }
        return files;
    }

    @Transient
    @Override
    // we consider a record to be citation record if it doesn't have any file attachments.
    public boolean isCitationRecord() {
        return getResourceAccessType() == ResourceAccessType.CITATION;
    }

    public Institution getPublisher() {
        return publisher;
    }

    public void setPublisher(Institution publisher) {
        this.publisher = publisher;
    }

    public String getPublisherLocation() {
        return publisherLocation;
    }

    public void setPublisherLocation(String publisherLocation) {
        this.publisherLocation = publisherLocation;
    }

    public String getPublisherName() {
        if (publisher != null) {
            return publisher.getName();
        }
        return null;
    }

    @Override
    public <R extends Resource> void copyImmutableFieldsFrom(R resource_) {
        super.copyImmutableFieldsFrom(resource_);
        InformationResource resource = (InformationResource) resource_;
        this.getInformationResourceFiles()
                .addAll(new HashSet<InformationResourceFile>(resource.getInformationResourceFiles()));
        this.setPublisher(resource.getPublisher());
        this.setResourceProviderInstitution(resource.getResourceProviderInstitution());

    };

    @XmlTransient
    public List<InformationResourceFile> getFilesWithProcessingErrors() {
        List<InformationResourceFile> files = new ArrayList<InformationResourceFile>();
        for (InformationResourceFile file : getInformationResourceFiles()) {
            if ((file.getStatus() == FileStatus.PROCESSING_ERROR)
                    || (file.getStatus() == FileStatus.PROCESSING_WARNING)) {
                files.add(file);
            }
        }
        return files;
    }

    @XmlTransient
    public List<InformationResourceFile> getFilesWithFatalProcessingErrors() {
        List<InformationResourceFile> files = new ArrayList<InformationResourceFile>();
        for (InformationResourceFile file : getInformationResourceFiles()) {
            if (file.getStatus() == FileStatus.PROCESSING_ERROR) {
                files.add(file);
            }
        }
        return files;
    }

    @Transient
    @XmlTransient
    public Set<ResourceCreator> getContacts() {
        return getResourceCreators(ResourceCreatorRole.CONTACT);
    }

    /**
     * Override this if you need to pass resource specific information on to the work flow process.
     * Make a new instance of the resource, and then copy the fields across that will be needed by the work flow process
     * 
     * @see Archive#getTransientCopyForWorkflow() for an implementation
     * @return <b>The default is null!</b> A copy of the information resource that will be serialised and sent to the work flow.
     */
    @SuppressWarnings("static-method")
    @Transient
    @XmlTransient
    public InformationResource getTransientCopyForWorkflow() {
        return null;
    }

    /**
     * Override this method to write back the fields that may have been changed in the transient copy
     * 
     * @param transientCopy
     */
    public void updateFromTransientResource(InformationResource transientCopy) {
        // Should we throw an exception if we are here ?
    }

    public boolean isInheritingIndividualAndInstitutionalCredit() {
        return inheritingIndividualAndInstitutionalCredit;
    }

    public void setInheritingIndividualAndInstitutionalCredit(boolean inheritingIndividualAndInstitutionalCredit) {
        this.inheritingIndividualAndInstitutionalCredit = inheritingIndividualAndInstitutionalCredit;
    }

    public String getDoi() {
        return doi;
    }

    public void setDoi(String doi) {
        this.doi = doi;
    }

    private transient List<FileProxy> fileProxies = new ArrayList<>();

    @XmlElementWrapper(name = "fileProxies")
    @XmlElement(name = "fileProxy")
    public List<FileProxy> getFileProxies() {
        return fileProxies;
    }

    public void setFileProxies(List<FileProxy> fileProxies) {
        this.fileProxies = fileProxies;
    }
}