<commit_before>package models;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;
import java.util.List;

import javax.persistence.*;

import models.resource.Resource;
import org.apache.commons.io.FileUtils;
import org.apache.tika.Tika;

import models.enumeration.ResourceType;

import play.data.validation.*;

import play.db.ebean.Model;
import scalax.file.NotDirectoryException;

@Entity
public class Attachment extends Model {
    private static final long serialVersionUID = 7856282252495067924L;
    public static Finder<Long, Attachment> find = new Finder<Long, Attachment>(Long.class,
            Attachment.class);
    private static String uploadDirectory = "uploads";
    @Id
    public Long id;

    @Constraints.Required
    public String name;

    @Constraints.Required
    public String hash;

    public Long projectId;

    @Enumerated(EnumType.STRING)
    public ResourceType containerType;

    public String mimeType;

    public Long size;

    public Long containerId;

    /**
     * 주어진 {@code attach}와 내용이 같은 첨부 파일을 찾는다.
     *
     * 내용이 같은 첨부 파일이라고 하는 것은 이름과 파일의 내용이 같고 첨부된 리소스도 같은 것을 말한다.
     * 구체적으로 말하면 {@link Attachment#name}, {@link Attachment#hash},
     * {@link Attachment#containerType}, {@link Attachment#containerId}가 서로 같은 첨부이다.
     *
     * @param attach 이 첨부 파일과 내용이 같은 첨부 파일을 찾는다.
     * @return 발견된 첨부 파일
     */
    private static Attachment findBy(Attachment attach) {
        return find.where()
                .eq("name", attach.name)
                .eq("hash", attach.hash)
                .eq("containerType", attach.containerType)
                .eq("containerId", attach.containerId).findUnique();
    }

    /**
     * 파일의 해시값이 {@code hash}인 첨부 파일이 존재하는지의 여부를 반환한다.
     *
     * @param hash 해시값
     * @return 해시값이 {@code hash}와 같은 첨부가 존재하는지의 여부
     */
    public static boolean exists(String hash) {
        return find.where().eq("hash", hash).findRowCount() > 0;
    }

    /**
     * 주어진 {@code containerType}과 {@code containerId}에 대응하는 리소스에 첨부된 첨부 파일의 목록을 반환한다.
     *
     * {@code containerType}은 {@link models.resource.Resource#getType()}의 반환값과,
     * {@code containerId}는 {@link models.resource.Resource#getId()}의 반환값과 비교한다.
     *
     * @param containerType 첨부 파일이 첨부된 리소스의 타입
     * @param containerId 첨부 파일이 첨부된 리소스의 아이디
     * @return 첨부 파일의 목록
     */
    public static List<Attachment> findByContainer(
            ResourceType containerType, Long containerId) {
        return find.where()
                .eq("containerType", containerType)
                .eq("containerId", containerId).findList();
    }

    /**
     * 주어진 {@code container}의 첨부된 첨부 파일의 목록을 반환한다.
     *
     * @param container 첨부 파일이 첨부된 리소스
     * @return 첨부 파일의 목록
     */
    public static List<Attachment> findByContainer(Resource container) {
        return findByContainer(container.getType(), container.getId());
    }

    /**
     * 주어진 {@code container}에 첨부된 첨부 파일의 갯수를 반환한다.
     *
     * when:
     *
     * @param container 첨부 파일이 첨부된 리소스
     * @return 첨부 파일의 목록
     */
    public static int countByContainer(Resource container) {
        return find.where()
                .eq("containerType", container.getType())
                .eq("containerId", container.getId()).findRowCount();
    }

    /**
     * {@code from}에 첨부된 모든 첨부 파일을 {@code to}로 옮긴다.
     *
     * when: 업로드 직후 일시적으로 사용자에게 첨부되었던 첨부 파일들을, 특정 리소스(이슈, 게시물 등)으로 옮기려 할 때
     *
     * @param from 첨부 파일이 원래 있었던 리소스
     * @param to 첨부 파일이 새로 옮겨질 리소스
     * @return
     */
    public static int moveAll(Resource from, Resource to) {
        List<Attachment> attachments = Attachment.findByContainer(from);
        for (Attachment attachment : attachments) {
            if(to.getProject() != null) {
                attachment.projectId = to.getProject().id;
            } else {
                attachment.projectId = null;
            }
            attachment.containerType = to.getType();
            attachment.containerId = to.getId();
            attachment.save();
        }
        return attachments.size();
    }

