ch.systemsx.cisd.openbis.generic.shared.dto.AttachmentPE.java Source code

Java tutorial

Introduction

Here is the source code for ch.systemsx.cisd.openbis.generic.shared.dto.AttachmentPE.java

Source

/*
 * Copyright 2007 ETH Zuerich, CISD
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package ch.systemsx.cisd.openbis.generic.shared.dto;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.Reader;
import java.io.Serializable;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.hibernate.annotations.Cascade;
import org.hibernate.search.annotations.ClassBridge;
import org.hibernate.search.annotations.ContainedIn;
import org.hibernate.search.annotations.Index;
import org.hibernate.search.annotations.Store;
import org.hibernate.search.bridge.FieldBridge;
import org.hibernate.search.bridge.LuceneOptions;
import org.hibernate.validator.Length;
import org.hibernate.validator.NotNull;

import com.sun.org.apache.xerces.internal.impl.io.UTF8Reader;

import ch.rinn.restrictions.Private;
import ch.systemsx.cisd.common.utilities.ModifiedShortPrefixToStringStyle;
import ch.systemsx.cisd.openbis.generic.client.web.client.application.GenericConstants;
import ch.systemsx.cisd.openbis.generic.shared.IServer;
import ch.systemsx.cisd.openbis.generic.shared.basic.IIdHolder;
import ch.systemsx.cisd.openbis.generic.shared.dto.hibernate.SearchFieldConstants;
import ch.systemsx.cisd.openbis.generic.shared.util.EqualsHashUtils;

/**
 * Contains information about an attachment together with its content.
 * 
 * @author Bernd Rinn
 */
@Entity
@Table(name = TableNames.ATTACHMENTS_TABLE, uniqueConstraints = {
        @UniqueConstraint(columnNames = { ColumnNames.EXPERIMENT_COLUMN, ColumnNames.FILE_NAME_COLUMN,
                ColumnNames.VERSION_COLUMN }),
        @UniqueConstraint(columnNames = { ColumnNames.SAMPLE_COLUMN, ColumnNames.FILE_NAME_COLUMN,
                ColumnNames.VERSION_COLUMN }),
        @UniqueConstraint(columnNames = { ColumnNames.PROJECT_COLUMN, ColumnNames.FILE_NAME_COLUMN,
                ColumnNames.VERSION_COLUMN }) })