    /**
     * {@code file}을 업로드 디렉토리로 옮긴다.
     *
     * when: Play 프레임워크는 파일을 업로드하면 일단 임시 디렉토리에 보관하는데, 이 파일을 업로드 디렉토리로
     * 옮기려 할 때 사용한다.
     *
     * @param file
     * @return
     * @throws NoSuchAlgorithmException
     * @throws IOException
     */
    private static String moveFileIntoUploadDirectory(File file)
            throws NoSuchAlgorithmException, IOException {
        // Compute sha1 checksum.
        MessageDigest algorithm = MessageDigest.getInstance("SHA1");
        DigestInputStream dis = new DigestInputStream(
                new BufferedInputStream(new FileInputStream(file)), algorithm);
        while (dis.read() != -1);
        Formatter formatter = new Formatter();
        for (byte b : algorithm.digest()) {
            formatter.format("%02x", b);
        }
        String hash = formatter.toString();

        // Store the file.
        // Before do that, create upload directory if it doesn't exist.
        File uploads = new File(uploadDirectory);<commit_msg>attachment: Add moveTo method.<commit_after>package models;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;
import java.util.List;

import javax.persistence.*;

import models.resource.Resource;
import org.apache.commons.io.FileUtils;
import org.apache.tika.Tika;

import models.enumeration.ResourceType;

import play.data.validation.*;

import play.db.ebean.Model;
import scalax.file.NotDirectoryException;

@Entity
public class Attachment extends Model {
    private static final long serialVersionUID = 7856282252495067924L;
    public static Finder<Long, Attachment> find = new Finder<Long, Attachment>(Long.class,
            Attachment.class);
    private static String uploadDirectory = "uploads";
    @Id
    public Long id;

    @Constraints.Required
    public String name;

    @Constraints.Required
    public String hash;

    public Long projectId;

    @Enumerated(EnumType.STRING)
    public ResourceType containerType;

    public String mimeType;

    public Long size;

    public Long containerId;

    /**
     * 주어진 {@code attach}와 내용이 같은 첨부 파일을 찾는다.
     *
     * 내용이 같은 첨부 파일이라고 하는 것은 이름과 파일의 내용이 같고 첨부된 리소스도 같은 것을 말한다.
     * 구체적으로 말하면 {@link Attachment#name}, {@link Attachment#hash},
     * {@link Attachment#containerType}, {@link Attachment#containerId}가 서로 같은 첨부이다.
     *
     * @param attach 이 첨부 파일과 내용이 같은 첨부 파일을 찾는다.
     * @return 발견된 첨부 파일
     */
    private static Attachment findBy(Attachment attach) {
        return find.where()
                .eq("name", attach.name)
                .eq("hash", attach.hash)
                .eq("containerType", attach.containerType)
                .eq("containerId", attach.containerId).findUnique();
    }

    /**
     * 파일의 해시값이 {@code hash}인 첨부 파일이 존재하는지의 여부를 반환한다.
     *
     * @param hash 해시값
     * @return 해시값이 {@code hash}와 같은 첨부가 존재하는지의 여부
     */
    public static boolean exists(String hash) {
        return find.where().eq("hash", hash).findRowCount() > 0;
    }

    /**
     * 주어진 {@code containerType}과 {@code containerId}에 대응하는 리소스에 첨부된 첨부 파일의 목록을 반환한다.
     *
     * {@code containerType}은 {@link models.resource.Resource#getType()}의 반환값과,
     * {@code containerId}는 {@link models.resource.Resource#getId()}의 반환값과 비교한다.
     *
     * @param containerType 첨부 파일이 첨부된 리소스의 타입
     * @param containerId 첨부 파일이 첨부된 리소스의 아이디
     * @return 첨부 파일의 목록
     */
    public static List<Attachment> findByContainer(
            ResourceType containerType, Long containerId) {
        return find.where()
                .eq("containerType", containerType)
                .eq("containerId", containerId).findList();
    }

    /**
     * 주어진 {@code container}의 첨부된 첨부 파일의 목록을 반환한다.
     *
     * @param container 첨부 파일이 첨부된 리소스
     * @return 첨부 파일의 목록
     */
    public static List<Attachment> findByContainer(Resource container) {
        return findByContainer(container.getType(), container.getId());
    }

    /**
     * 주어진 {@code container}에 첨부된 첨부 파일의 갯수를 반환한다.
     *
     * when:
     *
     * @param container 첨부 파일이 첨부된 리소스
     * @return 첨부 파일의 목록
     */
    public static int countByContainer(Resource container) {
        return find.where()
                .eq("containerType", container.getType())
                .eq("containerId", container.getId()).findRowCount();
    }

    /**
     * {@code from}에 첨부된 모든 첨부 파일을 {@code to}로 옮긴다.
     *
     * when: 업로드 직후 일시적으로 사용자에게 첨부되었던 첨부 파일들을, 특정 리소스(이슈, 게시물 등)으로 옮기려 할 때
     *
     * @param from 첨부 파일이 원래 있었던 리소스
     * @param to 첨부 파일이 새로 옮겨질 리소스
     * @return
     */
    public static int moveAll(Resource from, Resource to) {
        List<Attachment> attachments = Attachment.findByContainer(from);
        for (Attachment attachment : attachments) {
            attachment.moveTo(to);
        }
        return attachments.size();
    }

    /**
     * 이 첨부 파일을 {@code to}로 옮긴다.
     *
     * @param to 첨부 파일이 새로 옮겨질 리소스
     * @return
     */
    public void moveTo(Resource to) {
        if(to.getProject() != null) {
            projectId = to.getProject().id;
        } else {
            projectId = null;
        }
        containerType = to.getType();
        containerId = to.getId();
        update();
    }

    /**
     * {@code file}을 업로드 디렉토리로 옮긴다.
     *
     * when: Play 프레임워크는 파일을 업로드하면 일단 임시 디렉토리에 보관하는데, 이 파일을 업로드 디렉토리로
     * 옮기려 할 때 사용한다.
     *
     * @param file
     * @return
     * @throws NoSuchAlgorithmException
     * @throws IOException
     */
    private static String moveFileIntoUploadDirectory(File file)
            throws NoSuchAlgorithmException, IOException {
        // Compute sha1 checksum.
        MessageDigest algorithm = MessageDigest.getInstance("SHA1");
        DigestInputStream dis = new DigestInputStream(
                new BufferedInputStream(new FileInputStream(file)), algorithm);
        while (dis.read() != -1);
        Formatter formatter = new Formatter();
        for (byte b : algorithm.digest()) {
            formatter.format("%02x", b);
        }
        String hash = formatter.toString();

        // Store the file.
        // Before do that, create upload directory if it doesn't exist.
        File uploads = new File(uploadDirectory);
{'fraction_non_alphanumeric': 0.1042956293378353, 'fraction_numerical': 0.004689551678859501, 'mean_word_length': 2.3711666139740752, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5255245', 'n_tokens_mistral': 4062, 'n_tokens_neox': 4543, 'n_words': 913}