@ClassBridge(impl = AttachmentPE.AttachmentSearchBridge.class, index = Index.TOKENIZED, store = Store.NO)
public class AttachmentPE extends HibernateAbstractRegistrationHolder
        implements Serializable, Comparable<AttachmentPE>, IIdHolder {
    private static final long serialVersionUID = IServer.VERSION;

    public static final AttachmentPE[] EMPTY_ARRAY = new AttachmentPE[0];

    private String fileName;

    private int version;

    private AttachmentContentPE attachmentContent;

    transient private Long id;

    /**
     * Parent (e.g. an experiment) to which this attachment belongs.
     */
    private ExperimentPE experimentParent;

    private SamplePE sampleParent;

    private ProjectPE projectParent;

    private String description;

    private String title;

    /**
     * This bridge allows to save in the search index not only the content of the attachment, but
     * also corresponding file name and version.
     */
    public static class AttachmentSearchBridge implements FieldBridge {
        /**
         * The user can force indexing of the attachment by putting this prefix into attachment
         * description.
         */
        private static final String SEARCHABLE_ATTACHMENT_MARKER = "[searchable]";

        public void set(String name, Object/* AttachmentPE */ value, Document/* Lucene document */ document,
                LuceneOptions luceneOptions) {
            AttachmentPE attachment = (AttachmentPE) value;
            String attachmentName = attachment.getFileName();
            if (isSearchable(attachment)) {
                // TODO 2009-07-28, Tomasz Pylak: switch on indexing attachments
                // indexFileContent(document, luceneOptions, attachment, attachmentName);
            }
            // index the file name. Make the field code unique, so that we can recognize which field
            // has matched the query later on
            String attachmentNameFieldName = name + "'" + attachmentName + "' " + SearchFieldConstants.FILE_NAME;
            Field field = new Field(attachmentNameFieldName, attachmentName, Field.Store.YES,
                    luceneOptions.getIndex());
            document.add(field);
        }

        private static boolean isSearchable(AttachmentPE attachment) {
            String fileExt = FilenameUtils.getExtension(attachment.getFileName()).toLowerCase();
            if (fileExt.equals("txt") || fileExt.equals("pdf")) {
                return true;
            }
            String desc = attachment.getDescription();
            if (StringUtils.isNotBlank(desc) && desc.startsWith(SEARCHABLE_ATTACHMENT_MARKER)) {
                return true;
            }
            return false;
        }

        @SuppressWarnings("unused")
        private void indexFileContent(Document document, LuceneOptions luceneOptions, AttachmentPE attachment,
                String attachmentName) {
            byte[] byteContent = attachment.getAttachmentContent().getValue();
            Reader reader = createAttachmentReader(byteContent);
            indexFileContent(document, luceneOptions, attachment, attachmentName, reader);
        }

        private static Reader createAttachmentReader(byte[] byteContent) {
            // By default the UTF8 reader throws exception when the file content is binary.
            // In this case we just stop reading the file.
            return new UTF8Reader(new ByteArrayInputStream(byteContent)) {
                @Override
                public int read(char cbuf[], int off, int len) throws IOException {
                    try {
                        return super.read(cbuf, off, len);
                    } catch (Exception e) {
                        return -1;
                    }
                }
            };
        }

        private void indexFileContent(Document document, LuceneOptions luceneOptions, AttachmentPE attachment,
                String attachmentName, Reader contentReader) {
            String fieldName = SearchFieldConstants.PREFIX_ATTACHMENT + "'" + attachmentName + "', ver. "
                    + attachment.getVersion();

            Field field = new Field(fieldName, contentReader);
            if (luceneOptions.getBoost() != null) {
                field.setBoost(luceneOptions.getBoost());
            }
            document.add(field);
        }
    }

    /**
     * Returns the file name of the property or <code>null</code>.
     */
    @Column(name = ColumnNames.FILE_NAME_COLUMN)
    @NotNull(message = ValidationMessages.FILE_NAME_NOT_NULL_MESSAGE)
    @Length(max = 100, message = ValidationMessages.FILE_NAME_LENGTH_MESSAGE)
    public String getFileName() {
        return fileName;
    }

    public void setFileName(final String fileName) {
        this.fileName = fileName;
    }

    @Column(name = ColumnNames.VERSION_COLUMN)
    @NotNull(message = ValidationMessages.VERSION_NOT_NULL_MESSAGE)
    public int getVersion() {
        return version;
    }

    public void setVersion(final int version) {
        this.version = version;
    }

    @SequenceGenerator(name = SequenceNames.ATTACHMENT_SEQUENCE, sequenceName = SequenceNames.ATTACHMENT_SEQUENCE, allocationSize = 1)
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = SequenceNames.ATTACHMENT_SEQUENCE)
    public final Long getId() {
        return id;
    }

    public final void setId(final Long id) {
        this.id = id;
    }

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = ColumnNames.EXPERIMENT_COLUMN, updatable = false)
    @Private
    // for Hibernate and bean conversion only
    @ContainedIn
    public ExperimentPE getExperimentParentInternal() {
        return experimentParent;
    }

    @Private
    // for Hibernate and bean conversion only
    public void setExperimentParentInternal(final ExperimentPE parent) {
        this.experimentParent = parent;
    }

    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @Cascade(value = org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
    @NotNull(message = ValidationMessages.ATTACHMENT_CONTENT_NOT_NULL_MESSAGE)
    @JoinColumn(name = ColumnNames.ATTACHMENT_CONTENT_COLUMN)
    public AttachmentContentPE getAttachmentContent() {
        return attachmentContent;
    }

    public void setAttachmentContent(final AttachmentContentPE attachmentContent) {
        this.attachmentContent = attachmentContent;
    }

    @Column(name = ColumnNames.TITLE_COLUMN)
    @Length(max = 100, message = ValidationMessages.TITLE_LENGTH_MESSAGE)
    public String getTitle() {
        return title;
    }

    public void setTitle(final String title) {
        this.title = title;
    }

    @Column(name = ColumnNames.DESCRIPTION_COLUMN)
    @Length(max = GenericConstants.DESCRIPTION_1000, message = ValidationMessages.DESCRIPTION_LENGTH_MESSAGE)
    public String getDescription() {
        return description;
    }

    public void setDescription(final String description) {
        this.description = description;
    }

    @Override
    public final boolean equals(final Object obj) {
        EqualsHashUtils.assertDefined(getFileName(), "name");
        EqualsHashUtils.assertDefined(getVersion(), "version");
        if (obj == this) {
            return true;
        }
        if (obj instanceof AttachmentPE == false) {
            return false;
        }
        final AttachmentPE that = (AttachmentPE) obj;
        final EqualsBuilder builder = new EqualsBuilder();
        builder.append(getFileName(), that.getFileName());
        builder.append(getVersion(), that.getVersion());
        builder.append(getParent(), that.getParent());
        return builder.isEquals();
    }

    @Override
    public final int hashCode() {
        final HashCodeBuilder builder = new HashCodeBuilder();
        builder.append(getFileName());
        builder.append(getVersion());
        builder.append(getParent());
        return builder.toHashCode();
    }

    @Override
    public final String toString() {
        final ToStringBuilder builder = new ToStringBuilder(this,
                ModifiedShortPrefixToStringStyle.MODIFIED_SHORT_PREFIX_STYLE);
        builder.append("fileName", getFileName());
        builder.append("version", getVersion());
        builder.append("parent", getParent());
        return builder.toString();
    }

    //
    // Comparable
    //

    public final int compareTo(final AttachmentPE o) {
        final int byFile = getFileName().compareTo(o.getFileName());
        return byFile == 0 ? Integer.valueOf(getVersion()).compareTo(o.getVersion()) : byFile;
    }

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = ColumnNames.SAMPLE_COLUMN, updatable = false)
    @Private
    // for Hibernate and bean conversion only
    @ContainedIn
    public SamplePE getSampleParentInternal() {
        return sampleParent;
    }

    @Private
    // for Hibernate and bean conversion only
    public void setSampleParentInternal(final SamplePE parent) {
        this.sampleParent = parent;
    }

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = ColumnNames.PROJECT_COLUMN, updatable = false)
    @Private
    // for Hibernate and bean conversion only
    @ContainedIn
    public ProjectPE getProjectParentInternal() {
        return projectParent;
    }

    @Private
    // for Hibernate and bean conversion only
    public void setProjectParentInternal(final ProjectPE parent) {
        this.projectParent = parent;
    }

    @Transient
    public AttachmentHolderPE getParent() {
        if (getExperimentParentInternal() != null) {
            return getExperimentParentInternal();
        } else if (getSampleParentInternal() != null) {
            return getSampleParentInternal();
        } else if (getProjectParentInternal() != null) {
            return getProjectParentInternal();
        }
        return null;
    }

    public void setParent(AttachmentHolderPE owner) {
        if (owner instanceof ExperimentPE) {
            setExperimentParentInternal((ExperimentPE) owner);
        } else if (owner instanceof SamplePE) {
            setSampleParentInternal((SamplePE) owner);
        } else if (owner instanceof ProjectPE) {
            setProjectParentInternal((ProjectPE) owner);
        } else {
            throw new IllegalStateException("Unexpected attachment holder.");
        }
    }

}