<repo_name>nulab/backlog4j<file_sep>/build.gradle
buildscript {
    repositories {
        mavenCentral()
    }
}

plugins {
    id 'java-library'
    id 'maven-publish'
    id 'signing'
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

group = 'com.nulab-inc'
archivesBaseName = 'backlog4j'
version = '2.5.4-SNAPSHOT'

repositories {
    maven {
        url 'https://repo1.maven.org/maven2/'
    }
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0'
    testImplementation 'org.hamcrest:hamcrest-library:2.2'
    testImplementation 'org.exparity:hamcrest-date:2.0.8'
    testImplementation 'ch.qos.logback:logback-classic:1.2.11'

    implementation 'org.apache.commons:commons-lang3:3.12.0'
    implementation 'com.fasterxml.jackson.core:jackson-core:2.13.3'
    implementation 'com.fasterxml.jackson.core:jackson-annotations:2.13.3'
    implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.3'
    implementation 'org.slf4j:slf4j-api:1.7.36'
}

java {
    compileJava.options.encoding = 'UTF-8'
    compileTestJava.options.encoding = 'UTF-8'
    withSourcesJar()
    withJavadocJar()
}

test {
    useJUnitPlatform()
}

javadoc {
    options.locale = 'en_US'
    options.encoding = 'UTF-8'
    options.addStringOption('Xdoclint:none', '-quiet')
    title = 'Backlog4j ' + version + ' API'
}

jar {
    manifest {
        attributes('Implementation-Title': archivesBaseName,
                   'Implementation-Version': archiveVersion,
                   'Automatic-Module-Name': archiveBaseName)
    }
}

configurations {
    deployerJars
}

publishing {
    publications {
        mavenJava(MavenPublication) {
            from components.java
            pom {
                name = 'Backlog4j'
                description = 'Backlog4j is a Backlog binding library for Java.'
                url = 'https://github.com/nulab/backlog4j'
                scm {
                    connection = 'https://github.com/nulab/backlog4j.git'
                    developerConnection = 'https://github.com/nulab/backlog4j.git'
                    url = 'https://github.com/nulab/backlog4j.git'
                }
                licenses {
                    license {
                        name = 'MIT License'
                        url = 'http://www.opensource.org/licenses/mit-license.php'
                    }
                }
                developers {
                    developer {
                        id = 'uchida'
                        name = '<NAME>'
                        email = '<EMAIL>'
                    }
                }
            }
        }
    }

    repositories {
        maven {
            credentials {
                username = findProperty("ossrhUsername") ?: System.getenv("OSSRH_USERNAME")
                password = findProperty("ossrhPassword") ?: System.getenv("OSSRH_PASSWORD")
            }
            if (project.version.endsWith("-SNAPSHOT")) {
                url = "https://oss.sonatype.org/content/repositories/snapshots"
            } else {
                url = "https://oss.sonatype.org/service/local/staging/deploy/maven2"
            }
        }
    }
}

signing {
    sign publishing.publications.mavenJava
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/internal/json/InternalFactoryJSONImpl.java
package com.nulabinc.backlog4j.internal.json;


import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.nulabinc.backlog4j.*;
import com.nulabinc.backlog4j.auth.AccessToken;
import com.nulabinc.backlog4j.http.BacklogHttpResponse;
import com.nulabinc.backlog4j.internal.InternalFactory;
import com.nulabinc.backlog4j.internal.json.activities.ActivityJSONImpl;
import com.nulabinc.backlog4j.internal.json.auth.AccessTokenJSONImpl;
import com.nulabinc.backlog4j.internal.json.customFields.CustomFieldSettingJSONImpl;


/**
 * @author nulab-inc
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public class InternalFactoryJSONImpl implements InternalFactory {

    @Override
    public AccessToken createAccessToken(BacklogHttpResponse res) throws BacklogException {
        return createAccessToken(res.asString());
    }

    public AccessToken createAccessToken(String resStr) throws BacklogException {
        return createObject(resStr, AccessToken.class, AccessTokenJSONImpl.class);
    }

    @Override
    public Space createSpace(BacklogHttpResponse res) throws BacklogException {
        return createSpace(res.asString());
    }

    public Space createSpace(String resStr) throws BacklogException {
        return createObject(resStr, Space.class, SpaceJSONImpl.class);
    }

    @Override
    public SpaceNotification createSpaceNotification(BacklogHttpResponse res) throws BacklogException {
        return createSpaceNotification(res.asString());
    }

    public SpaceNotification createSpaceNotification(String resStr) throws BacklogException {
        return createObject(resStr, SpaceNotification.class, SpaceNotificationJSONImpl.class);
    }

    @Override
    public DiskUsage createDiskUsage(BacklogHttpResponse res) throws BacklogException {
        return createDiskUsage(res.asString());
    }

    public DiskUsage createDiskUsage(String resStr) throws BacklogException {
        return createObject(resStr, DiskUsage.class, DiskUsageJSONImpl.class);
    }

    @Override
    public DiskUsageDetail createDiskUsageDetail(BacklogHttpResponse res) throws BacklogException {
        return createDiskUsageDetail(res.asString());
    }

    public DiskUsageDetail createDiskUsageDetail(String resStr) throws BacklogException {
        return createObject(resStr, DiskUsageDetail.class, DiskUsageDetailJSONImpl.class);
    }

    @Override
    public ResponseList<Project> createProjectList(BacklogHttpResponse res) throws BacklogException {
        return createProjectList(res.asString());
    }

    public ResponseList<Project> createProjectList(String resStr) throws BacklogException {
        return createObjectList(resStr, Project.class, ProjectJSONImpl[].class);
    }

    @Override
    public ProjectWithVCS createProject(BacklogHttpResponse res) throws BacklogException {
        return createProject(res.asString());
    }

    public ProjectWithVCS createProject(String resStr) throws BacklogException {
        return createObject(resStr, ProjectWithVCS.class, ProjectWithVCSJSONImpl.class);
    }

    @Override
    public ResponseList<Activity> createActivityList(BacklogHttpResponse res) throws BacklogException {
        return createActivityList(res.asString());
    }

    public ResponseList<Activity> createActivityList(String resStr) throws BacklogException {
        return createObjectList(resStr, Activity.class, ActivityJSONImpl[].class);
    }

    @Override
    public Activity createActivity(BacklogHttpResponse res) throws BacklogException {
        return createActivity(res.asString());
    }

    public Activity createActivity(String resStr) throws BacklogException {
        return createObject(resStr, Activity.class, ActivityJSONImpl.class);
    }

    @Override
    public ResponseList<Issue> createIssueList(BacklogHttpResponse res) throws BacklogException {
        return createIssueList(res.asString());
    }

    public ResponseList<Issue> createIssueList(String resStr) throws BacklogException {
        return createObjectList(resStr, Issue.class, IssueJSONImpl[].class);
    }

    @Override
    public ResponseList<ViewedIssue> createViewedIssueList(BacklogHttpResponse res) throws BacklogException {
        return createViewedIssueList(res.asString());
    }

    public ResponseList<ViewedIssue> createViewedIssueList(String resStr) throws BacklogException {
        return createObjectList(resStr, ViewedIssue.class, ViewedIssueJSONImpl[].class);
    }

    @Override
    public ResponseList<ViewedProject> createViewedProjectList(BacklogHttpResponse res) throws BacklogException {
        return createViewedProjectList(res.asString());
    }

    public ResponseList<ViewedProject> createViewedProjectList(String resStr) throws BacklogException {
        return createObjectList(resStr, ViewedProject.class, ViewedProjectJSONImpl[].class);
    }

    @Override
    public ResponseList<ViewedWiki> createViewedWikiList(BacklogHttpResponse res) throws BacklogException {
        return createViewedWikiList(res.asString());
    }
    public ResponseList<ViewedWiki> createViewedWikiList(String resStr) throws BacklogException {
        return createObjectList(resStr, ViewedWiki.class, ViewedWikiJSONImpl[].class);
    }

    @Override
    public Issue createIssue(BacklogHttpResponse res) throws BacklogException {
        return createIssue(res.asString());
    }
    public Issue createIssue(String resStr) throws BacklogException {
        return createObject(resStr, Issue.class, IssueJSONImpl.class);
    }

    @Override
    public Issue importIssue(BacklogHttpResponse res) throws BacklogException {
        return importIssue(res.asString());
    }

    public Issue importIssue(String resStr) throws BacklogException {
        return createObject(resStr, Issue.class, IssueJSONImpl.class);
    }

    @Override
    public IssueComment createIssueComment(BacklogHttpResponse res) throws BacklogException {
        return createIssueComment(res.asString());
    }
    public IssueComment createIssueComment(String resStr) throws BacklogException {
        return createObject(resStr, IssueComment.class, IssueCommentJSONImpl.class);
    }

    @Override
    public ResponseList<IssueComment> createIssueCommentList(BacklogHttpResponse res) throws BacklogException {
        return createIssueCommentList(res.asString());
    }
    public ResponseList<IssueComment> createIssueCommentList(String resStr) throws BacklogException {
        return createObjectList(resStr, IssueComment.class, IssueCommentJSONImpl[].class);
    }

    @Override
    public User createUser(BacklogHttpResponse res) throws BacklogException {
        return createUser(res.asString());
    }
    public User createUser(String resStr) throws BacklogException {
        return createObject(resStr, User.class, UserJSONImpl.class);
    }

    @Override
    public ResponseList<User> createUserList(BacklogHttpResponse res) throws BacklogException {
        return createUserList(res.asString());
    }
    public ResponseList<User> createUserList(String resStr) throws BacklogException {
        return createObjectList(resStr, User.class, UserJSONImpl[].class);
    }

    @Override
    public IssueType createIssueType(BacklogHttpResponse res) throws BacklogException {
        return createObject(res.asString(), IssueType.class, IssueTypeJSONImpl.class);
    }
    public IssueType createIssueType(String resStr) throws BacklogException {
        return createObject(resStr, IssueType.class, IssueTypeJSONImpl.class);
    }

    @Override
    public ResponseList<IssueType> createIssueTypeList(BacklogHttpResponse res) throws BacklogException {
        return createObjectList(res.asString(), IssueType.class, IssueTypeJSONImpl[].class);
    }
    public ResponseList<IssueType> createIssueTypeList(String resStr) throws BacklogException {
        return createObjectList(resStr, IssueType.class, IssueTypeJSONImpl[].class);
    }

    @Override
    public Category createCategory(BacklogHttpResponse res) throws BacklogException {
        return createObject(res.asString(), Category.class, CategoryJSONImpl.class);
    }
    public Category createCategory(String resStr) throws BacklogException {
        return createObject(resStr, Category.class, CategoryJSONImpl.class);
    }

    @Override
    public ResponseList<Category> createCategoryList(BacklogHttpResponse res) throws BacklogException {
        return createObjectList(res.asString(), Category.class, CategoryJSONImpl[].class);
    }
    public ResponseList<Category> createCategoryList(String resStr) throws BacklogException {
        return createObjectList(resStr, Category.class, CategoryJSONImpl[].class);
    }

    @Override
    public CustomFieldSetting createCustomField(BacklogHttpResponse res) throws BacklogException {
        return createObject(res.asString(), CustomFieldSetting.class, CustomFieldSettingJSONImpl.class);
    }
    public CustomFieldSetting createCustomField(String resStr) throws BacklogException {
        return createObject(resStr, CustomFieldSetting.class, CustomFieldSettingJSONImpl.class);
    }

    @Override
    public ResponseList<CustomFieldSetting> createCustomFieldList(BacklogHttpResponse res) throws BacklogException {
        return createObjectList(res.asString(), CustomFieldSetting.class, CustomFieldSettingJSONImpl[].class);
    }
    public ResponseList<CustomFieldSetting> createCustomFieldList(String resStr) throws BacklogException {
        return createObjectList(resStr, CustomFieldSetting.class, CustomFieldSettingJSONImpl[].class);
    }

    @Override
    public ResponseList<Priority> createPriorityList(BacklogHttpResponse res) throws BacklogException {
        return createObjectList(res.asString(), Priority.class, PriorityJSONImpl[].class);
    }
    public ResponseList<Priority> createPriorityList(String resStr) throws BacklogException {
        return createObjectList(resStr, Priority.class, PriorityJSONImpl[].class);
    }

    @Override
    public ResponseList<Resolution> createResolutionList(BacklogHttpResponse res) throws BacklogException {
        return createObjectList(res.asString(), Resolution.class, ResolutionJSONImpl[].class);
    }
    public ResponseList<Resolution> createResolutionList(String resStr) throws BacklogException {
        return createObjectList(resStr, Resolution.class, ResolutionJSONImpl[].class);
    }

    @Override
    public ResponseList<Status> createStatusList(BacklogHttpResponse res) throws BacklogException {
        return createObjectList(res.asString(), Status.class, StatusJSONImpl[].class);
    }

    @Override
    public Status createStatus(BacklogHttpResponse res) throws BacklogException {
        return createObject(res.asString(), Status.class, StatusJSONImpl.class);
    }

    public ResponseList<Status> createStatusList(String resStr) throws BacklogException {
        return createObjectList(resStr, Status.class, StatusJSONImpl[].class);
    }

    @Override
    public Star createStar(BacklogHttpResponse res) throws BacklogException {
        return createObject(res.asString(), Star.class, StarJSONImpl.class);
    }
    public Star createStar(String resStr) throws BacklogException {
        return createObject(resStr, Star.class, StarJSONImpl.class);
    }

    @Override
    public ResponseList<Star> createStarList(BacklogHttpResponse res) throws BacklogException {
        return createObjectList(res.asString(), Star.class, StarJSONImpl[].class);
    }
    public ResponseList<Star> createStarList(String resStr) throws BacklogException {
        return createObjectList(resStr, Star.class, StarJSONImpl[].class);
    }

    @Override
    public Count createCount(BacklogHttpResponse res) throws BacklogException {
        return createObject(res.asString(), Count.class, CountJSONImpl.class);
    }
    public Count createCount(String resStr) throws BacklogException {
        return createObject(resStr, Count.class, CountJSONImpl.class);
    }

    @Override
    public Version createVersion(BacklogHttpResponse res) throws BacklogException {
        return createObject(res.asString(), Version.class, VersionJSONImpl.class);
    }
    public Version createVersion(String resStr) throws BacklogException {
        return createObject(resStr, Version.class, VersionJSONImpl.class);
    }

    @Override
    public ResponseList<Version> createVersionList(BacklogHttpResponse res) throws BacklogException {
        return createObjectList(res.asString(), Version.class, VersionJSONImpl[].class);
    }
    public ResponseList<Version> createVersionList(String resStr) throws BacklogException {
        return createObjectList(resStr, Version.class, VersionJSONImpl[].class);
    }

    @Override
    public Milestone createMilestone(BacklogHttpResponse res) throws BacklogException {
        return createObject(res.asString(), Milestone.class, MilestoneJSONImpl.class);
    }
    public Milestone createMilestone(String resStr) throws BacklogException {
        return createObject(resStr, Milestone.class, MilestoneJSONImpl.class);
    }

    @Override
    public ResponseList<Milestone> createMilestoneList(BacklogHttpResponse res) throws BacklogException {
        return createObjectList(res.asString(), Milestone.class, MilestoneJSONImpl[].class);
    }
    public ResponseList<Milestone> createMilestoneList(String resStr) throws BacklogException {
        return createObjectList(resStr, Milestone.class, MilestoneJSONImpl[].class);
    }

    @Override
    public Wiki createWiki(BacklogHttpResponse res) throws BacklogException {
        return createObject(res.asString(), Wiki.class, WikiJSONImpl.class);
    }
    public Wiki createWiki(String resStr) throws BacklogException {
        return createObject(resStr, Wiki.class, WikiJSONImpl.class);
    }

    @Override
    public Wiki importWiki(BacklogHttpResponse res) throws BacklogException {
        return createObject(res.asString(), Wiki.class, WikiJSONImpl.class);
    }
    public Wiki importWiki(String resStr) throws BacklogException {
        return createObject(resStr, Wiki.class, WikiJSONImpl.class);
    }

    @Override
    public ResponseList<Wiki> createWikiList(BacklogHttpResponse res) throws BacklogException {
        return createObjectList(res.asString(), Wiki.class, WikiJSONImpl[].class);
    }
    public ResponseList<Wiki> createWikiList(String resStr) throws BacklogException {
        return createObjectList(resStr, Wiki.class, WikiJSONImpl[].class);
    }

    @Override
    public WikiTag createWikiTag(BacklogHttpResponse res) throws BacklogException {
        return createObject(res.asString(), WikiTag.class, WikiTagJSONImpl.class);
    }
    public WikiTag createWikiTag(String resStr) throws BacklogException {
        return createObject(resStr, WikiTag.class, WikiTagJSONImpl.class);
    }

    @Override
    public ResponseList<WikiTag> createWikiTagList(BacklogHttpResponse res) throws BacklogException {
        return createObjectList(res.asString(), WikiTag.class, WikiTagJSONImpl[].class);
    }
    public ResponseList<WikiTag> createWikiTagList(String resStr) throws BacklogException {
        return createObjectList(resStr, WikiTag.class, WikiTagJSONImpl[].class);
    }

    @Override
    public WikiHistory createWikiHistory(BacklogHttpResponse res) throws BacklogException {
        return createObject(res.asString(), WikiHistory.class, WikiHistoryJSONImpl.class);
    }
    public WikiHistory createWikiHistory(String resStr) throws BacklogException {
        return createObject(resStr, WikiHistory.class, WikiHistoryJSONImpl.class);
    }

    @Override
    public ResponseList<WikiHistory> createWikiHistoryList(BacklogHttpResponse res) throws BacklogException {
        return createObjectList(res.asString(), WikiHistory.class, WikiHistoryJSONImpl[].class);
    }
    public ResponseList<WikiHistory> createWikiHistoryList(String resStr) throws BacklogException {
        return createObjectList(resStr, WikiHistory.class, WikiHistoryJSONImpl[].class);
    }

    @Override
    public ResponseList<Notification> createNotificationList(BacklogHttpResponse res) throws BacklogException {
        return createObjectList(res.asString(), Notification.class, NotificationJSONImpl[].class);
    }

    public ResponseList<Notification> createNotificationList(String resStr) throws BacklogException {
        return createObjectList(resStr, Notification.class, NotificationJSONImpl[].class);
    }

    @Override
    public Repository createRepository(BacklogHttpResponse res) throws BacklogException {
        return createRepository(res.asString());
    }

    public Repository createRepository(String resStr) throws BacklogException {
        return createObject(resStr, Repository.class, RepositoryJSONImpl.class);
    }

    @Override
    public ResponseList<Repository> createRepositoryList(BacklogHttpResponse res) throws BacklogException {
        return createObjectList(res.asString(), Repository.class, RepositoryJSONImpl[].class);
    }

    @Override
    public PullRequest createPullRequest(BacklogHttpResponse res) throws BacklogException {
        return createPullRequest(res.asString());
    }

    public PullRequest createPullRequest(String resStr) throws BacklogException {
        return createObject(resStr, PullRequest.class, PullRequestJSONImpl.class);
    }

    public ResponseList<Repository> createRepositoryList(String resStr) throws BacklogException {
        return createObjectList(resStr, Repository.class, RepositoryJSONImpl[].class);
    }

    @Override
    public ResponseList<PullRequest> createPullRequestList(BacklogHttpResponse res) throws BacklogException {
        return createObjectList(res.asString(), PullRequest.class, PullRequestJSONImpl[].class);
    }
    public ResponseList<PullRequest> createPullRequestList(String resStr) throws BacklogException {
        return createObjectList(resStr, PullRequest.class, PullRequestJSONImpl[].class);
    }

    @Override
    public PullRequestComment createPullRequestComment(BacklogHttpResponse res) throws BacklogException {
        return createPullRequestComment(res.asString());
    }

    public PullRequestComment createPullRequestComment(String resStr) throws BacklogException {
        return createObject(resStr, PullRequestComment.class, PullRequestCommentJSONImpl.class);
    }

    @Override
    public ResponseList<PullRequestComment> createPullRequestCommentList(BacklogHttpResponse res) throws BacklogException {
        return createPullRequestCommentList(res.asString());
    }
    public ResponseList<PullRequestComment> createPullRequestCommentList(String resStr) throws BacklogException {
        return createObjectList(resStr, PullRequestComment.class, PullRequestCommentJSONImpl[].class);
    }

    @Override
    public SharedFile createSharedFile(BacklogHttpResponse res) throws BacklogException {
        return createObject(res.asString(), SharedFile.class, SharedFileJSONImpl.class);
    }
    public SharedFile createSharedFile(String resStr) throws BacklogException {
        return createObject(resStr, SharedFile.class, SharedFileJSONImpl.class);
    }

    @Override
    public ResponseList<SharedFile> createSharedFileList(BacklogHttpResponse res) throws BacklogException {
        return createObjectList(res.asString(), SharedFile.class, SharedFileJSONImpl[].class);
    }
    public ResponseList<SharedFile> createSharedFileList(String resStr) throws BacklogException {
        return createObjectList(resStr, SharedFile.class, SharedFileJSONImpl[].class);
    }

    @Override
    public Attachment createAttachment(BacklogHttpResponse res) throws BacklogException {
        return createObject(res.asString(), Attachment.class, AttachmentJSONImpl.class);
    }

    public Attachment createAttachment(String resStr) throws BacklogException {
        return createObject(resStr, Attachment.class, AttachmentJSONImpl.class);
    }

    @Override
    public Group createGroup(BacklogHttpResponse res) throws BacklogException {
        return createGroup(res.asString());
    }

    public Group createGroup(String resStr) throws BacklogException {
        return createObject(resStr, Group.class, GroupJSONImpl.class);
    }

    @Override
    public ResponseList<Group> createGroupList(BacklogHttpResponse res) throws BacklogException {
        return createGroupList(res.asString());
    }

    public ResponseList<Group> createGroupList(String resStr) throws BacklogException {
        return createObjectList(resStr, Group.class, GroupJSONImpl[].class);
    }

    @Override
    public ResponseList<Attachment> createAttachmentList(BacklogHttpResponse res) throws BacklogException {
        return createObjectList(res.asString(), Attachment.class, AttachmentJSONImpl[].class);
    }
    public ResponseList<Attachment> createAttachmentList(String resStr) throws BacklogException {
        return createObjectList(resStr, Attachment.class, AttachmentJSONImpl[].class);
    }

    @Override
    public ResponseList<Webhook> createWebhookList(BacklogHttpResponse res) throws BacklogException {
        return createWebhookList(res.asString());
    }
    public ResponseList<Webhook> createWebhookList(String resStr) throws BacklogException {
        return createObjectList(resStr, Webhook.class, WebhookJSONImpl[].class);
    }

    @Override
    public Webhook createWebhook(BacklogHttpResponse res) throws BacklogException {
        return createObject(res.asString(), Webhook.class, WebhookJSONImpl.class);
    }
    public Webhook createWebhook(String resStr) throws BacklogException {
        return createObject(resStr, Webhook.class, WebhookJSONImpl.class);
    }

    @Override
    public Watch createWatch(BacklogHttpResponse res) throws BacklogException {
        return createWatch(res.asString());
    }

    @Override
    public ResponseList<Watch> createWatchList(BacklogHttpResponse res) throws BacklogException {
        return createObjectList(res.asString(), Watch.class, WatchJSONImpl[].class);
    }

    public Watch createWatch(String resStr) throws BacklogException {
        return createObject(resStr, Watch.class, WatchJSONImpl.class);
    }

    private <T1 , T2 extends T1> ResponseList<T1> createObjectList(String content, Class<T1> clazz1, Class<T2[]> clazz2) throws BacklogException {
        T1[] arrays = Jackson.fromJsonString(content, clazz2);
        ResponseList<T1> list = new ResponseListImpl<T1>();
        for (int i = 0; i < arrays.length; i++) {
            list.add(arrays[i]);
        }
        return list;
    }

    private <T1 , T2 extends T1> T1 createObject(String content, Class<T1> clazz1, Class<T2> clazz2) throws BacklogException {
        T1 obj = Jackson.fromJsonString(content, clazz2);
        return obj;
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/internal/json/ChangeJSONImpl.java
package com.nulabinc.backlog4j.internal.json;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.nulabinc.backlog4j.Change;
import com.nulabinc.backlog4j.User;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

/**
 * @author nulab-inc
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public class ChangeJSONImpl implements Change {

    private String field;
    @JsonProperty("new_value")
    private String newValue;
    @JsonProperty("old_value")
    private String oldValue;
    private String type;

    @Override
    public String getField() {
        return field;
    }

    @Override
    public String getNewValue() {
        return newValue;
    }

    @Override
    public String getOldValue() {
        return oldValue;
    }

    @Override
    public String getType() {
        return type;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        if (obj.getClass() != getClass()) {
            return false;
        }
        ChangeJSONImpl rhs = (ChangeJSONImpl) obj;
        return new EqualsBuilder()
                .append(this.field, rhs.field)
                .append(this.newValue, rhs.newValue)
                .append(this.oldValue, rhs.oldValue)
                .append(this.type, rhs.type)
                .isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder()
                .append(field)
                .append(newValue)
                .append(oldValue)
                .append(type)
                .toHashCode();
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("field", field)
                .append("newValue", newValue)
                .append("oldValue", oldValue)
                .append("type", type)
                .toString();
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/BacklogAPIError.java
package com.nulabinc.backlog4j;

import java.util.Arrays;
import java.util.List;

/**
 * The error class for Backlog exception.
 * Contains the error massages.
 *
 * @author nulab-inc
 */
public class BacklogAPIError {

    private BacklogAPIErrorMessage[] errors;

    public List<BacklogAPIErrorMessage> getErrors() {
        return Arrays.asList(errors);
    }

}
<file_sep>/src/test/java/com/nulabinc/backlog4j/internal/json/CategoryJSONImplTest.java
package com.nulabinc.backlog4j.internal.json;

import com.nulabinc.backlog4j.Category;
import com.nulabinc.backlog4j.ResponseList;
import org.junit.jupiter.api.Test;

import java.io.IOException;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
 * @author nulab-inc
 */
public class CategoryJSONImplTest extends AbstractJSONImplTest {
    @Test
    public void createCategoryListTest() throws IOException {
        String fileContentStr = getJsonString("json/categories.json");
        ResponseList<Category> versions = factory.createCategoryList(fileContentStr);

        assertEquals(4, versions.size());

        Category version = versions.get(0);
        assertEquals(1073967643, version.getId());
        assertEquals("検討", version.getName());

        version = versions.get(1);
        assertEquals(1073967645, version.getId());
        assertEquals("障害", version.getName());

        version = versions.get(2);
        assertEquals(1073967646, version.getId());
        assertEquals("懸念", version.getName());

        version = versions.get(3);
        assertEquals(1073967647, version.getId());
        assertEquals("企画", version.getName());

    }

    @Test
    public void createCategoryTest() throws IOException {
        String fileContentStr = getJsonString("json/category.json");
        Category category = factory.createCategory(fileContentStr);

        assertEquals(1073967641, category.getId());
        assertEquals("検討", category.getName());
    }

    @Test
    public void equalsTest() throws IOException {
        String fileContentStr = getJsonString("json/category.json");
        Category category1 = factory.createCategory(fileContentStr);
        Category category2 = factory.createCategory(fileContentStr);
        assertEquals(category1, category2);

    }

    @Test
    public void hashCodeTest() throws IOException {
        String fileContentStr = getJsonString("json/category.json");
        Category category1 = factory.createCategory(fileContentStr);
        Category category2 = factory.createCategory(fileContentStr);
        assertEquals(category1.hashCode(), category2.hashCode());

    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/WebhookMethods.java
package com.nulabinc.backlog4j.api;

import com.nulabinc.backlog4j.BacklogException;
import com.nulabinc.backlog4j.ResponseList;
import com.nulabinc.backlog4j.Webhook;
import com.nulabinc.backlog4j.api.option.CreateWebhookParams;
import com.nulabinc.backlog4j.api.option.UpdateWebhookParams;

/**
 * Executes Backlog Webhook APIs.
 *
 * @author nulab-inc
 */
public interface WebhookMethods {

    /**
     * Returns all the webhooks.
     *
     * @param projectIdOrKey the project key
     * @return the webhooks in a list.
     * @throws BacklogException
     */
    ResponseList<Webhook> getWebhooks(Object projectIdOrKey) throws BacklogException;

    /**
     * Create a webhook.
     *
     * @param params the creating webhook parameters.
     * @return the created webhook
     * @throws com.nulabinc.backlog4j.BacklogException
     */
    Webhook createWebhook(CreateWebhookParams params) throws BacklogException;

    /**
     * Returns the webhook.
     *
     * @param projectIdOrKey the project identifier
     * @param webhookId the webhook identifier
     * @return the Webhook
     * @throws com.nulabinc.backlog4j.BacklogException
     */
    Webhook getWebhook(Object projectIdOrKey, Object webhookId) throws BacklogException;

    /**
     * Updates the existing webhook.
     *
     * @param params the updating webhook parameters
     * @return the updated Webhook
     * @throws com.nulabinc.backlog4j.BacklogException
     */
    Webhook updateWebhook(UpdateWebhookParams params) throws BacklogException;

    /**
     * Deletes the existing webhook.
     *
     * @param projectIdOrKey the project key
     * @param webhookId the webhook identifier
     * @return the deleted webhook
     * @throws com.nulabinc.backlog4j.BacklogException
     */
    Webhook deleteWebhook(Object projectIdOrKey, Object webhookId) throws BacklogException;
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/AddIssueCommentNotificationParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.BacklogAPIException;
import com.nulabinc.backlog4j.http.NameValuePair;

import java.util.List;

/**
 * Parameters for add category API.
 *
 * @author nulab-inc
 */
public class AddIssueCommentNotificationParams extends PostParams {

    private Object issueIdOrKey;
    private Object commentId;

    /**
     * Constructor
     *
     * @param issueIdOrKey           the issue identifier
     * @param commentId         the comment identifier
     * @param notifiedUserIds   the user identifiers for notification
     */
    public AddIssueCommentNotificationParams(Object issueIdOrKey, Object commentId, List notifiedUserIds) {
        this.issueIdOrKey = issueIdOrKey;
        this.commentId = commentId;
        for (Object userId : notifiedUserIds) {
            parameters.add(new NameValuePair("notifiedUserId[]", String.valueOf(userId)));
        }
    }

    /**
     * Returns the comment identifier.
     *
     * @return comment id
     */
    public String getCommentId() {
        return commentId.toString();
    }

    /**
     * Returns the issue identifier string.
     *
     * @return issue id or project key
     */
    public String getIssueIdOrKeyString() {
        return issueIdOrKey.toString();
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/Version.java
package com.nulabinc.backlog4j;

import java.util.Date;

/**
 * The interface for Backlog version data.
 *
 * @author nulab-inc
 */
public interface Version {

    long getId();

    String getIdAsString();

    long getProjectId();

    String getProjectIdAsString();

    String getName();

    String getDescription();

    Date getStartDate();

    Date getReleaseDueDate();

    Boolean getArchived();
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/internal/json/activities/NotificationAddedContent.java
package com.nulabinc.backlog4j.internal.json.activities;

/**
 * @author nulab-inc
 */
public class NotificationAddedContent extends IssueCommentedContent {


}
<file_sep>/src/test/java/com/nulabinc/backlog4j/api/option/AbstractParamsTest.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.http.NameValuePair;

import java.util.List;

/**
 * @author nulab-inc
 */
public abstract class AbstractParamsTest {

    protected boolean existsOneKeyValue(List<NameValuePair> parameters,
                                        String key, String value) {
        int count = 0;
        for (NameValuePair pair : parameters) {
            if (pair.getName().equals(key)
                    && pair.getValue().equals(value))
                count++;
        }
        return count == 1;
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/SharedFile.java
package com.nulabinc.backlog4j;

import java.util.Date;

/**
 * The interface for Backlog shared file.
 *
 * @author nulab-inc
 */
public interface SharedFile {

    long getId();

    String getIdAsString();

    String getType();

    String getName();

    String getDir();

    long getSize();

    User getCreatedUser();

    Date getCreated();

    User getUpdatedUser();

    Date getUpdated();

    boolean isImage();

}
<file_sep>/src/main/java/com/nulabinc/backlog4j/internal/json/customFields/DateCustomFieldSetting.java
package com.nulabinc.backlog4j.internal.json.customFields;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.nulabinc.backlog4j.CustomFieldListItemSetting;
import com.nulabinc.backlog4j.internal.json.JacksonCustomDateDeserializer;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

import java.util.List;

/**
 * @author nulab-inc
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public class DateCustomFieldSetting extends CustomFieldSettingJSONImpl {

    public enum InitialValueType {
        Today(1), TodayPlusShiftDays(2), FixedDate(3);

        InitialValueType(int intValue) {
            this.intValue = intValue;
        }

        public int getIntValue() {
            return intValue;
        }

        public static InitialValueType valueOf(final int anIntValue) {
            for (InitialValueType d : values()) {
                if (d.getIntValue() == anIntValue) {
                    return d;
                }
            }
            return null;
        }

        private int intValue;
    }

    private int typeId = 4;
    private long[] applicableIssueTypes;
    @JsonDeserialize(using = JacksonCustomDateDeserializer.class)
    private java.util.Date min;
    @JsonDeserialize(using = JacksonCustomDateDeserializer.class)
    private java.util.Date max;
    private DateValueSetting initialDate;

    @Override
    public int getFieldTypeId() {
        return typeId;
    }

    @Override
    public FieldType getFieldType() {
        return FieldType.valueOf(typeId);
    }

    @Override
    public long[] getApplicableIssueTypes() {
        return applicableIssueTypes;
    }

    @Override
    public List<? extends CustomFieldListItemSetting> getItems() {
        return null;
    }

    public java.util.Date getMin() {
        return min;
    }

    public java.util.Date getMax() {
        return max;
    }

    public DateValueSetting getInitialDate() {
        return initialDate;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        if (obj.getClass() != getClass()) {
            return false;
        }
        DateCustomFieldSetting rhs = (DateCustomFieldSetting) obj;
        return new EqualsBuilder()
                .append(getId(), rhs.getId())
                .append(getName(), rhs.getName())
                .append(this.typeId, rhs.typeId)
                .append(this.applicableIssueTypes, rhs.applicableIssueTypes)
                .append(this.min, rhs.min)
                .append(this.max, rhs.max)
                .append(this.initialDate, rhs.initialDate)
                .isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder()
                .append(getId())
                .append(getName())
                .append(typeId)
                .append(applicableIssueTypes)
                .append(min)
                .append(max)
                .append(initialDate)
                .toHashCode();
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("id", getId())
                .append("name", getName())
                .append("typeId", typeId)
                .append("applicableIssueTypes", applicableIssueTypes)
                .append("min", min)
                .append("max", max)
                .append("initialDate", initialDate)
                .toString();
    }


}
<file_sep>/src/main/java/com/nulabinc/backlog4j/Status.java
package com.nulabinc.backlog4j;

/**
 * The interface for Backlog status data.
 *
 * @author nulab-inc
 */
public interface Status {

    int getId();

    String getIdAsString();

    String getName();

    Issue.StatusType getStatusType();

    Project.CustomStatusColor getColor();

    long getProjectId();

    String getProjectIdAsString();

    int getDisplayOrder();
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/internal/json/UserJSONImpl.java
package com.nulabinc.backlog4j.internal.json;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.nulabinc.backlog4j.User;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

import java.util.Date;

/**
 * @author nulab-inc
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public class UserJSONImpl implements User {

    private long id;
    private String name;
    private String userId;
    private int roleType;
    private String lang;
    private String mailAddress;
    private Date lastLoginTime;

    @Override
    public long getId() {
        return this.id;
    }

    @Override
    public String getIdAsString() {
        return String.valueOf(this.id);
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public RoleType getRoleType() {
        return RoleType.valueOf(roleType);
    }

    @Override
    public String getLang() {
        return this.lang;
    }

    @Override
    public String getMailAddress() {
        return this.mailAddress;
    }

    @Override
    public Date getLastLoginTime() {
        return this.lastLoginTime;
    }

    @Override
    public String getUserId() {
        return userId;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        if (obj.getClass() != getClass()) {
            return false;
        }
        UserJSONImpl rhs = (UserJSONImpl) obj;
        return new EqualsBuilder()
                .append(this.id, rhs.id)
                .append(this.name, rhs.name)
                .append(this.userId, rhs.userId)
                .append(this.roleType, rhs.roleType)
                .append(this.lang, rhs.lang)
                .append(this.mailAddress, rhs.mailAddress)
                .append(this.lastLoginTime, rhs.lastLoginTime)
                .isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder()
                .append(id)
                .append(name)
                .append(userId)
                .append(roleType)
                .append(lang)
                .append(mailAddress)
                .append(lastLoginTime)
                .toHashCode();
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("id", id)
                .append("name", name)
                .append("userId", userId)
                .append("roleType", roleType)
                .append("lang", lang)
                .append("mailAddress", mailAddress)
                .append("lastLoginTime", lastLoginTime)
                .toString();
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/BacklogAPIException.java
package com.nulabinc.backlog4j;

import com.nulabinc.backlog4j.http.BacklogHttpResponse;
import com.nulabinc.backlog4j.internal.json.Jackson;

/**
 * Exception thrown when a api response contains error.
 *
 * @author nulab-inc
 */
public class BacklogAPIException extends BacklogException {

    public enum ErrorType {
        Undefined(-1),
        InternalError(1), LicenceError(2), LicenceExpiredError(3), AccessDeniedError(4),
        UnauthorizedOperationError(5), NoResourceError(6), InvalidRequestError(7),
        SpaceOverCapacityError(8), ResourceOverflowError(9), TooLargeFileError(10),
        AuthenticationError(11);

        ErrorType(int intValue) {
            this.intValue = intValue;
        }

        public int getIntValue() {
            return intValue;
        }

        public static ErrorType valueOf(final int anIntValue) {
            for (ErrorType d : values()) {
                if (d.getIntValue() == anIntValue) {
                    return d;
                }
            }
            return Undefined;
        }

        private int intValue;
    }

    protected BacklogHttpResponse response;
    private int statusCode = -1;
    private BacklogAPIError backlogAPIError;

    public BacklogAPIException(String message, Throwable cause) {
        super(message, cause);
        decode(message);
    }

    public BacklogAPIException(String message, Exception cause, int statusCode) {
        this(message, cause);
        decode(message);
        this.statusCode = statusCode;
    }

    public BacklogAPIException(String message, BacklogHttpResponse response) {
        this(message);
        decode(response.asString());
        this.response = response;
        this.statusCode = response.getStatusCode();
    }

    public BacklogAPIException(String s) {
        super(s);
    }

    public BacklogAPIException(Throwable throwable) {
        super(throwable);
    }

    @Override
    public String getMessage() {
        StringBuilder value = new StringBuilder();
        value.append(super.getMessage());
        if(statusCode > 0){
            value.append("\n");
            value.append("status code - ").append(statusCode);
        }
        if (backlogAPIError != null) {
            for (BacklogAPIErrorMessage errorMessage: backlogAPIError.getErrors()) {

                value.append("\n");
                value.append("message - ").append(errorMessage.getMessage()).append("\n");
                value.append("code - ").append(errorMessage.getCode()).append("\n");
                String info = errorMessage.getErrorInfo();
                if (info != null && info.length() > 0) {
                    value.append("errorInfo - ").append(info).append("\n");
                }
                String moreInfo = errorMessage.getMoreInfo();
                if (moreInfo != null && moreInfo.length() > 0) {
                    value.append("moreInfo - ").append(moreInfo).append("\n");
                }
            }
        }
        return value.toString();
    }

    public BacklogAPIError getBacklogAPIError() {
        return backlogAPIError;
    }

    @Override
    public int getStatusCode() {
        return this.statusCode;
    }

    protected void decode(String str) {
        if (str != null && str.startsWith("{")) {
            backlogAPIError = Jackson.fromJsonString(str, BacklogAPIError.class);
        }
    }

    public ErrorType getErrorType(){
        if (backlogAPIError != null) {
            for (BacklogAPIErrorMessage errorMessage: backlogAPIError.getErrors()) {
                return ErrorType.valueOf(errorMessage.getCode());
            }
        }
        return null;
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/Icon.java
package com.nulabinc.backlog4j;

import java.io.InputStream;

/**
 * The interface for Backlog icon file data.
 *
 * @author nulab-inc
 */
public interface Icon {
    String getFilename();

    InputStream getContent();
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/internal/package-info.java
/**
 * Implementation classes for internal usage.
 */
package com.nulabinc.backlog4j.internal;<file_sep>/src/main/java/com/nulabinc/backlog4j/NotificationInfo.java
package com.nulabinc.backlog4j;

/**
 * The interface for Backlog notificationInfo data.
 *
 * @author nulab-inc
 */
public interface NotificationInfo {

    String getType();
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/QueryParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.http.NameValuePair;

/**
 * Parameters for query.
 *
 * @author nulab-inc
 */
public class QueryParams extends GetParams{

    public enum Order {
        Asc("asc"),
        Desc("desc");

        Order(String value) {
            this.value = value;
        }

        public String getStrValue() {
            return value;
        }

        public static Order strValueOf(final String anValue) {
            for (Order d : values()) {
                if (d.getStrValue().equals(anValue)) {
                    return d;
                }
            }
            return null;
        }

        private String value;
    }

    public QueryParams minId(Object minId) {
        parameters.add(new NameValuePair("minId", minId.toString()));
        return this;
    }

    public QueryParams maxId(Object maxId) {
        parameters.add(new NameValuePair("maxId", maxId.toString()));
        return this;
    }

    public QueryParams count(int count) {
        parameters.add(new NameValuePair("count", String.valueOf(count)));
        return this;
    }

    public QueryParams count(long count) {
        parameters.add(new NameValuePair("count", String.valueOf(count)));
        return this;
    }

    public QueryParams order(Order order) {
        parameters.add(new NameValuePair("order", order.getStrValue()));
        return this;
    }

}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/AddTextCustomFieldParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.CustomFieldSetting;

/**
 * Parameters for add text type custom field API.
 *
 * @author nulab-inc
 */
public class AddTextCustomFieldParams extends AddCustomFieldParams {

    public AddTextCustomFieldParams(Object projectIdOrKey, String name) {
        super(projectIdOrKey, CustomFieldSetting.FieldType.Text, name);
    }
}
<file_sep>/README.md
# Backlog4j ![Build Status](https://github.com/nulab/backlog4j/actions/workflows/build.yml/badge.svg?branch=master)

Backlog4j is a Backlog binding library for Java.
(英語の下に日本文が記載されています)

<img src="https://raw.githubusercontent.com/nulab/backlog4j/master/Backlog_logo.png" width='200'>


* Backlog
    * [https://backlog.com/](https://backlog.com/)
    * [https://backlog.com/ja/](https://backlog.com/ja/)

* Backlog API version 2
    * [https://developer.nulab.com/docs/backlog/#](https://developer.nulab.com/docs/backlog/#)
    * [https://developer.nulab.com/ja/docs/backlog/#](https://developer.nulab.com/ja/docs/backlog/#)


## Updates
* 2023/07/27 2.5.3 released
* 2022/09/15 2.5.2 released
* 2022/08/24 2.5.1 released
* 2022/04/27 2.5.0 released
* 2021/06/10 2.4.4 released

https://github.com/nulab/backlog4j/releases

## How to Install

### gradle

    'com.nulab-inc:backlog4j:2.5.3'

### maven

    <dependency>
      <groupId>com.nulab-inc</groupId>
      <artifactId>backlog4j</artifactId>
      <version>2.5.3</version>
    </dependency>

## How to use
Make BacklogConfigure with your space id and your api key.

    # If your space is in backlog.com
    BacklogConfigure configure = new BacklogComConfigure("yourSpaceId").apiKey("yourApiKey");

    # If your space is in backlogtool.com
    BacklogConfigure configure = new BacklogToolConfigure("yourSpaceId").apiKey("yourApiKey");

    # If your space is in backlog.jp
    BacklogConfigure configure = new BacklogJpConfigure("yourSpaceId").apiKey("yourApiKey");

And get the BacklogClient.

    BacklogClient backlog = new BacklogClientFactory(configure).newClient();


Then call Backlog API method. Enjoy Backlog API!

    Project project = backlog.getProject("PROJECT-KEY");



## Documents

* javadoc
    * http://nulab.github.io/backlog4j/javadoc/

## License

MIT License

* http://www.opensource.org/licenses/mit-license.php

## Requires
* Java 1.8


# Backlog4j とは
Backlog4j は Backlog API (https://developer.nulab.com/ja/docs/backlog/#) に簡単にアクセスするためのJavaクライアントライブラリです。

## インストール

### gradle を利用する場合

    'com.nulab-inc:backlog4j:2.5.3'

### maven を利用する場合

    <dependency>
      <groupId>com.nulab-inc</groupId>
      <artifactId>backlog4j</artifactId>
      <version>2.5.3</version>
    </dependency>

## 使い方

基本的な使い方は以下の 2 ステップとなります。

* BacklogConfigure を設定して BacklogClient オブジェクトを生成
* Backlog API に従ったメソッド呼び出し

以下の例では スペースIDとAPIキーを用いて BacklogConfigure を設定しています。

    # あなたのスペースが backlog.com 内にある場合
    BacklogConfigure configure = new BacklogComConfigure("yourSpaceId").apiKey("yourApiKey");

    # あなたのスペースが backlog.jp 内にある場合
    BacklogConfigure configure = new BacklogJpConfigure("yourSpaceId").apiKey("yourApiKey");

    # あなたのスペースが backlogtool.com 内にある場合
    BacklogConfigure configure = new BacklogToolConfigure("yourSpaceId").apiKey("yourApiKey");

続いて BacklogClient を生成します。

    BacklogClient backlog = new BacklogClientFactory(configure).newClient();

そして、取得したいプロジェクトのキーを指定し、プロジェクト情報をAPI経由で取得します。

    Project project = backlog.getProject("PROJECT-KEY");


## Android でご使用の場合
proguard-rules に以下を追記して難読化の対象外とすることを推奨します。

    #Backlog4j
    -keep public class com.nulabinc.backlog4j.** {*;}

    #Jackson(Backlog4j is using jackson)
    -keepattributes *Annotation*,EnclosingMethod, Signature, InnerClasses
    -keepnames class com.fasterxml.jackson.** { *; }
    -dontwarn com.fasterxml.jackson.databind.**
<file_sep>/src/main/java/com/nulabinc/backlog4j/DiskUsageDetail.java
package com.nulabinc.backlog4j;

/**
 * The interface for Backlog disk usage data.
 *
 * @author nulab-inc
 */
public interface DiskUsageDetail {
    long getProjectId();

    long getIssue();

    long getWiki();

    long getFile();

    long getSubversion();

    long getGit();

}
<file_sep>/src/main/java/com/nulabinc/backlog4j/PullRequestComment.java
package com.nulabinc.backlog4j;

import java.util.Date;
import java.util.List;

/**
 * Created by yuhkim on 2015/05/18.
 */
public interface PullRequestComment {

    long getId();

    String getIdAsString();

    String getContent();

    List<ChangeLog> getChangeLog();

    User getCreatedUser();

    Date getCreated();

    Date getUpdated();

    List<Star> getStars();

    List<Notification> getNotifications();

}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/AddSingleListCustomFieldParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.CustomFieldSetting;
import com.nulabinc.backlog4j.http.NameValuePair;

import java.util.List;

/**
 * Parameters for add single list type custom field API.
 *
 * @author nulab-inc
 */
public class AddSingleListCustomFieldParams extends AddCustomFieldParams {

    public AddSingleListCustomFieldParams(Object projectIdOrKey, String name) {
        super(projectIdOrKey, CustomFieldSetting.FieldType.SingleList, name);
    }

    public AddSingleListCustomFieldParams items(List<String> items) {
        for (String item : items) {
            parameters.add(new NameValuePair("items[]", item));
        }
        return this;
    }

    public AddSingleListCustomFieldParams allowInput(boolean allowInput) {
        parameters.add(new NameValuePair("allowInput", String.valueOf(allowInput)));
        return this;
    }

    public AddSingleListCustomFieldParams allowAddItem(boolean allowAddItem) {
        parameters.add(new NameValuePair("allowAddItem", String.valueOf(allowAddItem)));
        return this;
    }

    @Override
    public AddSingleListCustomFieldParams applicableIssueTypes(List<Long> applicableIssueTypes) {
        return (AddSingleListCustomFieldParams)super.applicableIssueTypes(applicableIssueTypes);
    }

    @Override
    public AddSingleListCustomFieldParams description(String description) {
        return (AddSingleListCustomFieldParams)super.description(description);
    }

    @Override
    public AddSingleListCustomFieldParams required(boolean required) {
        return (AddSingleListCustomFieldParams)super.required(required);
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/AddStatusParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.Project;
import com.nulabinc.backlog4j.http.NameValuePair;

/**
 * Parameters for add status API.
 *
 * @author nulab-inc
 */
public class AddStatusParams extends PostParams {

    private Object projectIdOrKey;

    public AddStatusParams(Object projectIdOrKey, String name, Project.CustomStatusColor color) {
        this.projectIdOrKey = projectIdOrKey;
        parameters.add(new NameValuePair("name", name));
        parameters.add(new NameValuePair("color", color.getStrValue()));
    }


    public String getProjectIdOrKeyString() {
        return projectIdOrKey.toString();
    }

}
<file_sep>/src/test/java/com/nulabinc/backlog4j/internal/json/IssueCommentJSONImplTest.java
package com.nulabinc.backlog4j.internal.json;

import com.nulabinc.backlog4j.AttachmentInfo;
import com.nulabinc.backlog4j.ChangeLog;
import com.nulabinc.backlog4j.IssueComment;
import com.nulabinc.backlog4j.ResponseList;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
 * @author nulab-inc
 */
public class IssueCommentJSONImplTest extends AbstractJSONImplTest {
    @Test
    public void createIssueCommentListTest() throws IOException {
        String fileContentStr = getJsonString("json/issue_comments.json");
        ResponseList<IssueComment> comments = factory.createIssueCommentList(fileContentStr);

        assertEquals(2, comments.size());

        IssueComment comment = comments.get(0);
        assertEquals(1102837675, comment.getId());
        List<ChangeLog> logs = comment.getChangeLog();
        ChangeLog log = logs.get(0);
        assertEquals("*銀行振込：30件", log.getNewValue());
        AttachmentInfo attachmentInfo = log.getAttachmentInfo();
        assertEquals(1123344555, attachmentInfo.getId());


        comment = comments.get(1);
        assertEquals(1102836944, comment.getId());

    }

    @Test
    public void createIssueCommentTest() throws IOException {
        String fileContentStr = getJsonString("json/issue_comment.json");
        IssueComment issueType = factory.createIssueComment(fileContentStr);

        assertEquals(1102837675, issueType.getId());
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/internal/json/customFields/ListItemSetting.java
package com.nulabinc.backlog4j.internal.json.customFields;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.nulabinc.backlog4j.CustomFieldListItemSetting;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

/**
 * @author nulab-inc
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public class ListItemSetting implements CustomFieldListItemSetting {

    private long id;
    private String name;
    private Boolean allowInput;
    private int displayOrder;

    public long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public Boolean getAllowInput() {
        return allowInput;
    }

    public int getDisplayOrder() {
        return displayOrder;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        if (obj.getClass() != getClass()) {
            return false;
        }
        ListItemSetting rhs = (ListItemSetting) obj;
        return new EqualsBuilder()
                .append(this.id, rhs.id)
                .append(this.name, rhs.name)
                .append(this.allowInput, rhs.allowInput)
                .append(this.displayOrder, rhs.displayOrder)
                .isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder()
                .append(id)
                .append(name)
                .append(allowInput)
                .append(displayOrder)
                .toHashCode();
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("id", id)
                .append("name", name)
                .append("allowInput", allowInput)
                .append("displayOrder", displayOrder)
                .toString();
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/internal/json/customFields/CustomFieldSettingJSONImpl.java
package com.nulabinc.backlog4j.internal.json.customFields;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.nulabinc.backlog4j.CustomField;
import com.nulabinc.backlog4j.CustomFieldSetting;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

/**
 * @author nulab-inc
 */
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property = "typeId")
@JsonSubTypes({
        @JsonSubTypes.Type(value = TextCustomFieldSetting.class, name = "1"),
        @JsonSubTypes.Type(value = TextAreaCustomFieldSetting.class, name = "2"),
        @JsonSubTypes.Type(value = NumericCustomFieldSetting.class, name = "3"),
        @JsonSubTypes.Type(value = DateCustomFieldSetting.class, name = "4"),
        @JsonSubTypes.Type(value = SingleListCustomFieldSetting.class, name = "5"),
        @JsonSubTypes.Type(value = MultipleListCustomFieldSetting.class, name = "6"),
        @JsonSubTypes.Type(value = CheckBoxCustomFieldSetting.class, name = "7"),
        @JsonSubTypes.Type(value = RadioCustomFieldSetting.class, name = "8") })
public abstract class CustomFieldSettingJSONImpl implements CustomFieldSetting {

    private long id;
    private int typeId;
    private String name;
    private long[] applicableIssueTypes;
    private String description;
    private Boolean required;

    @Override
    public long getId() {
        return id;
    }

    @Override
    public String getIdAsString() {
        return String.valueOf(this.id);
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public long[] getApplicableIssueTypes() {
        return applicableIssueTypes;
    }

    @Override
    public String getDescription() {
        return description;
    }

    @Override
    public boolean isRequired() {
        return required;
    }



    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("id", id)
                .append("typeId", typeId)
                .append("name", name)
                .append("applicableIssueTypes", applicableIssueTypes)
                .append("description", description)
                .append("required", required)
                .toString();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        if (obj.getClass() != getClass()) {
            return false;
        }
        CustomFieldSettingJSONImpl rhs = (CustomFieldSettingJSONImpl) obj;
        return new EqualsBuilder()
                .append(this.id, rhs.id)
                .append(this.typeId, rhs.typeId)
                .append(this.name, rhs.name)
                .append(this.applicableIssueTypes, rhs.applicableIssueTypes)
                .append(this.description, rhs.description)
                .append(this.required, rhs.required)
                .isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder()
                .append(id)
                .append(typeId)
                .append(name)
                .append(applicableIssueTypes)
                .append(description)
                .append(required)
                .toHashCode();
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/ViewedWiki.java
package com.nulabinc.backlog4j;

import java.util.Date;

/**
 * The interface for Backlog viewed wiki data.
 *
 * @author nulab-inc
 */
public interface ViewedWiki {
    Wiki getPage();

    Date getUpdated();
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/UpdateWikiParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.http.NameValuePair;

/**
 * Parameters for update wiki page API.
 *
 * @author nulab-inc
 */
public class UpdateWikiParams extends PatchParams {

    private Object wikiId;

    public UpdateWikiParams(Object wikiId) {
        this.wikiId = wikiId;
    }

    public String getWikiId() {
        return wikiId.toString();
    }

    public UpdateWikiParams name(String name) {
        parameters.add(new NameValuePair("name", name));
        return this;
    }

    public UpdateWikiParams content(String content) {
        parameters.add(new NameValuePair("content", content));
        return this;
    }

    public UpdateWikiParams mailNotify(boolean mailNotify) {
        parameters.add(new NameValuePair("mailNotify", String.valueOf(mailNotify)));
        return this;
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/AddIssueCommentParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.BacklogAPIException;
import com.nulabinc.backlog4j.http.NameValuePair;

import java.util.List;

/**
 * Parameters for add issue comment API.
 *
 * @author nulab-inc
 */
public class AddIssueCommentParams extends PostParams {

    private Object issueIdOrKey;

    /**
     * Constructor
     *
     * @param issueIdOrKey the issue identifier
     * @param content the comment content
     */
    public AddIssueCommentParams(Object issueIdOrKey, String content) {
        this.issueIdOrKey = issueIdOrKey;
        parameters.add(new NameValuePair("content", content));
    }

    /**
     * Returns the issue identifier string.
     *
     * @return issue id or issue key
     */
    public String getIssueIdOrKeyString() {
        return issueIdOrKey.toString();
    }

    /**
     * Sets the notified users.
     *
     * @param notifiedUserIds the notified user identifiers
     * @return AddIssueCommentParams instance
     */
    public AddIssueCommentParams notifiedUserIds(List notifiedUserIds) {
        for (Object notifiedUserId : notifiedUserIds) {
            parameters.add(new NameValuePair("notifiedUserId[]", notifiedUserId.toString()));
        }
        return this;
    }

    /**
     * Sets the attachment files.
     *
     * @param attachmentIds the notified file identifiers
     * @return AddIssueCommentParams instance
     */
    public AddIssueCommentParams attachmentIds(List attachmentIds) {
        for (Object attachmentId : attachmentIds) {
            parameters.add(new NameValuePair("attachmentId[]", attachmentId.toString()));
        }
        return this;
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/GroupProjectActivity.java
package com.nulabinc.backlog4j;

/**
 * The interface for Backlog group project activity data.
 *
 * @author nulab-inc
 */
public interface GroupProjectActivity {

    long getId();

    String getIdAsString();

    Activity.Type getType();

}
<file_sep>/src/main/java/com/nulabinc/backlog4j/http/BacklogHttpResponse.java
package com.nulabinc.backlog4j.http;

import java.io.InputStream;
import java.util.Date;

/**
 * @author nulab-inc
 */
public interface BacklogHttpResponse {
    int getStatusCode();

    int getRateLimitLimit();

    int getRateLimitRemaining();

    Date getRateLimitResetDate();

    String getRateLimitReset();

    InputStream asInputStream();

    String asString();

    String getFileNameFromContentDisposition();

}
<file_sep>/src/main/java/com/nulabinc/backlog4j/AttachmentInfo.java
package com.nulabinc.backlog4j;

/**
 * The interface for Backlog attachment file.
 *
 * @author nulab-inc
 */
public interface AttachmentInfo {

    long getId();

    String getIdAsString();

    String getName();

    boolean isImage();
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/internal/http/MimeHelper.java
package com.nulabinc.backlog4j.internal.http;

import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

public final class MimeHelper {

    private static final String DISPOSITION_FILENAME = "filename";

    private static final String MIME_SPECIALS = "()<>@,;:\\\"/[]?=" + "\t ";

    private static final String WHITE = " \t\n\r";

    private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();

    private static final byte[] HEX_DECODE = new byte[0x80];

    static {
        for (int i = 0; i < HEX_DIGITS.length; i++) {
            HEX_DECODE[HEX_DIGITS[i]] = (byte) i;
            HEX_DECODE[Character.toLowerCase(HEX_DIGITS[i])] = (byte) i;
        }
    }

    private MimeHelper() {
    }

    /**
     * Decodes a filename from the Content-Disposition header value according to
     * RFC 2183 and RFC 2231.
     * <p/>
     * See <a href="http://tools.ietf.org/html/rfc2231">RFC 2231</a> for
     * details.
     *
     * @param value the header value to decode
     * @return the filename
     */
    public static String decodeContentDispositionFilename(String value) {
        Map<String, String> params = new HashMap<String, String>();
        decodeContentDisposition(value, params);
        return params.get(DISPOSITION_FILENAME);
    }

    /**
     * Decodes the Content-Disposition header value according to RFC 2183 and
     * RFC 2231.
     * <p/>
     * Does not deal with continuation lines.
     * <p/>
     * See <a href="http://tools.ietf.org/html/rfc2231">RFC 2231</a> for
     * details.
     *
     * @param value  the header value to decode
     * @param params the map of parameters to fill
     * @return the disposition
     */
    public static String decodeContentDisposition(String value, Map<String, String> params) {
        try {
            HeaderTokenizer tokenizer = new HeaderTokenizer(value);
            // get the first token, which must be an ATOM
            Token token = tokenizer.next();
            if (token.getType() != Token.ATOM) {
                return null;
            }
            String disposition = token.getValue();
            // value ignored in this method

            // the remainder is the parameters
            String remainder = tokenizer.getRemainder();
            if (remainder != null) {
                getParameters(remainder, params);
            }
            return disposition;
        } catch (ParseException e) {
            return null;
        }
    }

    protected static class ParseException extends Exception {
        private static final long serialVersionUID = 1L;

        public ParseException() {
            super();
        }

        public ParseException(String message) {
            super(message);
        }
    }

    /*
     * From geronimo-javamail_1.4_spec-1.7.1. Token
     */
    protected static class Token {
        // Constant values from J2SE 1.4 API Docs (Constant values)
        public static final int ATOM = -1;

        public static final int COMMENT = -3;

        public static final int EOF = -4;

        public static final int QUOTEDSTRING = -2;

        private final int type;

        private final String value;

        public Token(int type, String value) {
            this.type = type;
            this.value = value;
        }

        public int getType() {
            return type;
        }

        public String getValue() {
            return value;
        }
    }

    /*
     * Tweaked from geronimo-javamail_1.4_spec-1.7.1. HeaderTokenizer
     */
    protected static class HeaderTokenizer {

        private static final Token EOF = new Token(Token.EOF, null);

        private final String header;

        private final String delimiters;

        private final boolean skipComments;

        private int pos;

        public HeaderTokenizer(String header) {
            this(header, MIME_SPECIALS, true);
        }

        protected HeaderTokenizer(String header, String delimiters, boolean skipComments) {
            this.header = header;
            this.delimiters = delimiters;
            this.skipComments = skipComments;
        }

        public String getRemainder() {
            return header.substring(pos);
        }

        public Token next() throws ParseException {
            return readToken();
        }

        /**
         * Read an ATOM token from the parsed header.
         *
         * @return A token containing the value of the atom token.
         */
        private Token readAtomicToken() {
            // skip to next delimiter
            int start = pos;
            while (++pos < header.length()) {
                // break on the first non-atom character.
                char ch = header.charAt(pos);
                if (delimiters.indexOf(header.charAt(pos)) != -1 || ch < 32 || ch >= 127) {
                    break;
                }
            }
            return new Token(Token.ATOM, header.substring(start, pos));
        }

        /**
         * Read the next token from the header.
         *
         * @return The next token from the header. White space is skipped, and
         * comment tokens are also skipped if indicated.
         */
        private Token readToken() throws ParseException {
            if (pos >= header.length()) {
                return EOF;
            } else {
                char c = header.charAt(pos);
                // comment token...read and skip over this
                if (c == '(') {
                    Token comment = readComment();
                    if (skipComments) {
                        return readToken();
                    } else {
                        return comment;
                    }
                    // quoted literal
                } else if (c == '\"') {
                    return readQuotedString();
                    // white space, eat this and find a real token.
                } else if (WHITE.indexOf(c) != -1) {
                    eatWhiteSpace();
                    return readToken();
                    // either a CTL or special. These characters have a
                    // self-defining token type.
                } else if (c < 32 || c >= 127 || delimiters.indexOf(c) != -1) {
                    pos++;
                    return new Token((int) c, String.valueOf(c));
                } else {
                    // start of an atom, parse it off.
                    return readAtomicToken();
                }
            }
        }

        /**
         * Extract a substring from the header string and apply any
         * escaping/folding rules to the string.
         *
         * @param start The starting offset in the header.
         * @param end   The header end offset + 1.
         * @return The processed string value.
         */
        private String getEscapedValue(int start, int end) throws ParseException {
            StringBuilder value = new StringBuilder();
            for (int i = start; i < end; i++) {
                char ch = header.charAt(i);
                // is this an escape character?
                if (ch == '\\') {
                    i++;
                    if (i == end) {
                        throw new ParseException("Invalid escape character");
                    }
                    value.append(header.charAt(i));
                } else if (ch == '\r') {
                    // line breaks are ignored, except for naked '\n'
                    // characters, which are consider parts of linear
                    // whitespace.
                    // see if this is a CRLF sequence, and skip the second if it
                    // is.
                    if (i < end - 1 && header.charAt(i + 1) == '\n') {
                        i++;
                    }
                } else {
                    // just append the ch value.
                    value.append(ch);
                }
            }
            return value.toString();
        }

        /**
         * Read a comment from the header, applying nesting and escape rules to
         * the content.
         *
         * @return A comment token with the token value.
         */
        private Token readComment() throws ParseException {
            int start = pos + 1;
            int nesting = 1;
            boolean requiresEscaping = false;
            // skip to end of comment/string
            while (++pos < header.length()) {
                char ch = header.charAt(pos);
                if (ch == ')') {
                    nesting--;
                    if (nesting == 0) {
                        break;
                    }
                } else if (ch == '(') {
                    nesting++;
                } else if (ch == '\\') {
                    pos++;
                    requiresEscaping = true;
                } else if (ch == '\r') {
                    // we need to process line breaks also
                    requiresEscaping = true;
                }
            }
            if (nesting != 0) {
                throw new ParseException("Unbalanced comments");
            }
            String value;
            if (requiresEscaping) {
                value = getEscapedValue(start, pos);
            } else {
                value = header.substring(start, pos++);
            }
            return new Token(Token.COMMENT, value);
        }

        /**
         * Parse out a quoted string from the header, applying escaping rules to
         * the value.
         *
         * @return The QUOTEDSTRING token with the value.
         * @throws ParseException
         */
        private Token readQuotedString() throws ParseException {
            int start = pos + 1;
            boolean requiresEscaping = false;
            // skip to end of comment/string
            while (++pos < header.length()) {
                char ch = header.charAt(pos);
                if (ch == '"') {
                    String value;
                    if (requiresEscaping) {
                        value = getEscapedValue(start, pos++);
                    } else {
                        value = header.substring(start, pos++);
                    }
                    return new Token(Token.QUOTEDSTRING, value);
                } else if (ch == '\\') {
                    pos++;
                    requiresEscaping = true;
                } else if (ch == '\r') {
                    // we need to process line breaks also
                    requiresEscaping = true;
                }
            }
            throw new ParseException("Missing '\"'");
        }

        /**
         * Skip white space in the token string.
         */
        private void eatWhiteSpace() {
            // skip to end of whitespace
            while (++pos < header.length() && WHITE.indexOf(header.charAt(pos)) != -1) {
                // just read
            }
        }
    }

    /*
     * Tweaked from geronimo-javamail_1.4_spec-1.7.1. ParameterList
     */
    protected static Map<String, String> getParameters(String list, Map<String, String> params) throws ParseException {
        HeaderTokenizer tokenizer = new HeaderTokenizer(list);
        while (true) {
            Token token = tokenizer.next();
            switch (token.getType()) {
                case Token.EOF:
                    // the EOF token terminates parsing.
                    return params;

                case ';':
                    // each new parameter is separated by a semicolon, including
                    // the first, which separates
                    // the parameters from the main part of the header.
                    // the next token needs to be a parameter name
                    token = tokenizer.next();
                    // allow a trailing semicolon on the parameters.
                    if (token.getType() == Token.EOF) {
                        return params;
                    }

                    if (token.getType() != Token.ATOM) {
                        throw new ParseException("Invalid parameter name: " + token.getValue());
                    }

                    // get the parameter name as a lower case version for better
                    // mapping.
                    String name = token.getValue().toLowerCase(Locale.ENGLISH);

                    token = tokenizer.next();

                    // parameters are name=value, so we must have the "=" here.
                    if (token.getType() != '=') {
                        throw new ParseException("Missing '='");
                    }

                    // now the value, which may be an atom or a literal
                    token = tokenizer.next();

                    if (token.getType() != Token.ATOM && token.getType() != Token.QUOTEDSTRING) {
                        throw new ParseException("Invalid parameter value: " + token.getValue());
                    }

                    String value = token.getValue();

                    // we might have to do some additional decoding. A name that
                    // ends with "*" is marked as being encoded, so if requested, we
                    // decode the value.
                    if (name.endsWith("*")) {
                        name = name.substring(0, name.length() - 1);
                        value = decodeRFC2231value(value);
                    }
                    params.put(name, value);
                    break;
                default:
                    throw new ParseException("Missing ';'");
            }
        }
    }

    protected static String decodeRFC2231value(String value) {
        int q1 = value.indexOf('\'');
        if (q1 == -1) {
            // missing charset
            return value;
        }
        String mimeCharset = value.substring(0, q1);
        int q2 = value.indexOf('\'', q1 + 1);
        if (q2 == -1) {
            // missing language
            return value;
        }
        byte[] bytes = fromHex(value.substring(q2 + 1));
        try {
            return new String(bytes, Charset.forName(mimeCharset));
        } catch (UnsupportedCharsetException e) {
            // incorrect encoding
            return value;
        }
    }

    protected static byte[] fromHex(String data) {
        ByteArrayOutputStream out = new ByteArrayOutputStream(data.length());
        for (int i = 0; i < data.length(); ) {
            char c = data.charAt(i++);
            if (c == '%') {
                if (i > data.length() - 2) {
                    break; // unterminated sequence
                }
                byte b1 = HEX_DECODE[data.charAt(i++) & 0x7f];
                byte b2 = HEX_DECODE[data.charAt(i++) & 0x7f];
                out.write((b1 << 4) | b2);
            } else {
                out.write((byte) c);
            }
        }
        return out.toByteArray();
    }

}<file_sep>/src/main/java/com/nulabinc/backlog4j/IssueType.java
package com.nulabinc.backlog4j;

/**
 * The interface for Backlog issue type data.
 *
 * @author nulab-inc
 */
public interface IssueType {

    long getId();

    String getIdAsString();

    long getProjectId();

    String getProjectIdAsString();

    String getName();

    Project.IssueTypeColor getColor();

    String getTemplateSummary();

    String getTemplateDescription();
}
<file_sep>/src/test/java/com/nulabinc/backlog4j/api/option/AddCustomFieldParamsTest.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.http.NameValuePair;
import com.nulabinc.backlog4j.internal.json.customFields.DateCustomFieldSetting;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
 * @author nulab-inc
 */
public class AddCustomFieldParamsTest {

    @Test
    public void AddTextCustomFieldParamsTest() {

        // when
        AddTextCustomFieldParams params = new AddTextCustomFieldParams(
                "PRJ", "TextCustomField");
        params.description("TextCustomFieldです").required(true);

        // then
        List<NameValuePair> parameters = params.getParamList();
        assertEquals(4, parameters.size());

        NameValuePair pair = parameters.get(0);
        assertEquals("typeId", pair.getName());
        assertEquals("1", pair.getValue());

        pair = parameters.get(1);
        assertEquals("name", pair.getName());
        assertEquals("TextCustomField", pair.getValue());

        pair = parameters.get(2);
        assertEquals("description", pair.getName());
        assertEquals("TextCustomFieldです", pair.getValue());

        pair = parameters.get(3);
        assertEquals("required", pair.getName());
        assertEquals("true", pair.getValue());


    }

    @Test
    public void AddTextAreaCustomFieldParamsTest() {

        // when
        AddTextAreaCustomFieldParams params = new AddTextAreaCustomFieldParams(
                "PRJ", "TextAreaCustomField");
        params.description("TextCustomFieldです").required(true);

        // then
        List<NameValuePair> parameters = params.getParamList();
        assertEquals(4, parameters.size());

        NameValuePair pair = parameters.get(0);
        assertEquals("typeId", pair.getName());
        assertEquals("2", pair.getValue());

        pair = parameters.get(1);
        assertEquals("name", pair.getName());
        assertEquals("TextAreaCustomField", pair.getValue());

        pair = parameters.get(2);
        assertEquals("description", pair.getName());
        assertEquals("TextCustomFieldです", pair.getValue());

        pair = parameters.get(3);
        assertEquals("required", pair.getName());
        assertEquals("true", pair.getValue());


    }

    @Test
    public void AddNumericCustomFieldParamsTest() {
        // when
        AddNumericCustomFieldParams params = new AddNumericCustomFieldParams(
                "PRJ", "NumericCustomField");
        params.description("TextCustomFieldです").required(true).min(10).max(121212L).initialValue(21);

        // then
        List<NameValuePair> parameters = params.getParamList();
        assertEquals(7, parameters.size());

        NameValuePair pair = parameters.get(0);
        assertEquals("typeId", pair.getName());
        assertEquals("3", pair.getValue());

        pair = parameters.get(1);
        assertEquals("name", pair.getName());
        assertEquals("NumericCustomField", pair.getValue());

        pair = parameters.get(2);
        assertEquals("description", pair.getName());
        assertEquals("TextCustomFieldです", pair.getValue());

        pair = parameters.get(3);
        assertEquals("required", pair.getName());
        assertEquals("true", pair.getValue());

        pair = parameters.get(4);
        assertEquals("min", pair.getName());
        assertEquals("10.0", pair.getValue());

        pair = parameters.get(5);
        assertEquals("max", pair.getName());
        assertEquals("121212.0", pair.getValue());

        pair = parameters.get(6);
        assertEquals("initialValue", pair.getName());
        assertEquals("21.0", pair.getValue());
    }

    @Test
    public void AddDateCustomFieldParamsTest() {
        // when
        AddDateCustomFieldParams params = new AddDateCustomFieldParams(
                "PRJ", "DateCustomField");
        params.description("DateCustomFieldです").required(true)
                .initialValueType(DateCustomFieldSetting.InitialValueType.TodayPlusShiftDays)
                .initialShift(2);

        // then
        List<NameValuePair> parameters = params.getParamList();
        assertEquals(6, parameters.size());

        NameValuePair pair = parameters.get(0);
        assertEquals("typeId", pair.getName());
        assertEquals("4", pair.getValue());

        pair = parameters.get(1);
        assertEquals("name", pair.getName());
        assertEquals("DateCustomField", pair.getValue());

        pair = parameters.get(2);
        assertEquals("description", pair.getName());
        assertEquals("DateCustomFieldです", pair.getValue());

        pair = parameters.get(3);
        assertEquals("required", pair.getName());
        assertEquals("true", pair.getValue());

        pair = parameters.get(4);
        assertEquals("initialValueType", pair.getName());
        assertEquals("2", pair.getValue());

        pair = parameters.get(5);
        assertEquals("initialShift", pair.getName());
        assertEquals("2", pair.getValue());
    }

    @Test
    public void AddSingleListCustomFieldParamsTest() {
        // when
        AddSingleListCustomFieldParams params = new AddSingleListCustomFieldParams(
                "PRJ", "SingleListCustomField");
        params.description("SingleListCustomFieldです").required(true)
                .allowInput(false)
                .allowAddItem(true)
                .items(Arrays.asList("卵", "牛乳", "みかん"));

        // then
        List<NameValuePair> parameters = params.getParamList();
        assertEquals(9, parameters.size());

        NameValuePair pair = parameters.get(0);
        assertEquals("typeId", pair.getName());
        assertEquals("5", pair.getValue());

        pair = parameters.get(1);
        assertEquals("name", pair.getName());
        assertEquals("SingleListCustomField", pair.getValue());

        pair = parameters.get(2);
        assertEquals("description", pair.getName());
        assertEquals("SingleListCustomFieldです", pair.getValue());

        pair = parameters.get(3);
        assertEquals("required", pair.getName());
        assertEquals("true", pair.getValue());

        pair = parameters.get(4);
        assertEquals("allowInput", pair.getName());
        assertEquals("false", pair.getValue());

        pair = parameters.get(5);
        assertEquals("allowAddItem", pair.getName());
        assertEquals("true", pair.getValue());

        pair = parameters.get(6);
        assertEquals("items[]", pair.getName());
        assertEquals("卵", pair.getValue());

        pair = parameters.get(7);
        assertEquals("items[]", pair.getName());
        assertEquals("牛乳", pair.getValue());

        pair = parameters.get(8);
        assertEquals("items[]", pair.getName());
        assertEquals("みかん", pair.getValue());

    }

    @Test
    public void AddMultipleListCustomFieldParamsTest() {
        // when
        AddMultipleListCustomFieldParams params = new AddMultipleListCustomFieldParams(
                "PRJ", "MultipleListCustomField");
        params.description("MultipleListCustomFieldです").required(true)
                .allowInput(false)
                .allowAddItem(true)
                .items(Arrays.asList("卵", "牛乳", "みかん"));

        // then
        List<NameValuePair> parameters = params.getParamList();
        assertEquals(9, parameters.size());

        NameValuePair pair = parameters.get(0);
        assertEquals("typeId", pair.getName());
        assertEquals("6", pair.getValue());

        pair = parameters.get(1);
        assertEquals("name", pair.getName());
        assertEquals("MultipleListCustomField", pair.getValue());

        pair = parameters.get(2);
        assertEquals("description", pair.getName());
        assertEquals("MultipleListCustomFieldです", pair.getValue());

        pair = parameters.get(3);
        assertEquals("required", pair.getName());
        assertEquals("true", pair.getValue());

        pair = parameters.get(4);
        assertEquals("allowInput", pair.getName());
        assertEquals("false", pair.getValue());

        pair = parameters.get(5);
        assertEquals("allowAddItem", pair.getName());
        assertEquals("true", pair.getValue());

        pair = parameters.get(6);
        assertEquals("items[]", pair.getName());
        assertEquals("卵", pair.getValue());

        pair = parameters.get(7);
        assertEquals("items[]", pair.getName());
        assertEquals("牛乳", pair.getValue());

        pair = parameters.get(8);
        assertEquals("items[]", pair.getName());
        assertEquals("みかん", pair.getValue());

    }

    @Test
    public void AddCheckBoxCustomFieldParamsTest() {
        // when
        AddCheckBoxCustomFieldParams params = new AddCheckBoxCustomFieldParams(
                "PRJ", "CheckBoxCustomField");
        params.description("CheckBoxCustomFieldです").required(true)
                .allowInput(false)
                .allowAddItem(true)
                .items(Arrays.asList("卵", "牛乳", "みかん"));

        // then
        List<NameValuePair> parameters = params.getParamList();
        assertEquals(9, parameters.size());

        NameValuePair pair = parameters.get(0);
        assertEquals("typeId", pair.getName());
        assertEquals("7", pair.getValue());

        pair = parameters.get(1);
        assertEquals("name", pair.getName());
        assertEquals("CheckBoxCustomField", pair.getValue());

        pair = parameters.get(2);
        assertEquals("description", pair.getName());
        assertEquals("CheckBoxCustomFieldです", pair.getValue());

        pair = parameters.get(3);
        assertEquals("required", pair.getName());
        assertEquals("true", pair.getValue());

        pair = parameters.get(4);
        assertEquals("allowInput", pair.getName());
        assertEquals("false", pair.getValue());

        pair = parameters.get(5);
        assertEquals("allowAddItem", pair.getName());
        assertEquals("true", pair.getValue());

        pair = parameters.get(6);
        assertEquals("items[]", pair.getName());
        assertEquals("卵", pair.getValue());

        pair = parameters.get(7);
        assertEquals("items[]", pair.getName());
        assertEquals("牛乳", pair.getValue());

        pair = parameters.get(8);
        assertEquals("items[]", pair.getName());
        assertEquals("みかん", pair.getValue());

    }

    @Test
    public void AddRadioCustomFieldParamsTest() {
        // when
        AddRadioCustomFieldParams params = new AddRadioCustomFieldParams(
                "PRJ", "RadioCustomField");
        params.description("RadioCustomFieldです").required(true)
                .allowInput(false)
                .allowAddItem(true)
                .items(Arrays.asList("卵", "牛乳", "みかん"));

        // then
        List<NameValuePair> parameters = params.getParamList();
        assertEquals(9, parameters.size());

        NameValuePair pair = parameters.get(0);
        assertEquals("typeId", pair.getName());
        assertEquals("8", pair.getValue());

        pair = parameters.get(1);
        assertEquals("name", pair.getName());
        assertEquals("RadioCustomField", pair.getValue());

        pair = parameters.get(2);
        assertEquals("description", pair.getName());
        assertEquals("RadioCustomFieldです", pair.getValue());

        pair = parameters.get(3);
        assertEquals("required", pair.getName());
        assertEquals("true", pair.getValue());

        pair = parameters.get(4);
        assertEquals("allowInput", pair.getName());
        assertEquals("false", pair.getValue());

        pair = parameters.get(5);
        assertEquals("allowAddItem", pair.getName());
        assertEquals("true", pair.getValue());

        pair = parameters.get(6);
        assertEquals("items[]", pair.getName());
        assertEquals("卵", pair.getValue());

        pair = parameters.get(7);
        assertEquals("items[]", pair.getName());
        assertEquals("牛乳", pair.getValue());

        pair = parameters.get(8);
        assertEquals("items[]", pair.getName());
        assertEquals("みかん", pair.getValue());

    }
}
<file_sep>/src/test/java/com/nulabinc/backlog4j/api/option/UpdateGroupParamsTest.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.http.NameValuePair;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
 * @author nulab-inc
 */
public class UpdateGroupParamsTest extends AbstractParamsTest {

    @Test
    public void createParamTest() {

        // when
        UpdateGroupParams params = new UpdateGroupParams(1000000001L);
        params.name("group 1")
                .members(Arrays.asList(2000000001L, 2000000002L));

        // then
        assertEquals("1000000001", params.getGroupId());
        List<NameValuePair> parameters = params.getParamList();
        assertEquals(3, parameters.size());
        assertTrue(existsOneKeyValue(parameters, "name", "group 1"));
        assertTrue(existsOneKeyValue(parameters, "members[]", "2000000001"));
        assertTrue(existsOneKeyValue(parameters, "members[]", "2000000002"));
    }

    @Test
    public void createParamWithStringIdTest() {

        // when
        UpdateGroupParams params = new UpdateGroupParams("1000000001");
        params.name("group 2")
                .members(Arrays.asList("2000000001", "2000000002"));

        // then
        assertEquals("1000000001", params.getGroupId());
        List<NameValuePair> parameters = params.getParamList();
        assertEquals(3, parameters.size());
        assertTrue(existsOneKeyValue(parameters, "name", "group 2"));
        assertTrue(existsOneKeyValue(parameters, "members[]", "2000000001"));
        assertTrue(existsOneKeyValue(parameters, "members[]", "2000000002"));
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/AddDateCustomFieldParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.CustomFieldSetting;
import com.nulabinc.backlog4j.internal.json.customFields.DateCustomFieldSetting;
import com.nulabinc.backlog4j.http.NameValuePair;

import java.util.List;

/**
 * Parameters for add date type custom field API.
 *
 * @author nulab-inc
 */
public class AddDateCustomFieldParams extends AddCustomFieldParams {

    public AddDateCustomFieldParams(Object projectIdOrKey, String name) {
        super(projectIdOrKey, CustomFieldSetting.FieldType.Date, name);
    }

    public AddDateCustomFieldParams min(String min) {
        parameters.add(new NameValuePair("min", min));
        return this;
    }

    public AddDateCustomFieldParams max(String max) {
        parameters.add(new NameValuePair("max", max));
        return this;
    }

    public AddDateCustomFieldParams initialValueType(DateCustomFieldSetting.InitialValueType initialValueType) {
        parameters.add(new NameValuePair("initialValueType", String.valueOf(initialValueType.getIntValue())));
        return this;
    }

    public AddDateCustomFieldParams initialDate(String initialDate) {
        parameters.add(new NameValuePair("initialDate", initialDate));
        return this;
    }

    public AddDateCustomFieldParams initialShift(int initialShift) {
        parameters.add(new NameValuePair("initialShift", String.valueOf(initialShift)));
        return this;
    }

    @Override
    public AddDateCustomFieldParams applicableIssueTypes(List<Long> applicableIssueTypes) {
        return (AddDateCustomFieldParams) super.applicableIssueTypes(applicableIssueTypes);
    }

    @Override
    public AddDateCustomFieldParams description(String description) {
        return (AddDateCustomFieldParams) super.description(description);
    }

    @Override
    public AddDateCustomFieldParams required(boolean required) {
        return (AddDateCustomFieldParams) super.required(required);
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/GetParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.http.NameValuePair;

import java.util.ArrayList;
import java.util.List;

/**
 * Parameters for get request.
 *
 * @author nulab-inc
 */
public abstract class GetParams {
    protected List<NameValuePair> parameters = new ArrayList<NameValuePair>();

    public List<NameValuePair> getParamList() {
        return parameters;
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/Repository.java
package com.nulabinc.backlog4j;

import java.util.Date;

/**
 * The interface for Backlog repository data.
 *
 * @author nulab-inc
 */
public interface Repository {

    long getId();

    String getIdAsString();

    long getProjectId();

    String getProjectIdAsString();

    String getName();

    String getDescription();

    String getHookUrl();

    String getHttpUrl();

    String getSshUrl();

    long getDisplayOrder();

    Date getPushedAt();

    User getCreatedUser();

    Date getCreated();

    User getUpdatedUser();

    Date getUpdated();
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/Star.java
package com.nulabinc.backlog4j;

import java.util.Date;

/**
 * The interface for Backlog star data.
 *
 * @author nulab-inc
 */
public interface Star {
    long getId();

    String getIdAsString();

    String getComment();

    String getUrl();

    String getTitle();

    User getPresenter();

    Date getCreated();

}
<file_sep>/src/main/java/com/nulabinc/backlog4j/Comment.java
package com.nulabinc.backlog4j;

/**
 * The interface for Backlog comment data.
 *
 * @author nulab-inc
 */
public interface Comment {

    long getId();

    String getIdAsString();

    String getContent();

}
<file_sep>/src/main/java/com/nulabinc/backlog4j/internal/json/WebhookJSONImpl.java
package com.nulabinc.backlog4j.internal.json;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.nulabinc.backlog4j.Activity;
import com.nulabinc.backlog4j.User;
import com.nulabinc.backlog4j.Webhook;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * @author nulab-inc
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public class WebhookJSONImpl implements Webhook {

    private long id;
    private String name;
    private String description;
    private String hookUrl;
    private boolean allEvent;
    private int[] activityTypeIds;
    @JsonDeserialize(as=UserJSONImpl.class)
    private User createdUser;
    @JsonDeserialize(using = JacksonCustomDateDeserializer.class)
    private Date created;
    @JsonDeserialize(as=UserJSONImpl.class)
    private User updatedUser;
    @JsonDeserialize(using = JacksonCustomDateDeserializer.class)
    private Date updated;

    @Override
    public long getId() {
        return this.id;
    }

    @Override
    public String getIdAsString() {
        return String.valueOf(this.id);
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public String getDescription() {
        return this.description;
    }

    @Override
    public String getHookUrl() {
        return this.hookUrl;
    }

    @Override
    public boolean isAllEvent() {
        return this.allEvent;
    }

    @Override
    public List<Activity.Type> getActivityTypeIds() {
        if(activityTypeIds==null){
            return null;
        }

        List<Activity.Type> typeIds = new ArrayList<Activity.Type>();
        for(int activityTypeId : activityTypeIds){
            typeIds.add(Activity.Type.valueOf(activityTypeId));
        }
        return typeIds;
    }

    @Override
    public User getCreatedUser() {
        return createdUser;
    }

    @Override
    public Date getCreated() {
        return created;
    }

    @Override
    public User getUpdatedUser() {
        return updatedUser;
    }

    @Override
    public Date getUpdated() {
        return updated;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        if (obj.getClass() != getClass()) {
            return false;
        }
        WebhookJSONImpl rhs = (WebhookJSONImpl) obj;
        return new EqualsBuilder()
                .append(this.id, rhs.id)
                .append(this.name, rhs.name)
                .append(this.description, rhs.description)
                .append(this.hookUrl, rhs.hookUrl)
                .append(this.allEvent, rhs.allEvent)
                .append(this.activityTypeIds, rhs.activityTypeIds)
                .append(this.createdUser, rhs.createdUser)
                .append(this.created, rhs.created)
                .append(this.updatedUser, rhs.updatedUser)
                .append(this.updated, rhs.updated)
                .isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder()
                .append(id)
                .append(name)
                .append(description)
                .append(hookUrl)
                .append(allEvent)
                .append(activityTypeIds)
                .append(createdUser)
                .append(created)
                .append(updatedUser)
                .append(updated)
                .toHashCode();
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("id", id)
                .append("name", name)
                .append("description", description)
                .append("hookUrl", hookUrl)
                .append("allEvent", allEvent)
                .append("activityTypeIds", activityTypeIds)
                .append("createdUser", createdUser)
                .append("created", created)
                .append("updatedUser", updatedUser)
                .append("updated", updated)
                .toString();
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/Link.java
package com.nulabinc.backlog4j;

/**
 * The interface for Backlog link data.
 *
 * @author nulab-inc
 */
public interface Link {

    long getId();

    String getIdAsString();

    long getKeyId();

    String getKeyIdAsString();

    String getTitle();

}
<file_sep>/src/test/java/com/nulabinc/backlog4j/api/option/UpdateIssueParamsTest.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.Issue;
import com.nulabinc.backlog4j.http.NameValuePair;
import org.junit.jupiter.api.Test;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
 * @author nulab-inc
 */
public class UpdateIssueParamsTest extends AbstractParamsTest {
    @Test
    public void createParamTest() {

        // when
        UpdateIssueParams params = new UpdateIssueParams(1000000001L);
        params.summary("test issue 1")
                .issueTypeId(2000000001L)
                .priority(Issue.PriorityType.Normal)
                .parentIssueId(3000000001L)
                .description("This is a test issue.")
                .startDate("2014-08-01")
                .dueDate("2014-12-01")
                .estimatedHours(16)
                .actualHours(12.5f)
                .assigneeId(4000000001L)
                .categoryIds(Arrays.asList(5000000001L, 5000000002L))
                .versionIds(Arrays.asList(6000000001L, 6000000002L))
                .milestoneIds(Arrays.asList(7000000001L, 7000000002L))
                .notifiedUserIds(Arrays.asList(8000000001L, 8000000002L))
                .attachmentIds(Arrays.asList(9000000001L, 9000000002L))
                .comment("issue comment")
                .status(Issue.StatusType.Resolved)
                .resolution(Issue.ResolutionType.Fixed);

        // then
        assertEquals("1000000001", params.getIssueIdOrKeyString());
        List<NameValuePair> parameters = params.getParamList();
        assertEquals(23, parameters.size());
        assertTrue(existsOneKeyValue(parameters, "summary", "test issue 1"));
        assertTrue(existsOneKeyValue(parameters, "issueTypeId", "2000000001"));
        assertTrue(existsOneKeyValue(parameters, "priorityId", "3"));
        assertTrue(existsOneKeyValue(parameters, "parentIssueId", "3000000001"));
        assertTrue(existsOneKeyValue(parameters, "description", "This is a test issue."));
        assertTrue(existsOneKeyValue(parameters, "startDate", "2014-08-01"));
        assertTrue(existsOneKeyValue(parameters, "dueDate", "2014-12-01"));
        assertTrue(existsOneKeyValue(parameters, "estimatedHours", "16"));
        assertTrue(existsOneKeyValue(parameters, "actualHours", "12.5"));
        assertTrue(existsOneKeyValue(parameters, "assigneeId", "4000000001"));
        assertTrue(existsOneKeyValue(parameters, "categoryId[]", "5000000001"));
        assertTrue(existsOneKeyValue(parameters, "categoryId[]", "5000000002"));
        assertTrue(existsOneKeyValue(parameters, "versionId[]", "6000000001"));
        assertTrue(existsOneKeyValue(parameters, "versionId[]", "6000000002"));
        assertTrue(existsOneKeyValue(parameters, "milestoneId[]", "7000000001"));
        assertTrue(existsOneKeyValue(parameters, "milestoneId[]", "7000000002"));
        assertTrue(existsOneKeyValue(parameters, "notifiedUserId[]", "8000000001"));
        assertTrue(existsOneKeyValue(parameters, "notifiedUserId[]", "8000000002"));
        assertTrue(existsOneKeyValue(parameters, "attachmentId[]", "9000000001"));
        assertTrue(existsOneKeyValue(parameters, "attachmentId[]", "9000000002"));
        assertTrue(existsOneKeyValue(parameters, "comment", "issue comment"));
        assertTrue(existsOneKeyValue(parameters, "statusId", "3"));
        assertTrue(existsOneKeyValue(parameters, "resolutionId", "0"));
    }

    @Test
    public void createParamEmptySetTest() {

        // when
        UpdateIssueParams params = new UpdateIssueParams(1000000001L);
        params.resolution(null).assigneeId(0L)
                .dueDate(null).startDate(null)
                .categoryIds(null).versionIds(null).milestoneIds(null)
                .estimatedHours(null).actualHours(null);

        List<NameValuePair> parameters = params.getParamList();
        assertTrue(existsOneKeyValue(parameters, "resolutionId", ""));
        assertTrue(existsOneKeyValue(parameters, "assigneeId", ""));
        assertTrue(existsOneKeyValue(parameters, "dueDate", ""));
        assertTrue(existsOneKeyValue(parameters, "startDate", ""));
        assertTrue(existsOneKeyValue(parameters, "categoryId[]", ""));
        assertTrue(existsOneKeyValue(parameters, "versionId[]", ""));
        assertTrue(existsOneKeyValue(parameters, "milestoneId[]", ""));
        assertTrue(existsOneKeyValue(parameters, "estimatedHours", ""));
        assertTrue(existsOneKeyValue(parameters, "actualHours", ""));

    }


    @Test
    public void createTextCustomFieldParamTest() {
        // when
        UpdateIssueParams params = new UpdateIssueParams(1000000001L);
        params.textCustomFields(Arrays.asList(
                new CustomFiledValue(3000000001L, "egg"),
                new CustomFiledValue(3000000002L, "rice")));

        params.textCustomField(new CustomFiledValue(3000000003L, "tea"));

        // then
        List<NameValuePair> parameters = params.getParamList();
        assertTrue(existsOneKeyValue(parameters, "customField_3000000001", "egg"));
        assertTrue(existsOneKeyValue(parameters, "customField_3000000002", "rice"));
        assertTrue(existsOneKeyValue(parameters, "customField_3000000003", "tea"));
    }

    @Test
    public void createTextAreaCustomFieldParamTest() {
        // when
        UpdateIssueParams params = new UpdateIssueParams(1000000001L);
        params.textAreaCustomFields(Arrays.asList(
                new CustomFiledValue(3000000001L, "egg"),
                new CustomFiledValue(3000000002L, "rice")));

        params.textAreaCustomField(new CustomFiledValue(3000000003L, "tea"));

        // then
        List<NameValuePair> parameters = params.getParamList();
        assertTrue(existsOneKeyValue(parameters, "customField_3000000001", "egg"));
        assertTrue(existsOneKeyValue(parameters, "customField_3000000002", "rice"));
        assertTrue(existsOneKeyValue(parameters, "customField_3000000003", "tea"));
    }

    @Test
    public void createNumericCustomFieldParamTest() {
        // when
        UpdateIssueParams params = new UpdateIssueParams(1000000001L);
        params.numericCustomFields(Arrays.asList(
                new CustomFiledValue(3000000001L, -111f),
                new CustomFiledValue(3000000002L, 5555.121f)));

        params.numericCustomField(new CustomFiledValue(3000000003L, 123.61f));
        params.numericCustomField(new CustomFiledValue(3000000004L, 123.67777));

        // then
        List<NameValuePair> parameters = params.getParamList();
        assertTrue(existsOneKeyValue(parameters, "customField_3000000001", "-111.0"));
        assertTrue(existsOneKeyValue(parameters, "customField_3000000002", "5555.121"));
        assertTrue(existsOneKeyValue(parameters, "customField_3000000003", "123.61"));
        assertTrue(existsOneKeyValue(parameters, "customField_3000000004", "123.67777"));

    }

    @Test
    public void createDateCustomFieldParamTest() {
        // when
        UpdateIssueParams params = new UpdateIssueParams(1000000001L);
        params.dateCustomFields(Arrays.asList(
                new CustomFiledValue(3000000001L, "2008-10-01"),
                new CustomFiledValue(3000000002L, "2008-10-02")));

        params.dateCustomField(new CustomFiledValue(3000000003L, "2008-10-10"));

        // then
        List<NameValuePair> parameters = params.getParamList();
        assertTrue(existsOneKeyValue(parameters, "customField_3000000001", "2008-10-01"));
        assertTrue(existsOneKeyValue(parameters, "customField_3000000002", "2008-10-02"));
        assertTrue(existsOneKeyValue(parameters, "customField_3000000003", "2008-10-10"));
    }

    @Test
    public void createSingleListCustomFieldParamTest() {
        // when
        UpdateIssueParams params = new UpdateIssueParams(1000000001L);
        params.singleListCustomFields(Arrays.asList(
                new CustomFiledItem(3000000001L, 4000000001L),
                new CustomFiledItem(3000000002L, 4000000002L)));

        params.singleListCustomField(new CustomFiledItem(3000000003L, 4000000003L));

        // then
        List<NameValuePair> parameters = params.getParamList();
        assertTrue(existsOneKeyValue(parameters, "customField_3000000001", "4000000001"));
        assertTrue(existsOneKeyValue(parameters, "customField_3000000002", "4000000002"));
        assertTrue(existsOneKeyValue(parameters, "customField_3000000003", "4000000003"));
    }

    @Test
    public void createRadioCustomFieldParamTest() {
        // when
        UpdateIssueParams params = new UpdateIssueParams(1000000001L);
        params.radioCustomFields(Arrays.asList(
                new CustomFiledItem(3000000001L, 4000000001L),
                new CustomFiledItem(3000000002L, 4000000002L)));

        params.radioCustomField(new CustomFiledItem(3000000003L, 4000000003L));

        // then
        List<NameValuePair> parameters = params.getParamList();
        assertTrue(existsOneKeyValue(parameters, "customField_3000000001", "4000000001"));
        assertTrue(existsOneKeyValue(parameters, "customField_3000000002", "4000000002"));
        assertTrue(existsOneKeyValue(parameters, "customField_3000000003", "4000000003"));
    }

    @Test
    public void createMultipleListCustomFieldParamTest() {
        // when
        UpdateIssueParams params = new UpdateIssueParams(1000000001L);
        params.multipleListCustomFields(Arrays.asList(
                new CustomFiledItems(3000000001L, Arrays.asList(4000000001L, 4000000002L)),
                new CustomFiledItems(3000000002L, Arrays.asList(4000000003L, 4000000004L))));

        params.multipleListCustomField(new CustomFiledItems(3000000003L, Arrays.asList(4000000005L, 4000000006L)));

        // then
        List<NameValuePair> parameters = params.getParamList();
        assertTrue(existsOneKeyValue(parameters, "customField_3000000001", "4000000001"));
        assertTrue(existsOneKeyValue(parameters, "customField_3000000001", "4000000002"));
        assertTrue(existsOneKeyValue(parameters, "customField_3000000002", "4000000003"));
        assertTrue(existsOneKeyValue(parameters, "customField_3000000002", "4000000004"));
        assertTrue(existsOneKeyValue(parameters, "customField_3000000003", "4000000005"));
        assertTrue(existsOneKeyValue(parameters, "customField_3000000003", "4000000006"));
    }

    @Test
    public void createCheckBoxListCustomFieldParamTest() {
        // when
        UpdateIssueParams params = new UpdateIssueParams(1000000001L);
        params.checkBoxCustomFields(Arrays.asList(
                new CustomFiledItems(3000000001L, Arrays.asList(4000000001L, 4000000002L)),
                new CustomFiledItems(3000000002L, Arrays.asList(4000000003L, 4000000004L))));

        params.checkBoxCustomField(new CustomFiledItems(3000000003L, Arrays.asList(4000000005L, 4000000006L)));

        // then
        List<NameValuePair> parameters = params.getParamList();
        assertTrue(existsOneKeyValue(parameters, "customField_3000000001", "4000000001"));
        assertTrue(existsOneKeyValue(parameters, "customField_3000000001", "4000000002"));
        assertTrue(existsOneKeyValue(parameters, "customField_3000000002", "4000000003"));
        assertTrue(existsOneKeyValue(parameters, "customField_3000000002", "4000000004"));
        assertTrue(existsOneKeyValue(parameters, "customField_3000000003", "4000000005"));
        assertTrue(existsOneKeyValue(parameters, "customField_3000000003", "4000000006"));
    }

    @Test
    public void createIntHoursParamTest() {

        // when
        UpdateIssueParams params = new UpdateIssueParams(1000000001L);
        params.estimatedHours(16)
                .actualHours(12);

        // then
        assertEquals("1000000001", params.getIssueIdOrKeyString());
        List<NameValuePair> parameters = params.getParamList();
        assertEquals(2, parameters.size());
        assertTrue(existsOneKeyValue(parameters, "estimatedHours", "16"));
        assertTrue(existsOneKeyValue(parameters, "actualHours", "12"));
    }

    @Test
    public void createFloatHoursParamTest() {

        // when
        UpdateIssueParams params = new UpdateIssueParams(1000000001L);
        params.estimatedHours(16.00f)
                .actualHours(12.12f);

        // then
        assertEquals("1000000001", params.getIssueIdOrKeyString());
        List<NameValuePair> parameters = params.getParamList();
        assertEquals(2, parameters.size());
        assertTrue(existsOneKeyValue(parameters, "estimatedHours", "16.0"));
        assertTrue(existsOneKeyValue(parameters, "actualHours", "12.12"));
    }


    @Test
    public void createBigDecimalHoursParamTest() {

        // when
        UpdateIssueParams params = new UpdateIssueParams(1000000001L);
        params.estimatedHours(new BigDecimal("16.00"))
                .actualHours(new BigDecimal("12.12345"));

        // then
        assertEquals("1000000001", params.getIssueIdOrKeyString());
        List<NameValuePair> parameters = params.getParamList();
        assertEquals(2, parameters.size());
        assertTrue(existsOneKeyValue(parameters, "estimatedHours", "16.00"));
        assertTrue(existsOneKeyValue(parameters, "actualHours", "12.12"));
    }

}
<file_sep>/src/main/java/com/nulabinc/backlog4j/internal/json/ViewedWikiJSONImpl.java
package com.nulabinc.backlog4j.internal.json;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.nulabinc.backlog4j.User;
import com.nulabinc.backlog4j.ViewedWiki;
import com.nulabinc.backlog4j.Wiki;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

import java.util.Date;

/**
 * @author nulab-inc
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public class ViewedWikiJSONImpl implements ViewedWiki {

    @JsonDeserialize(as = WikiJSONImpl.class)
    private Wiki page;
    @JsonDeserialize(using = JacksonCustomDateDeserializer.class)
    private Date updated;

    @Override
    public Wiki getPage() {
        return page;
    }

    @Override
    public Date getUpdated() {
        return updated;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        if (obj.getClass() != getClass()) {
            return false;
        }
        ViewedWikiJSONImpl rhs = (ViewedWikiJSONImpl) obj;
        return new EqualsBuilder()
                .append(this.page, rhs.page)
                .append(this.updated, rhs.updated)
                .isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder()
                .append(page)
                .append(updated)
                .toHashCode();
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("page", page)
                .append("updated", updated)
                .toString();
    }
}
<file_sep>/src/test/java/com/nulabinc/backlog4j/internal/json/PullRequestCommentJSONImplTest.java
package com.nulabinc.backlog4j.internal.json;

import com.nulabinc.backlog4j.AttachmentInfo;
import com.nulabinc.backlog4j.ChangeLog;
import com.nulabinc.backlog4j.Notification;
import com.nulabinc.backlog4j.PullRequestComment;
import com.nulabinc.backlog4j.ResponseList;
import com.nulabinc.backlog4j.Star;
import com.nulabinc.backlog4j.User;
import org.exparity.hamcrest.date.DateMatchers;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.util.Calendar;
import java.util.List;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;

/**
 * @author nulab-inc
 */
public class PullRequestCommentJSONImplTest extends AbstractJSONImplTest {
    @Test
    public void createPullRequestCommentListTest() throws IOException {
        String fileContentStr = getJsonString("json/pull_request_comments.json");
        ResponseList<PullRequestComment> pullRequestComments = factory.createPullRequestCommentList(fileContentStr);

        assertEquals(1, pullRequestComments.size());

        PullRequestComment pullRequestComment = pullRequestComments.get(0);
        assertEquals(12345, pullRequestComment.getId());
        assertEquals("test comment\"(greenpepper)\"\"(Cacoo)\"", pullRequestComment.getContent());

        List<ChangeLog> logs = pullRequestComment.getChangeLog();
        ChangeLog log = logs.get(0);
        assertEquals("*銀行振込：30件", log.getNewValue());
        AttachmentInfo attachmentInfo = log.getAttachmentInfo();
        assertEquals(1123344555, attachmentInfo.getId());

        List<Star> stars = pullRequestComment.getStars();
        assertEquals(1, stars.size());

        List<Notification> notifications = pullRequestComment.getNotifications();
        assertEquals(0, notifications.size());

        User user = pullRequestComment.getCreatedUser();
        assertEquals(11111111, user.getId());

        Calendar calendar = Calendar.getInstance();
        calendar.set(2015, Calendar.JULY, 16, 0, 0, 0);
        assertThat(calendar.getTime(), DateMatchers.sameDay(pullRequestComment.getCreated()));

//        User updateUser = pullRequestComment.getUpdatedUser();
//        assertEquals(2, updateUser.getId());

        calendar = Calendar.getInstance();
        calendar.set(2015, Calendar.JULY, 16, 0, 0, 0);
        assertThat(calendar.getTime(), DateMatchers.sameDay(pullRequestComment.getUpdated()));

    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/PullRequestQueryParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.PullRequest;
import com.nulabinc.backlog4j.http.NameValuePair;

import java.util.List;

/**
 * Parameters for pull request query.
 *
 * @author nulab-inc
 */
public class PullRequestQueryParams extends QueryParams {

    public PullRequestQueryParams statusType(List<PullRequest.StatusType> statusType) {
        for (PullRequest.StatusType type : statusType) {
            parameters.add(new NameValuePair("statusId[]", String.valueOf(type.getIntValue())));
        }
        return this;
    }

    public PullRequestQueryParams assigneeIds(List assigneeIds) {
        for (Object assigneeId : assigneeIds) {
            parameters.add(new NameValuePair("assigneeId[]", assigneeId.toString()));
        }
        return this;
    }

    public PullRequestQueryParams issueIds(List issueIds) {
        for (Object issueId : issueIds) {
            parameters.add(new NameValuePair("issueId[]", issueId.toString()));
        }
        return this;
    }

    public PullRequestQueryParams createdUserIds(List createdUserIds) {
        for (Object createdUserId : createdUserIds) {
            parameters.add(new NameValuePair("createdUserId[]", createdUserId.toString()));
        }
        return this;
    }

    @Override
    public PullRequestQueryParams minId(Object minId) {
        return (PullRequestQueryParams) super.minId(minId);
    }

    @Override
    public PullRequestQueryParams maxId(Object maxId) {
        return (PullRequestQueryParams) super.maxId(maxId);
    }

    @Override
    public PullRequestQueryParams count(long count) {
        return (PullRequestQueryParams) super.count(count);
    }

    @Override
    public PullRequestQueryParams count(int count) {
        return (PullRequestQueryParams) super.count(count);
    }

    @Override
    public PullRequestQueryParams order(Order order) {
        return (PullRequestQueryParams) super.order(order);
    }

    public PullRequestQueryParams offset(long offset) {
        parameters.add(new NameValuePair("offset", String.valueOf(offset)));
        return this;
    }

    public PullRequestQueryParams offset(int offset) {
        parameters.add(new NameValuePair("offset", String.valueOf(offset)));
        return this;
    }
}
<file_sep>/src/test/java/com/nulabinc/backlog4j/internal/json/ActivityJSONImplTest.java
package com.nulabinc.backlog4j.internal.json;

import com.nulabinc.backlog4j.Activity;
import com.nulabinc.backlog4j.Attachment;
import com.nulabinc.backlog4j.Change;
import com.nulabinc.backlog4j.Comment;
import com.nulabinc.backlog4j.GroupProjectActivity;
import com.nulabinc.backlog4j.Project;
import com.nulabinc.backlog4j.ResponseList;
import com.nulabinc.backlog4j.User;
import com.nulabinc.backlog4j.internal.json.activities.IssueCommentedActivity;
import com.nulabinc.backlog4j.internal.json.activities.IssueCommentedContent;
import com.nulabinc.backlog4j.internal.json.activities.IssueCreatedActivity;
import com.nulabinc.backlog4j.internal.json.activities.IssueCreatedContent;
import com.nulabinc.backlog4j.internal.json.activities.IssueUpdatedActivity;
import com.nulabinc.backlog4j.internal.json.activities.IssueUpdatedContent;
import com.nulabinc.backlog4j.internal.json.activities.ProjectUserAddedActivity;
import com.nulabinc.backlog4j.internal.json.activities.ProjectUserAddedContent;
import com.nulabinc.backlog4j.internal.json.activities.ProjectUserRemovedActivity;
import com.nulabinc.backlog4j.internal.json.activities.ProjectUserRemovedContent;
import com.nulabinc.backlog4j.internal.json.activities.PullRequestAddedActivity;
import com.nulabinc.backlog4j.internal.json.activities.PullRequestCommentedActivity;
import com.nulabinc.backlog4j.internal.json.activities.PullRequestContent;
import com.nulabinc.backlog4j.internal.json.activities.PullRequestUpdatedActivity;
import com.nulabinc.backlog4j.internal.json.activities.WikiCreatedActivity;
import com.nulabinc.backlog4j.internal.json.activities.WikiCreatedContent;
import com.nulabinc.backlog4j.internal.json.activities.WikiUpdatedActivity;
import com.nulabinc.backlog4j.internal.json.activities.WikiUpdatedContent;
import org.exparity.hamcrest.date.DateMatchers;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.util.Calendar;
import java.util.List;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
 * Created by yuh kim on 2014/07/23.
 */
public class ActivityJSONImplTest extends AbstractJSONImplTest {

    @Test
    public void createActivityIssueCreatedTest() throws IOException {
        String fileContentStr = getJsonString("json/activity_issue_created.json");
        Activity activity = factory.createActivity(fileContentStr);
        System.out.println(activity.toString());

        assertEquals(Activity.Type.IssueCreated, activity.getType());

        IssueCreatedActivity issueCreated = (IssueCreatedActivity) activity;

        assertEquals(624929, issueCreated.getId());

        Project project = issueCreated.getProject();
        assertEquals(1073761031, project.getId());
        assertEquals("TEST_PRJ1407208757143", project.getProjectKey());
        assertEquals("テストプロジェクト", project.getName());
        assertFalse(project.isChartEnabled());
        assertTrue(project.isSubtaskingEnabled());
        assertEquals(Project.TextFormattingRule.Backlog, project.getTextFormattingRule());
        assertFalse(project.isArchived());

        IssueCreatedContent content = issueCreated.getContent();
        assertEquals(1073803919, content.getId());
        assertEquals(1, content.getKeyId());
        assertEquals("課題作成", content.getSummary());
        assertEquals("テスト", content.getDescription());

        User user = issueCreated.getCreatedUser();
        assertEquals(1073751781, user.getId());
        assertEquals("test_admin", user.getUserId());
        assertEquals("あどみにさん", user.getName());
        assertEquals(User.RoleType.Admin, user.getRoleType());
        assertNull(user.getLang());
        assertEquals("<EMAIL>", user.getMailAddress());

        Calendar calendar = Calendar.getInstance();
        calendar.set(2014, Calendar.AUGUST, 5, 5, 50, 59);
        assertThat(calendar.getTime(), DateMatchers.sameDay(issueCreated.getCreated()));
    }

    @Test
    public void createActivityIssueUpdatedTest() throws IOException {
        String fileContentStr = getJsonString("json/activity_issue_updated.json");
        Activity activity = factory.createActivity(fileContentStr);
        System.out.println(activity.toString());

        assertEquals(Activity.Type.IssueUpdated, activity.getType());

        IssueUpdatedActivity issueUpdated = (IssueUpdatedActivity) activity;

        assertEquals(39293172, issueUpdated.getId());


        IssueUpdatedContent content = issueUpdated.getContent();
        assertEquals(1079097008, content.getId());
        assertEquals(1, content.getKeyId());
        assertEquals("課題追加テスト", content.getSummary());
        assertEquals("", content.getDescription());

        Comment comment = content.getComment();
        assertEquals(1102443987, comment.getId());
        assertEquals("コメント時ファイル添付テスト", comment.getContent());

        List<Attachment> attachments = content.getAttachments();
        assert attachments.size() > 0;
        Attachment attachment = attachments.get(0);
        assertEquals(1076499483, attachment.getId());
        assertEquals(attachment.getName(), "twoshot.JPG");
        assertEquals(86513, attachment.getSize());

        List<Change> changes = content.getChanges();
        assert changes.size() > 0;
        Change change = changes.get(0);
        assertEquals("attachment", change.getField());
        assertEquals("twoshot.JPG", change.getNewValue());
        assertEquals("", change.getOldValue());
        assertEquals("standard", change.getType());

    }

    @Test
    public void createActivityIssueCommentedTest() throws IOException {
        String fileContentStr = getJsonString("json/activity_issue_commented.json");
        Activity activity = factory.createActivity(fileContentStr);
        System.out.println(activity.toString());


        assertEquals(Activity.Type.IssueCommented, activity.getType());

        IssueCommentedActivity issueCommented = (IssueCommentedActivity) activity;

        assertEquals(39261460, issueCommented.getId());

        IssueCommentedContent content = issueCommented.getContent();
        assertEquals(1079097008, content.getId());
        assertEquals(1, content.getKeyId());
        assertEquals("課題追加テスト", content.getSummary());
        assertEquals("", content.getDescription());
        Comment comment = content.getComment();
        assertEquals(1102419188, comment.getId());
        assertEquals("コメント時ファイル添付テスト", comment.getContent());

    }

    @Test
    public void createActivityWikiCreatedTest() throws IOException {
        String fileContentStr = getJsonString("json/activity_wiki_created.json");
        Activity activity = factory.createActivity(fileContentStr);
        System.out.println(activity.toString());

        assertEquals(Activity.Type.WikiCreated, activity.getType());
        WikiCreatedActivity wikiCreated = (WikiCreatedActivity) activity;

        assertEquals(624763, wikiCreated.getId());
        WikiCreatedContent content = wikiCreated.getContent();
        assertEquals(1073768599, content.getId());
        assertEquals("テスト", content.getName());
        assertEquals("ああああ", content.getContent());


    }

    @Test
    public void createActivityWikiUpdatedTest() throws IOException {
        String fileContentStr = getJsonString("json/activity_wiki_updated.json");
        Activity activity = factory.createActivity(fileContentStr);
        System.out.println(activity.toString());

        assertEquals(Activity.Type.WikiUpdated, activity.getType());
        WikiUpdatedActivity wikiUpdated = (WikiUpdatedActivity) activity;

        assertEquals(624764, wikiUpdated.getId());
        WikiUpdatedContent content = wikiUpdated.getContent();
        assertEquals(1073768599, content.getId());
        assertEquals("テスト", content.getName());
        assertEquals("ああああ", content.getContent());
        List<Attachment> attachments = content.getAttachments();
        assertEquals(18105, attachments.get(0).getId());
        assertEquals("backlog_icon.png", attachments.get(0).getName());
        assertEquals(5050, attachments.get(0).getSize());
    }

    @Test
    public void createActivityProjectUserAddedTest() throws IOException {
        String fileContentStr = getJsonString("json/activity_project_user_added.json");
        Activity activity = factory.createActivity(fileContentStr);
        System.out.println(activity.toString());

        assertEquals(Activity.Type.ProjectUserAdded, activity.getType());
        ProjectUserAddedActivity projectUserAdded = (ProjectUserAddedActivity) activity;

        assertEquals(623680, projectUserAdded.getId());

        ProjectUserAddedContent content = projectUserAdded.getContent();
        List<User> users = content.getUsers();
        assertEquals(1073751782, users.get(0).getId());

        List<GroupProjectActivity> groupProjectActivities = content.getGroupProjectActivities();
        assertEquals(623680, groupProjectActivities.get(0).getId());
        assertEquals(Activity.Type.ProjectUserAdded, groupProjectActivities.get(0).getType());


    }

    @Test
    public void createActivityProjectUserRemovedTest() throws IOException {
        String fileContentStr = getJsonString("json/activity_project_user_removed.json");
        Activity activity = factory.createActivity(fileContentStr);
        System.out.println(activity.toString());

        assertEquals(Activity.Type.ProjectUserRemoved, activity.getType());
        ProjectUserRemovedActivity projectUserRemoved = (ProjectUserRemovedActivity) activity;

        assertEquals(623559, projectUserRemoved.getId());


        ProjectUserRemovedContent content = projectUserRemoved.getContent();
        List<User> users = content.getUsers();
        assertEquals(1073936985, users.get(0).getId());
        assertEquals("test_admin", users.get(0).getUserId());
        assertEquals(1073936985, users.get(0).getId());

        List<GroupProjectActivity> groupProjectActivities = content.getGroupProjectActivities();
        assertEquals(623554, groupProjectActivities.get(0).getId());
        assertEquals(Activity.Type.ProjectUserRemoved, groupProjectActivities.get(0).getType());

        assertEquals("よろしく", content.getComment());
    }

    @Test
    public void createActivityPullRequestAddedTest() throws IOException {
        String fileContentStr = getJsonString("json/activity_pull_request_added.json");
        Activity activity = factory.createActivity(fileContentStr);
        System.out.println(activity.toString());

        assertEquals(Activity.Type.PullRequestAdded, activity.getType());
        PullRequestAddedActivity pullRequestAdded = (PullRequestAddedActivity) activity;

        assertEquals(623559, pullRequestAdded.getId());


        PullRequestContent content = pullRequestAdded.getContent();
        assertEquals(148, content.getId());
        assertEquals(112, content.getNumber());
        assertEquals("アイコンとトグルのデザイン最適化", content.getSummary());
        assertEquals("マージおねがいします！", content.getDescription());

        assertNull(content.getComment());
        assertTrue(content.getChanges().isEmpty());
        assertEquals(77777777, content.getIssue().getId());
    }

    @Test
    public void createActivityPullRequestUpdatedTest() throws IOException {
        String fileContentStr = getJsonString("json/activity_pull_request_updated.json");
        Activity activity = factory.createActivity(fileContentStr);
        System.out.println(activity.toString());

        assertEquals(Activity.Type.PullRequestUpdated, activity.getType());
        PullRequestUpdatedActivity pullRequestUpdated = (PullRequestUpdatedActivity) activity;

        assertEquals(121212, pullRequestUpdated.getId());


        PullRequestContent content = pullRequestUpdated.getContent();
        assertEquals(148, content.getId());
        assertEquals(112, content.getNumber());
        assertEquals("デザイン最適化", content.getSummary());
        assertEquals("マージおねがいします！", content.getDescription());

        assertEquals(579, content.getComment().getId());
        assertEquals("", content.getComment().getContent());

        assertEquals("status", content.getChanges().get(0).getField());
        assertEquals("3", content.getChanges().get(0).getNewValue());
        assertEquals("1", content.getChanges().get(0).getOldValue());

    }

    @Test
    public void createActivityPullRequestCommentedTest() throws IOException {
        String fileContentStr = getJsonString("json/activity_pull_request_commented.json");
        Activity activity = factory.createActivity(fileContentStr);
        System.out.println(activity.toString());

        assertEquals(Activity.Type.PullRequestCommented, activity.getType());
        PullRequestCommentedActivity pullRequestCommented = (PullRequestCommentedActivity) activity;

        assertEquals(3333333, pullRequestCommented.getId());


        PullRequestContent content = pullRequestCommented.getContent();
        assertEquals(146, content.getId());
        assertEquals(111, content.getNumber());
        assertEquals("統合", content.getSummary());
        assertEquals("レビューおねがいします", content.getDescription());

        assertEquals(576, content.getComment().getId());
        assertEquals("マージしやしたー", content.getComment().getContent());

        assertEquals(0, content.getChanges().size());

    }

    @Test
    public void createActivityListTest() throws IOException {
        String fileContentStr = getJsonString("json/activities.json");
        ResponseList<Activity> list = factory.createActivityList(fileContentStr);
        System.out.println(list.toString());
    }


    @Test
    public void equalsTest() throws IOException {
        String fileContentStr = getJsonString("json/activity_issue_created.json");
        Activity activity1 = factory.createActivity(fileContentStr);
        Activity activity2 = factory.createActivity(fileContentStr);
        assertEquals(activity1, activity2);

    }

    @Test
    public void hashCodeTest() throws IOException {
        String fileContentStr = getJsonString("json/activity_issue_created.json");
        Activity activity1 = factory.createActivity(fileContentStr);
        Activity activity2 = factory.createActivity(fileContentStr);

        assertEquals(activity1.hashCode(), activity2.hashCode());

    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/Group.java
package com.nulabinc.backlog4j;

import java.util.Date;
import java.util.List;

/**
 * The interface for Backlog group data.
 *
 * @author nulab-inc
 */
public interface Group {

    long getId();

    String getIdAsString();

    String getName();

    List<User> getMembers();

    long getDisplayOrder();

    User getCreatedUser();

    Date getCreated();

    User getUpdatedUser();

    Date getUpdated();

}
<file_sep>/src/main/java/com/nulabinc/backlog4j/BacklogClientBase.java
package com.nulabinc.backlog4j;

import com.nulabinc.backlog4j.api.option.*;
import com.nulabinc.backlog4j.auth.AccessToken;
import com.nulabinc.backlog4j.auth.OAuthSupport;
import com.nulabinc.backlog4j.conf.BacklogConfigure;
import com.nulabinc.backlog4j.http.BacklogHttpClient;
import com.nulabinc.backlog4j.http.BacklogHttpClientImpl;
import com.nulabinc.backlog4j.http.BacklogHttpResponse;
import com.nulabinc.backlog4j.http.NameValuePair;
import com.nulabinc.backlog4j.internal.InternalFactory;
import com.nulabinc.backlog4j.internal.json.InternalFactoryJSONImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Base of BacklogClient.
 *
 * @author nulab-inc
 */
public abstract class BacklogClientBase{

    private final Logger logger = LoggerFactory.getLogger(BacklogClientBase.class);

    protected BacklogHttpClient httpClient;
    protected BacklogConfigure configure;
    protected InternalFactory factory = new InternalFactoryJSONImpl();
    protected OAuthSupport oAuthSupport;
    protected BacklogEndPointSupport backlogEndPointSupport;

    public BacklogClientBase(BacklogConfigure configure) {
        this.configure = configure;
        this.httpClient = new BacklogHttpClientImpl();
        this.backlogEndPointSupport = new BacklogEndPointSupport(configure);
        configureHttpClient();
    }

    public BacklogClientBase(BacklogConfigure configure, BacklogHttpClient httpClient) {
        this.configure = configure;
        this.httpClient = httpClient;
        this.backlogEndPointSupport = new BacklogEndPointSupport(configure);
        configureHttpClient();
    }

    public void setOAuthSupport(OAuthSupport oAuthSupport) {
        this.oAuthSupport = oAuthSupport;
    }

    private void configureHttpClient() {

        if (this.configure.getApiKey() != null) {
            httpClient.setApiKey(this.configure.getApiKey());
        } else if (this.configure.getAccessToken() != null) {
            httpClient.setBearerToken(this.configure.getAccessToken().getToken());
        } else {
            throw new BacklogAPIException("ApiKey or AccessToken must not be null");
        }

        httpClient.setReadTimeout(this.configure.getReadTimeout());
        httpClient.setConnectionTimeout(this.configure.getConnectionTimeout());
    }

    protected BacklogHttpResponse get(String endpoint) throws BacklogException {
        return this.get(endpoint, null, null);
    }

    protected BacklogHttpResponse get(String endpoint, GetParams getParams) throws BacklogException {
        return this.get(endpoint, getParams, null);
    }

    protected BacklogHttpResponse get(String endpoint, QueryParams queryParams) throws BacklogException {
        return this.get(endpoint, null, queryParams);
    }

    protected BacklogHttpResponse get(String endpoint, GetParams getParams, QueryParams queryParams) throws BacklogException {
        BacklogHttpResponse ires = httpClient.get(endpoint, getParams, queryParams);
        if (needTokenRefresh(ires)) {
            refreshToken();
            ires = httpClient.get(endpoint, getParams, queryParams);
        }
        loggingResponse(endpoint, ires);
        checkError(ires);
        return ires;
    }

    protected BacklogHttpResponse post(String endpoint) throws BacklogException {
        return this.post(endpoint, new ArrayList<NameValuePair>(), new ArrayList<NameValuePair>());
    }

    protected BacklogHttpResponse post(String endpoint, PostParams postParams) throws BacklogException {
        return this.post(endpoint, postParams.getParamList(), new ArrayList<NameValuePair>());
    }

    protected BacklogHttpResponse post(String endpoint, List<NameValuePair> parameters, List<NameValuePair> headers) throws BacklogException {
        BacklogHttpResponse ires = httpClient.post(endpoint, parameters, headers);
        if (needTokenRefresh(ires)) {
            refreshToken();
            ires = httpClient.post(endpoint, parameters, headers);
        }
        loggingResponse(endpoint, ires);
        checkError(ires);
        return ires;
    }

    protected BacklogHttpResponse patch(String endpoint, PatchParams postParams) throws BacklogException {
        return this.patch(endpoint, postParams.getParamList(), new ArrayList<NameValuePair>());
    }

    protected BacklogHttpResponse patch(String endpoint, List<NameValuePair> parameters, List<NameValuePair> headers) throws BacklogException {
        BacklogHttpResponse ires = httpClient.patch(endpoint, parameters, headers);
        if (needTokenRefresh(ires)) {
            refreshToken();
            ires = httpClient.patch(endpoint, parameters, headers);
        }
        loggingResponse(endpoint, ires);
        checkError(ires);
        return ires;
    }

    protected BacklogHttpResponse put(String endpoint, List<NameValuePair> parameters) throws BacklogException {
        BacklogHttpResponse ires = httpClient.put(endpoint, parameters);
        if (needTokenRefresh(ires)) {
            refreshToken();
            ires = httpClient.put(endpoint, parameters);
        }
        loggingResponse(endpoint, ires);
        checkError(ires);
        return ires;
    }

    protected BacklogHttpResponse delete(String endpoint) throws BacklogException {
        return this.delete(endpoint, new ArrayList<NameValuePair>());
    }

    protected BacklogHttpResponse delete(String endpoint, DeleteParams deleteParams) throws BacklogException {
        return this.delete(endpoint, deleteParams.getParamList());
    }

    protected BacklogHttpResponse delete(String endpoint, NameValuePair param) throws BacklogException {
        List<NameValuePair> params = new ArrayList<NameValuePair>();
        if (param != null) {
            params.add(param);
        }
        return this.delete(endpoint, params);
    }

    protected BacklogHttpResponse delete(String endpoint, List<NameValuePair> parameters) throws BacklogException {
        BacklogHttpResponse ires = httpClient.delete(endpoint, parameters);
        if (needTokenRefresh(ires)) {
            refreshToken();
            ires = httpClient.delete(endpoint, parameters);
        }
        loggingResponse(endpoint, ires);
        checkError(ires);
        return ires;
    }

    protected BacklogHttpResponse postMultiPart(String endpoint, Map<String, Object> parameters) throws BacklogException {
        BacklogHttpResponse ires = httpClient.postMultiPart(endpoint, parameters);
        if (needTokenRefresh(ires)) {
            refreshToken();
            ires = httpClient.postMultiPart(endpoint, parameters);
        }
        loggingResponse(endpoint, ires);
        checkError(ires);
        return ires;
    }

    private void loggingResponse(String endpoint, BacklogHttpResponse ires) {
        logger.info("status code:" + ires.getStatusCode() + " url:" + endpoint);
    }

    private String getRateLimitResetFormatedDate(BacklogHttpResponse ires) {
        if (ires.getRateLimitResetDate() == null) {
            return "";
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        return sdf.format(ires.getRateLimitResetDate());
    }

    protected String buildEndpoint(String connection) {
        StringBuilder url = new StringBuilder()
                .append(configure.getRestBaseURL())
                .append(connection == null ? "" : "/" + connection);
        return url.toString();
    }


    private void checkError(BacklogHttpResponse ires) {
        if (ires.getStatusCode() != 200 &&
                ires.getStatusCode() != 201 &&
                ires.getStatusCode() != 202 &&
                ires.getStatusCode() != 203 &&
                ires.getStatusCode() != 204) {
            if (ires.getStatusCode() == 429) {
                throw new BacklogAPIException("The API usage limit has been exceeded, and will be available again from " + getRateLimitResetFormatedDate(ires) + ".", ires);
            } else {
                throw new BacklogAPIException("backlog api request failed.", ires);
            }
        }
    }

    private boolean needTokenRefresh(BacklogHttpResponse ires) {
        return (ires.getStatusCode() == 401 ||
                ires.getStatusCode() == 0) && // for android bug
                configure.getApiKey() == null &&
                configure.getAccessToken() != null;
    }

    private void refreshToken() {
        AccessToken accessToken = oAuthSupport.refreshOAuthAccessToken();
        configure.accessToken(accessToken);
        configureHttpClient();
    }

}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/UpdateStatusParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.Project;
import com.nulabinc.backlog4j.http.NameValuePair;

/**
 * Parameters for update status API.
 *
 * @author nulab-inc
 */
public class UpdateStatusParams extends PatchParams {

    private Object projectIdOrKey;
    private Object statusId;

    public UpdateStatusParams(Object projectIdOrKey, Object statusId, String name, Project.CustomStatusColor color) {
        this.projectIdOrKey = projectIdOrKey;
        this.statusId = statusId;
        parameters.add(new NameValuePair("name", name));
        parameters.add(new NameValuePair("color", color.getStrValue()));
    }


    public String getProjectIdOrKeyString() {
        return projectIdOrKey.toString();
    }

    public String getStatusId() {
        return statusId.toString();
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/Activity.java
package com.nulabinc.backlog4j;

import java.util.Date;

/**
 * The interface for Backlog activity data.
 *
 * @author nulab-inc
 */
public interface Activity {

    enum Type {
        Undefined(-1), IssueCreated(1), IssueUpdated(2), IssueCommented(3), IssueDeleted(4),
        WikiCreated(5), WikiUpdated(6), WikiDeleted(7),
        FileAdded(8), FileUpdated(9), FileDeleted(10),
        SvnCommitted(11), GitPushed(12), GitRepositoryCreated(13),
        IssueMultiUpdated(14), ProjectUserAdded(15), ProjectUserRemoved(16), NotifyAdded(17),
        PullRequestAdded(18), PullRequestUpdated(19), PullRequestCommented(20), PullRequestMerged(21);

        Type(int intValue) {
            this.intValue = intValue;
        }

        public int getIntValue() {
            return intValue;
        }

        public static Type valueOf(final int anIntValue) {
            for (Type d : values()) {
                if (d.getIntValue() == anIntValue) {
                    return d;
                }
            }
            return Undefined;
        }

        private int intValue;
    }

    long getId();

    String getIdAsString();

    Project getProject();

    Type getType();

    Content getContent();

    User getCreatedUser();

    Date getCreated();
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/internal/json/ProjectWithVCSJSONImpl.java
package com.nulabinc.backlog4j.internal.json;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.nulabinc.backlog4j.ProjectWithVCS;
import org.apache.commons.lang3.builder.ToStringBuilder;

@JsonIgnoreProperties(ignoreUnknown = true)
public class ProjectWithVCSJSONImpl extends ProjectJSONImpl implements ProjectWithVCS {
    private boolean useSubversion;
    private boolean useGit;

    @Override
    public boolean getUseSubversion() { return useSubversion; }

    @Override
    public boolean getUseGit() { return useGit; }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        if (obj.getClass() != getClass()) {
            return false;
        }
        ProjectWithVCSJSONImpl rhs = (ProjectWithVCSJSONImpl) obj;
        return super.createEqualsBuilder(rhs)
                .append(this.useSubversion, rhs.useSubversion)
                .append(this.useGit, rhs.useGit)
                .isEquals();
    }

    @Override
    public int hashCode() {
        return super.createHashCodeBuilder()
                .append(useSubversion)
                .append(useGit)
                .toHashCode();
    }

    @Override
    public String toString() {
        return super.createToStringBuilder()
                .append("useSubversion", useSubversion)
                .append("useGit", useGit)
                .toString();
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/auth/BacklogOAuthSupport.java
package com.nulabinc.backlog4j.auth;


import com.nulabinc.backlog4j.BacklogAPIException;
import com.nulabinc.backlog4j.BacklogAuthException;
import com.nulabinc.backlog4j.BacklogException;
import com.nulabinc.backlog4j.conf.BacklogConfigure;
import com.nulabinc.backlog4j.http.BacklogHttpClient;
import com.nulabinc.backlog4j.http.BacklogHttpClientImpl;
import com.nulabinc.backlog4j.http.BacklogHttpResponse;
import com.nulabinc.backlog4j.http.NameValuePair;
import com.nulabinc.backlog4j.internal.InternalFactory;
import com.nulabinc.backlog4j.internal.json.InternalFactoryJSONImpl;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

/**
 * @author nulab-inc
 */
public class BacklogOAuthSupport implements OAuthSupport {

    private String clientId;
    private String clientSecret;
    private String redirectUrl;

    private BacklogConfigure configure;
    private BacklogHttpClient httpClient;
    private InternalFactory factory = new InternalFactoryJSONImpl();
    private OnAccessTokenRefreshListener listener;

    public BacklogOAuthSupport(BacklogConfigure configure) {
        this.configure = configure;
        this.httpClient = new BacklogHttpClientImpl();
        init();
    }

    public BacklogOAuthSupport(BacklogConfigure configure, BacklogHttpClient httpClient) {
        this.configure = configure;
        this.httpClient = httpClient;
        init();
    }

    private void init() {
        httpClient.setReadTimeout(this.configure.getReadTimeout());
        httpClient.setConnectionTimeout(this.configure.getConnectionTimeout());
    }

    public void setOAuthClientId(String clientId, String clientSecret) {
        this.clientId = clientId;
        this.clientSecret = clientSecret;
    }
    @Override
    public void setOAuthRedirectUrl(String redirectUrl) {
        this.redirectUrl = redirectUrl;
    }

    @Override
    public String getOAuthAuthorizationURL() throws BacklogException {
        String url = configure.getOAuthAuthorizationURL() +
                "?client_id=" + this.clientId +
                "&response_type=" + "code";
        if (redirectUrl != null) {
            try {
                url += "&redirect_uri=" + URLEncoder.encode(this.redirectUrl, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                throw new BacklogAPIException(e);
            }
        }
        return url;
    }

    @Override
    public AccessToken getOAuthAccessToken(String oauthCode) throws BacklogException {
        if (oauthCode == null) {
            throw new IllegalArgumentException("oauthCode must not be null");
        }
        BacklogHttpResponse httpResponse = getAccessTokenResponse(oauthCode);
        checkError(httpResponse);
        return factory.createAccessToken(httpResponse);
    }

    @Override
    public AccessToken refreshOAuthAccessToken() throws BacklogException {
        if (configure.getAccessToken() == null) {
            throw new IllegalArgumentException("AccessToken must not be null");
        }
        BacklogHttpResponse httpResponse = getRefreshTokenResponse();
        checkError(httpResponse);
        AccessToken accessToken =  factory.createAccessToken(httpResponse);
        configure.accessToken(accessToken);
        listener.onAccessTokenRefresh(accessToken);
        return accessToken;
    }

    @Override
    public void setOnAccessTokenRefreshListener(OnAccessTokenRefreshListener listener) {
        this.listener = listener;
    }

    private BacklogHttpResponse getAccessTokenResponse(String oauthCode) throws BacklogException {
        List<NameValuePair> params = new ArrayList<NameValuePair>();
        params.add(new NameValuePair("code", oauthCode));
        params.add(new NameValuePair("client_id", clientId));
        params.add(new NameValuePair("client_secret", clientSecret));
        params.add(new NameValuePair("redirect_uri", redirectUrl));
        params.add(new NameValuePair("grant_type", "authorization_code"));
        BacklogHttpResponse ires = httpClient.post(configure.getOAuthAccessTokenURL(), params, new ArrayList<NameValuePair>());
        checkError(ires);
        return ires;

    }

    private BacklogHttpResponse getRefreshTokenResponse() throws BacklogException {
        List<NameValuePair> params = new ArrayList<NameValuePair>();
        params.add(new NameValuePair("client_id", clientId));
        params.add(new NameValuePair("client_secret", clientSecret));
        params.add(new NameValuePair("refresh_token", configure.getAccessToken().getRefresh()));
        params.add(new NameValuePair("grant_type", "refresh_token"));
        BacklogHttpResponse ires = httpClient.post(configure.getOAuthAccessTokenURL(), params, new ArrayList<NameValuePair>());
        checkError(ires);
        return ires;
    }

    private void checkError(BacklogHttpResponse ires) {
        if (ires.getStatusCode() != 200 &&
                ires.getStatusCode() != 201) {
            throw new BacklogAuthException("backlog oauth request failed.", ires);
        }
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/PostedAttachment.java
package com.nulabinc.backlog4j;

/**
 * The interface for Backlog posted attachment file.
 *
 * @author nulab-inc
 */
public interface PostedAttachment {

    long getId();

    String getIdAsString();

    String getName();

    long getSize();

}
<file_sep>/src/main/java/com/nulabinc/backlog4j/ResponseList.java
package com.nulabinc.backlog4j;

import java.util.List;

/**
 * The interface for response list.
 *
 * @author nulab-inc
 */
public interface ResponseList<T> extends List<T> {
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/UpdateTextCustomFieldParams.java
package com.nulabinc.backlog4j.api.option;

/**
 * Parameters for update text type custom field API.
 *
 * @author nulab-inc
 */
public class UpdateTextCustomFieldParams extends UpdateCustomFieldParams {

    public UpdateTextCustomFieldParams(Object projectIdOrKey, long customFiledId) {
        super(projectIdOrKey, customFiledId);
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/UpdateTextAreaCustomFieldParams.java
package com.nulabinc.backlog4j.api.option;

/**
 * Parameters for update text area type custom field API.
 *
 * @author nulab-inc
 */
public class UpdateTextAreaCustomFieldParams extends UpdateCustomFieldParams {

    public UpdateTextAreaCustomFieldParams(Object projectIdOrKey, long customFiledId) {
        super(projectIdOrKey, customFiledId);
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/ViewedProject.java
package com.nulabinc.backlog4j;

import java.util.Date;

/**
 * The interface for Backlog viewed project data.
 *
 * @author nulab-inc
 */
public interface ViewedProject {
    Project getProject();

    Date getUpdated();
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/UpdateIssueCommentParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.http.NameValuePair;

/**
 * Parameters for update issue comment API.
 *
 * @author nulab-inc
 */
public class UpdateIssueCommentParams extends PatchParams {
    private Object issueIdOrKey;
    private Object commentId;

    public UpdateIssueCommentParams(Object issueIdOrKey, Object commentId, String content) {
        this.issueIdOrKey = issueIdOrKey;
        this.commentId = commentId;
        parameters.add(new NameValuePair("content", content));
    }

    public String getCommentId() {
        return commentId.toString();
    }

    public String getIssueIdOrKeyString() {
        return issueIdOrKey.toString();
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/UpdateGroupParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.http.NameValuePair;

import java.util.List;

/**
 * Parameters for update group API.
 *
 * @author nulab-inc
 */
public class UpdateGroupParams extends PatchParams {

    private String groupId;

    public UpdateGroupParams(Object groupId){
        this.groupId = groupId.toString();
    }


    public UpdateGroupParams name(String name) {
        parameters.add(new NameValuePair("name", name));
        return this;
    }

    public UpdateGroupParams members(List members) {
        for (Object member : members) {
            parameters.add(new NameValuePair("members[]", member.toString()));
        }
        return this;
    }

    public String getGroupId() {
        return groupId;
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/AddCategoryParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.http.NameValuePair;

/**
 * Parameters for add category API.
 *
 * @author nulab-inc
 */
public class AddCategoryParams extends PostParams {

    private Object projectIdOrKey;

    /**
     * Constructor
     *
     * @param projectIdOrKey    the project identifier
     * @param name              the category name
     */
    public AddCategoryParams(Object projectIdOrKey, String name) {
        this.projectIdOrKey = projectIdOrKey;
        parameters.add(new NameValuePair("name", name));
    }

    /**
     * Returns the project identifier string.
     *
     * @return project id or project key
     */
    public String getProjectIdOrKeyString() {
        return projectIdOrKey.toString();
    }
}
<file_sep>/src/test/java/com/nulabinc/backlog4j/internal/json/UserJSONImplTest.java
package com.nulabinc.backlog4j.internal.json;

import com.nulabinc.backlog4j.ResponseList;
import com.nulabinc.backlog4j.User;
import org.exparity.hamcrest.date.DateMatchers;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;

/**
 * @author nulab-inc
 */
public class UserJSONImplTest extends AbstractJSONImplTest {
    @Test
    public void createUserListTest() throws IOException {
        String fileContentStr = getJsonString("json/users.json");
        ResponseList<User> users = factory.createUserList(fileContentStr);

        assertEquals(2, users.size());

        User user = users.get(0);
        assertEquals(1073910170, user.getId());
        assertEquals("test1", user.getName());
        assertEquals(User.RoleType.Admin, user.getRoleType());
        assertNull(user.getLang());
        assertEquals("<EMAIL>", user.getMailAddress());

        Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
        calendar.set(2022, Calendar.SEPTEMBER, 7, 11, 33, 45);
        calendar.set(Calendar.MILLISECOND, 0);
        assertEquals(calendar.getTime(), user.getLastLoginTime());

        user = users.get(1);
        assertEquals(1073910171, user.getId());
        assertEquals("test2", user.getName());
        assertEquals(User.RoleType.User, user.getRoleType());
        assertEquals("ja", user.getLang());
        assertEquals("<EMAIL>", user.getMailAddress());
        assertNull(user.getLastLoginTime());
    }

    @Test
    public void createUserTest() throws IOException {
        String fileContentStr = getJsonString("json/user.json");
        User user = factory.createUser(fileContentStr);

        assertEquals(1073936936, user.getId());
        assertEquals("test", user.getName());
        assertEquals(User.RoleType.Admin, user.getRoleType());
        assertNull(user.getLang());
        assertEquals("<EMAIL>", user.getMailAddress());
    }

    @Test
    public void equalsTest() throws IOException {
        String fileContentStr = getJsonString("json/user.json");
        User user1 = factory.createUser(fileContentStr);
        User user2 = factory.createUser(fileContentStr);
        assertEquals(user1, user2);

    }

    @Test
    public void hashCodeTest() throws IOException {
        String fileContentStr = getJsonString("json/user.json");
        User user1 = factory.createUser(fileContentStr);
        User user2 = factory.createUser(fileContentStr);
        assertEquals(user1.hashCode(), user2.hashCode());

    }
}
<file_sep>/src/test/java/com/nulabinc/backlog4j/api/option/UpdateWebhookParamsTest.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.Activity;
import com.nulabinc.backlog4j.http.NameValuePair;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
 * @author nulab-inc
 */
public class UpdateWebhookParamsTest extends AbstractParamsTest {
    @Test
    public void createParamTest() {

        // when
        long projectId = 1000001L;
        long webhookId = 1L;
        String name = "name";
        String hookUrl = "hookurl";
        UpdateWebhookParams params = new UpdateWebhookParams(projectId, webhookId);
        params.name(name)
                .hookUrl(hookUrl)
                .description("description")
                .activityTypeIds(Arrays.asList(Activity.Type.IssueCreated, Activity.Type.IssueUpdated));

        // then
        List<NameValuePair> parameters = params.getParamList();
        assertEquals(5, parameters.size());
        assertTrue(existsOneKeyValue(parameters, "name", name));
        assertTrue(existsOneKeyValue(parameters, "hookUrl", hookUrl));
        assertTrue(existsOneKeyValue(parameters, "description", "description"));
        assertTrue(existsOneKeyValue(parameters, "activityTypeIds[]", "1"));
        assertTrue(existsOneKeyValue(parameters, "activityTypeIds[]", "2"));
        assertEquals(String.valueOf(projectId), params.getProjectIdOrKeyString());
        assertEquals("1", params.getWebhookId());
    }

    @Test
    public void createParam_AllEvent_Test() {

        // when
        String projectKey = "PROJECT";
        long webhookId = 1L;
        UpdateWebhookParams params = new UpdateWebhookParams(projectKey, webhookId);
        params.allEvent(true);

        // then
        List<NameValuePair> parameters = params.getParamList();
        assertEquals(1, parameters.size());
        assertTrue(existsOneKeyValue(parameters, "allEvent", "true"));
        assertEquals(projectKey, params.getProjectIdOrKeyString());
        assertEquals("1", params.getWebhookId());
    }

    @Test
    public void createParam_SetNull_Test() {

        // when
        long projectId = 1000001L;
        long webhookId = 1L;
        UpdateWebhookParams params = new UpdateWebhookParams(projectId, webhookId);
        params.name(null)
                .hookUrl(null)
                .description(null);

        // then
        List<NameValuePair> parameters = params.getParamList();
        assertEquals(3, parameters.size());
        assertTrue(existsOneKeyValue(parameters, "name", ""));
        assertTrue(existsOneKeyValue(parameters, "hookUrl", ""));
        assertTrue(existsOneKeyValue(parameters, "description", ""));
        assertEquals(String.valueOf(projectId), params.getProjectIdOrKeyString());
        assertEquals("1", params.getWebhookId());
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/UpdateWebhookParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.Activity;
import com.nulabinc.backlog4j.http.NameValuePair;

import java.util.List;

/**
 * Parameters for update webhook API.
 *
 * @author nulab-inc
 */
public class UpdateWebhookParams extends PatchParams {

    private Object projectIdOrKey;
    private Object webhookId;

    public UpdateWebhookParams(Object projectIdOrKey, long webhookId) {
        this.projectIdOrKey = projectIdOrKey;
        this.webhookId = webhookId;
    }

    public String getProjectIdOrKeyString() {
        return projectIdOrKey.toString();
    }

    public String getWebhookId() {
        return webhookId.toString();
    }

    public UpdateWebhookParams name(String name) {
        String value = (name==null) ? "" : name;
        parameters.add(new NameValuePair("name", value));
        return this;
    }

    public UpdateWebhookParams description(String description) {
        String value = (description==null) ? "" : description;
        parameters.add(new NameValuePair("description", value));
        return this;
    }

    public UpdateWebhookParams hookUrl(String hookUrl) {
        String value = (hookUrl==null) ? "" : hookUrl;
        parameters.add(new NameValuePair("hookUrl", value));
        return this;
    }

    public UpdateWebhookParams allEvent(boolean allEvent) {
        parameters.add(new NameValuePair("allEvent", String.valueOf(allEvent)));
        return this;
    }

    public UpdateWebhookParams activityTypeIds(List<Activity.Type> activityTypeIds) {
        if (activityTypeIds != null) {
            for (Activity.Type type : activityTypeIds) {
                parameters.add(new NameValuePair("activityTypeIds[]", String.valueOf(type.getIntValue())));
            }
        }
        return this;
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/GetWikiTagsParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.http.NameValuePair;

/**
 * Parameters for get wiki page's tag API.
 *
 * @author nulab-inc
 */
public class GetWikiTagsParams extends GetParams {

    public GetWikiTagsParams(Object projectKey) {
        parameters.add(new NameValuePair("projectIdOrKey", projectKey.toString()));
    }
}
<file_sep>/src/test/java/com/nulabinc/backlog4j/conf/BacklogComConfigureTest.java
package com.nulabinc.backlog4j.conf;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
 * @author nulab-inc
 */
public class BacklogComConfigureTest {

    @Test
    public void getOAuthAuthorizationURLTest() throws Exception {

        // when
        String testSpace = "test";
        BacklogConfigure configure = new BacklogComConfigure(testSpace);

        // then
        String expected = "https://" + testSpace + ".backlog.com/api/v2";
        assertEquals(expected, configure.getRestBaseURL());

    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/internal/json/SpaceJSONImpl.java
package com.nulabinc.backlog4j.internal.json;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.nulabinc.backlog4j.SharedFile;
import com.nulabinc.backlog4j.Space;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

import java.util.Date;

/**
 * @author nulab-inc
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public class SpaceJSONImpl implements Space {

    private String spaceKey;
    private String name;
    private long ownerId;
    private String lang;
    private String timezone;
    private String reportSendTime;
    private String textFormattingRule;
    @JsonDeserialize(using = JacksonCustomDateDeserializer.class)
    private Date updated;
    @JsonDeserialize(using = JacksonCustomDateDeserializer.class)
    private Date created;

    @Override
    public String getSpaceKey() {
        return spaceKey;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public long getOwnerId() {
        return ownerId;
    }

    @Override
    public String getOwnerIdAsString() {
        return String.valueOf(this.ownerId);
    }

    @Override
    public String getLang() {
        return lang;
    }

    @Override
    public String getTimezone() {
        return timezone;
    }

    @Override
    public String getReportSendTime() {
        return reportSendTime;
    }

    @Override
    public String getTextFormattingRule() {
        return textFormattingRule;
    }

    @Override
    public Date getUpdated() {
        return updated;
    }

    @Override
    public Date getCreated() {
        return created;
    }


    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        if (obj.getClass() != getClass()) {
            return false;
        }
        SpaceJSONImpl rhs = (SpaceJSONImpl) obj;
        return new EqualsBuilder()
                .append(this.spaceKey, rhs.spaceKey)
                .append(this.name, rhs.name)
                .append(this.ownerId, rhs.ownerId)
                .append(this.lang, rhs.lang)
                .append(this.timezone, rhs.timezone)
                .append(this.reportSendTime, rhs.reportSendTime)
                .append(this.textFormattingRule, rhs.textFormattingRule)
                .append(this.updated, rhs.updated)
                .append(this.created, rhs.created)
                .isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder()
                .append(spaceKey)
                .append(name)
                .append(ownerId)
                .append(lang)
                .append(timezone)
                .append(reportSendTime)
                .append(textFormattingRule)
                .append(updated)
                .append(created)
                .toHashCode();
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("spaceKey", spaceKey)
                .append("name", name)
                .append("ownerId", ownerId)
                .append("lang", lang)
                .append("timezone", timezone)
                .append("reportSendTime", reportSendTime)
                .append("textFormattingRule", textFormattingRule)
                .append("updated", updated)
                .append("created", created)
                .toString();
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/UpdateDateCustomFieldParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.internal.json.customFields.DateCustomFieldSetting;
import com.nulabinc.backlog4j.http.NameValuePair;

import java.util.List;

/**
 * Parameters for update date type custom field API.
 *
 * @author nulab-inc
 */
public class UpdateDateCustomFieldParams extends UpdateCustomFieldParams {

    public UpdateDateCustomFieldParams(Object projectIdOrKey, long customFiledId) {
        super(projectIdOrKey, customFiledId);
    }

    public UpdateDateCustomFieldParams min(String min) {
        parameters.add(new NameValuePair("min", min));
        return this;
    }

    public UpdateDateCustomFieldParams max(String max) {
        parameters.add(new NameValuePair("max", max));
        return this;
    }

    public UpdateDateCustomFieldParams initialValueType(DateCustomFieldSetting.InitialValueType initialValueType) {
        parameters.add(new NameValuePair("initialValueType", String.valueOf(initialValueType.getIntValue())));
        return this;
    }

    public UpdateDateCustomFieldParams initialDate(String initialDate) {
        parameters.add(new NameValuePair("initialDate", initialDate));
        return this;
    }

    public UpdateDateCustomFieldParams initialShift(int initialShift) {
        parameters.add(new NameValuePair("initialShift", String.valueOf(initialShift)));
        return this;
    }

    @Override
    public UpdateDateCustomFieldParams applicableIssueTypes(List<Long> applicableIssueTypes) {
        return (UpdateDateCustomFieldParams) super.applicableIssueTypes(applicableIssueTypes);
    }

    @Override
    public UpdateDateCustomFieldParams description(String description) {
        return (UpdateDateCustomFieldParams) super.description(description);
    }

    @Override
    public UpdateDateCustomFieldParams required(boolean required) {
        return (UpdateDateCustomFieldParams) super.required(required);
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/Watch.java
package com.nulabinc.backlog4j;

import java.util.Date;

/**
 * The interface for Backlog Watch data.
 */
public interface Watch {

    long getId();

    String getIdAsString();
    
    boolean getAlreadyRead();
    
    String getAlreadyReadAsString();
    
    String getNote();

    String getType();

    Issue getIssue();

    Date getCreated();

    Date getUpdated();

}
<file_sep>/src/main/java/com/nulabinc/backlog4j/BacklogAPIErrorMessage.java
package com.nulabinc.backlog4j;

/**
 * The error message class for Backlog exception.
 *
 * @author nulab-inc
 */
public class BacklogAPIErrorMessage {

    private String message;
    private int code;
    private String errorInfo;
    private String moreInfo;

    public String getMessage() {
        return message;
    }

    public int getCode() {
        return code;
    }

    public String getErrorInfo() {
        return errorInfo;
    }

    public String getMoreInfo() {
        return moreInfo;
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/internal/InternalFactory.java
package com.nulabinc.backlog4j.internal;


import com.nulabinc.backlog4j.*;
import com.nulabinc.backlog4j.auth.AccessToken;
import com.nulabinc.backlog4j.http.BacklogHttpResponse;

/**
 * @author nulab-inc
 */
public interface InternalFactory {

    AccessToken createAccessToken(BacklogHttpResponse res) throws BacklogException;

    Space createSpace(BacklogHttpResponse res) throws BacklogException;

    SpaceNotification createSpaceNotification(BacklogHttpResponse res) throws BacklogException;

    DiskUsage createDiskUsage(BacklogHttpResponse res) throws BacklogException;

    DiskUsageDetail createDiskUsageDetail(BacklogHttpResponse res) throws BacklogException;

    ResponseList<Project> createProjectList(BacklogHttpResponse res) throws BacklogException;

    ProjectWithVCS createProject(BacklogHttpResponse res) throws BacklogException;

    ResponseList<Activity> createActivityList(BacklogHttpResponse res) throws BacklogException;

    Activity createActivity(BacklogHttpResponse res) throws BacklogException;

    ResponseList<Issue> createIssueList(BacklogHttpResponse res) throws BacklogException;

    Issue createIssue(BacklogHttpResponse res) throws BacklogException;

    Issue importIssue(BacklogHttpResponse res) throws BacklogException;

    ResponseList<IssueComment> createIssueCommentList(BacklogHttpResponse res) throws BacklogException;

    IssueComment createIssueComment(BacklogHttpResponse res) throws BacklogException;

    ResponseList<User> createUserList(BacklogHttpResponse res) throws BacklogException;

    User createUser(BacklogHttpResponse res) throws BacklogException;

    ResponseList<IssueType> createIssueTypeList(BacklogHttpResponse res) throws BacklogException;

    IssueType createIssueType(BacklogHttpResponse res) throws BacklogException;

    ResponseList<Category> createCategoryList(BacklogHttpResponse res) throws BacklogException;

    Category createCategory(BacklogHttpResponse res) throws BacklogException;

    ResponseList<CustomFieldSetting> createCustomFieldList(BacklogHttpResponse res) throws BacklogException;

    CustomFieldSetting createCustomField(BacklogHttpResponse res) throws BacklogException;

    ResponseList<Priority> createPriorityList(BacklogHttpResponse res) throws BacklogException;

    ResponseList<Resolution> createResolutionList(BacklogHttpResponse res) throws BacklogException;

    ResponseList<Status> createStatusList(BacklogHttpResponse res) throws BacklogException;

    Status createStatus(BacklogHttpResponse res) throws BacklogException;

    ResponseList<Star> createStarList(BacklogHttpResponse res) throws BacklogException;

    Star createStar(BacklogHttpResponse res) throws BacklogException;

    Count createCount(BacklogHttpResponse res) throws BacklogException;

    ResponseList<Version> createVersionList(BacklogHttpResponse res) throws BacklogException;

    Version createVersion(BacklogHttpResponse res) throws BacklogException;

    ResponseList<Milestone> createMilestoneList(BacklogHttpResponse res) throws BacklogException;

    Milestone createMilestone(BacklogHttpResponse res) throws BacklogException;

    Wiki createWiki(BacklogHttpResponse res) throws BacklogException;

    Wiki importWiki(BacklogHttpResponse res) throws BacklogException;

    ResponseList<Wiki> createWikiList(BacklogHttpResponse res) throws BacklogException;

    ResponseList<WikiTag> createWikiTagList(BacklogHttpResponse res) throws BacklogException;

    WikiHistory createWikiHistory(BacklogHttpResponse res) throws BacklogException;

    WikiTag createWikiTag(BacklogHttpResponse res) throws BacklogException;

    ResponseList<WikiHistory> createWikiHistoryList(BacklogHttpResponse res) throws BacklogException;

    ResponseList<Notification> createNotificationList(BacklogHttpResponse res) throws BacklogException;

    Repository createRepository(BacklogHttpResponse res) throws BacklogException;

    ResponseList<Repository> createRepositoryList(BacklogHttpResponse res) throws BacklogException;

    PullRequest createPullRequest(BacklogHttpResponse res) throws BacklogException;

    ResponseList<PullRequest> createPullRequestList(BacklogHttpResponse res) throws BacklogException;

    PullRequestComment createPullRequestComment(BacklogHttpResponse res) throws BacklogException;

    ResponseList<PullRequestComment> createPullRequestCommentList(BacklogHttpResponse res) throws BacklogException;

    ResponseList<ViewedIssue> createViewedIssueList(BacklogHttpResponse res) throws BacklogException;

    ResponseList<ViewedProject> createViewedProjectList(BacklogHttpResponse res) throws BacklogException;

    ResponseList<ViewedWiki> createViewedWikiList(BacklogHttpResponse res) throws BacklogException;

    ResponseList<SharedFile> createSharedFileList(BacklogHttpResponse res) throws BacklogException;

    SharedFile createSharedFile(BacklogHttpResponse res) throws BacklogException;

    ResponseList<Attachment> createAttachmentList(BacklogHttpResponse res) throws BacklogException;

    Attachment createAttachment(BacklogHttpResponse res) throws BacklogException;

    Group createGroup(BacklogHttpResponse res) throws BacklogException;

    ResponseList<Group> createGroupList(BacklogHttpResponse res) throws BacklogException;

    ResponseList<Webhook> createWebhookList(BacklogHttpResponse res) throws BacklogException;

    Webhook createWebhook(BacklogHttpResponse res) throws BacklogException;

    Watch createWatch(BacklogHttpResponse res) throws BacklogException;

    ResponseList<Watch> createWatchList(BacklogHttpResponse res) throws BacklogException;

}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/CreateUserParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.User;
import com.nulabinc.backlog4j.http.NameValuePair;

/**
 * Parameters for create user API.
 *
 * @author nulab-inc
 */
public class CreateUserParams extends PostParams {

    public CreateUserParams(String userId, String password,
                                 String name, String mailAddress,
                                 User.RoleType roleType){
        parameters.add(new NameValuePair("userId", userId));
        parameters.add(new NameValuePair("password", <PASSWORD>));
        parameters.add(new NameValuePair("name", name));
        parameters.add(new NameValuePair("mailAddress", mailAddress));
        parameters.add(new NameValuePair("roleType", String.valueOf(roleType.getIntValue())));
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/conf/BacklogPackageConfigure.java
package com.nulabinc.backlog4j.conf;

import java.net.MalformedURLException;

/**
 * @author nulab-inc
 */
public class BacklogPackageConfigure extends BacklogConfigure {

    private String url;

    public BacklogPackageConfigure(String url) throws MalformedURLException {

        if (url == null) {
            throw new IllegalArgumentException("url must not be null");
        }

        if (url.endsWith("/")) {
            this.url = url.substring(0, url.length() -1);
        } else {
            this.url = url;
        }
    }

    @Override
    public String getOAuthAuthorizationURL() {
        return this.url + "/OAuth2AccessRequest.action";
    }

    @Override
    public String getOAuthAccessTokenURL() {
        return this.url + "/api/v2/oauth2/token";
    }

    @Override
    public String getRestBaseURL() {
        return this.url + "/api/v2";
    }

    @Override
    public String getWebAppBaseURL() {
        return this.url;
    }

}
<file_sep>/src/main/java/com/nulabinc/backlog4j/internal/json/IssueTypeJSONImpl.java
package com.nulabinc.backlog4j.internal.json;

/**
 * @author nulab-inc
 */

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.nulabinc.backlog4j.IssueType;
import com.nulabinc.backlog4j.Project;
import com.nulabinc.backlog4j.User;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

/**
 * @author nulab-inc
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public class IssueTypeJSONImpl implements IssueType {

    private long id;
    private long projectId;
    private String name;
    private String color;
    private String templateSummary;
    private String templateDescription;

    @Override
    public long getId() {
        return this.id;
    }

    @Override
    public String getIdAsString() {
        return String.valueOf(this.id);
    }

    @Override
    public long getProjectId() {
        return this.projectId;
    }

    @Override
    public String getProjectIdAsString() {
        return String.valueOf(this.projectId);
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public Project.IssueTypeColor getColor() {
        return Project.IssueTypeColor.strValueOf(this.color);
    }

    @Override
    public String getTemplateSummary() { return this.templateSummary; }

    @Override
    public String getTemplateDescription() { return this.templateDescription; }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        if (obj.getClass() != getClass()) {
            return false;
        }
        IssueTypeJSONImpl rhs = (IssueTypeJSONImpl) obj;
        return new EqualsBuilder()
                .append(this.id, rhs.id)
                .append(this.projectId, rhs.projectId)
                .append(this.name, rhs.name)
                .append(this.color, rhs.color)
                .append(this.templateSummary, rhs.templateSummary)
                .append(this.templateDescription, rhs.templateDescription)
                .isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder()
                .append(id)
                .append(projectId)
                .append(name)
                .append(color)
                .append(templateSummary)
                .append(templateDescription)
                .toHashCode();
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("id", id)
                .append("projectId", projectId)
                .append("name", name)
                .append("color", color)
                .append("templateSummary", templateSummary)
                .append("templateDescription", templateDescription)
                .toString();
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/AttachmentData.java
package com.nulabinc.backlog4j;

/**
 * The interface for Backlog attachment file data.
 *
 * @author nulab-inc
 */
public interface AttachmentData extends FileData{

}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/AddWikiAttachmentParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.http.NameValuePair;

import java.util.List;

/**
 * Parameters for add attachment file to wiki page's API.
 *
 * @author nulab-inc
 */
public class AddWikiAttachmentParams extends PostParams {

    private Object wikiId;

    public AddWikiAttachmentParams(Object wikiId, List attachmentIds){
        this.wikiId = wikiId;
        for (Object attachmentId : attachmentIds) {
            parameters.add(new NameValuePair("attachmentId[]", String.valueOf(attachmentId)));
        }
    }

    public String getWikiId() {
        return this.wikiId.toString();
    }
}
<file_sep>/src/test/java/com/nulabinc/backlog4j/internal/json/WebhookJSONImplTest.java
package com.nulabinc.backlog4j.internal.json;

import com.nulabinc.backlog4j.Activity;
import com.nulabinc.backlog4j.ResponseList;
import com.nulabinc.backlog4j.User;
import com.nulabinc.backlog4j.Webhook;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
 * @author nulab-inc
 */
public class WebhookJSONImplTest extends AbstractJSONImplTest {
    @Test
    public void createWebhookListTest() throws IOException {
        String fileContentStr = getJsonString("json/webhooks.json");
        ResponseList<Webhook> webhooks = factory.createWebhookList(fileContentStr);

        assertEquals(2, webhooks.size());

        Webhook webhook = webhooks.get(0);
        assertEquals(1079196895, webhook.getId());
        assertEquals("webhook", webhook.getName());
        assertEquals("", webhook.getDescription());
        assertEquals("http://nulab.test/", webhook.getHookUrl());
        assertFalse(webhook.isAllEvent());
        assertNotNull(webhook.getActivityTypeIds());
        assertEquals(5, webhook.getActivityTypeIds().size());
        List<Activity.Type> activityTypeIds = webhook.getActivityTypeIds();
        assertEquals(Activity.Type.IssueCreated, activityTypeIds.get(0));
        assertEquals(Activity.Type.IssueUpdated, activityTypeIds.get(1));
        assertEquals(Activity.Type.IssueCommented, activityTypeIds.get(2));
        assertEquals(Activity.Type.IssueDeleted, activityTypeIds.get(3));
        assertEquals(Activity.Type.WikiCreated, activityTypeIds.get(4));

        assertEquals("2014-11-30 01:22:21", DateFormatUtils.formatUTC(webhook.getCreated(), "yyyy-MM-dd HH:mm:ss"));
        assertNotNull(webhook.getCreatedUser());
        assertEquals(1, webhook.getCreatedUser().getId());
        assertEquals("admin", webhook.getCreatedUser().getUserId());
        assertEquals("admin", webhook.getCreatedUser().getName());
        assertEquals(User.RoleType.Admin, webhook.getCreatedUser().getRoleType());
        assertEquals("ja", webhook.getCreatedUser().getLang());
        assertEquals("<EMAIL>", webhook.getCreatedUser().getMailAddress());

        assertEquals("2015-02-13 17:36:59", DateFormatUtils.formatUTC(webhook.getUpdated(), "yyyy-MM-dd HH:mm:ss"));
        assertNotNull(webhook.getUpdatedUser());
        assertEquals(2, webhook.getUpdatedUser().getId());
        assertEquals("developer", webhook.getUpdatedUser().getUserId());
        assertEquals("developer", webhook.getUpdatedUser().getName());
        assertEquals(User.RoleType.User, webhook.getUpdatedUser().getRoleType());
        assertEquals("en", webhook.getUpdatedUser().getLang());
        assertEquals("<EMAIL>", webhook.getUpdatedUser().getMailAddress());

        webhook = webhooks.get(1);
        assertEquals(1079097008, webhook.getId());
        assertEquals("webhook2", webhook.getName());
        assertEquals("this is description", webhook.getDescription());
        assertEquals("http://nulab.test/", webhook.getHookUrl());
        assertTrue(webhook.isAllEvent());
        assertNotNull(webhook.getActivityTypeIds());
        assertEquals(0, webhook.getActivityTypeIds().size());
    }

    @Test
    public void createWebhookTest() throws IOException {
        String fileContentStr = getJsonString("json/webhook.json");
        Webhook webhook = factory.createWebhook(fileContentStr);

        assertEquals(1079196895, webhook.getId());
        assertEquals("webhook", webhook.getName());
        assertEquals("", webhook.getDescription());
        assertEquals("http://nulab.test/", webhook.getHookUrl());
        assertFalse(webhook.isAllEvent());
        assertNotNull(webhook.getActivityTypeIds());
        assertEquals(5, webhook.getActivityTypeIds().size());
        List<Activity.Type> activityTypeIds = webhook.getActivityTypeIds();
        assertEquals(Activity.Type.IssueCreated, activityTypeIds.get(0));
        assertEquals(Activity.Type.IssueUpdated, activityTypeIds.get(1));
        assertEquals(Activity.Type.IssueCommented, activityTypeIds.get(2));
        assertEquals(Activity.Type.IssueDeleted, activityTypeIds.get(3));
        assertEquals(Activity.Type.WikiCreated, activityTypeIds.get(4));

        assertEquals("2014-11-30 01:22:21", DateFormatUtils.formatUTC(webhook.getCreated(), "yyyy-MM-dd HH:mm:ss"));
        assertNotNull(webhook.getCreatedUser());
        assertEquals(1, webhook.getCreatedUser().getId());
        assertEquals("admin", webhook.getCreatedUser().getUserId());
        assertEquals("admin", webhook.getCreatedUser().getName());
        assertEquals(User.RoleType.Admin, webhook.getCreatedUser().getRoleType());
        assertEquals("ja", webhook.getCreatedUser().getLang());
        assertEquals("<EMAIL>", webhook.getCreatedUser().getMailAddress());

        assertEquals("2014-11-30 01:22:21", DateFormatUtils.formatUTC(webhook.getUpdated(), "yyyy-MM-dd HH:mm:ss"));
        assertNotNull(webhook.getUpdatedUser());
        assertEquals(1, webhook.getUpdatedUser().getId());
        assertEquals("admin", webhook.getUpdatedUser().getUserId());
        assertEquals("admin", webhook.getUpdatedUser().getName());
        assertEquals(User.RoleType.Admin, webhook.getUpdatedUser().getRoleType());
        assertEquals("ja", webhook.getUpdatedUser().getLang());
        assertEquals("<EMAIL>", webhook.getUpdatedUser().getMailAddress());
    }

    @Test
    public void equalsTest1() throws IOException {
        String fileContentStr = getJsonString("json/webhook.json");
        Webhook webhook1 = factory.createWebhook(fileContentStr);
        Webhook webhook2 = factory.createWebhook(fileContentStr);
        assertEquals(webhook1, webhook2);
    }

    @Test
    public void hashCodeTest1() throws IOException {
        String fileContentStr = getJsonString("json/webhook.json");
        Webhook webhook1 = factory.createWebhook(fileContentStr);
        Webhook webhook2 = factory.createWebhook(fileContentStr);
        assertEquals(webhook1.hashCode(), webhook2.hashCode());

    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/UpdateWatchParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.http.NameValuePair;

/**
 * Parameters for update watch API.
 *
 * @author nulab-inc
 */
public class UpdateWatchParams extends PatchParams {

    private Object watchingId;

    public UpdateWatchParams(Object watchingId) {
        this.watchingId = watchingId;
    }

    public String getWatchingIdString() {
        return watchingId.toString();
    }

    public UpdateWatchParams note(String note) {
        parameters.add(new NameValuePair("note", String.valueOf(note)));
        return this;
    }

}
<file_sep>/src/main/java/com/nulabinc/backlog4j/internal/json/DiskUsageJSONImpl.java
package com.nulabinc.backlog4j.internal.json;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.nulabinc.backlog4j.DiskUsage;
import com.nulabinc.backlog4j.DiskUsageDetail;
import com.nulabinc.backlog4j.User;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

import java.util.Arrays;
import java.util.List;

/**
 * @author nulab-inc
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public class DiskUsageJSONImpl implements DiskUsage {
    private long capacity;
    private long issue;
    private long wiki;
    private long file;
    private long subversion;
    private long git;
    @JsonDeserialize(as = DiskUsageDetailJSONImpl[].class)
    private DiskUsageDetail[] details;


    @Override
    public long getCapacity() {
        return capacity;
    }

    @Override
    public long getIssue() {
        return issue;
    }

    @Override
    public long getWiki() {
        return wiki;
    }

    @Override
    public long getFile() {
        return file;
    }

    @Override
    public long getSubversion() {
        return subversion;
    }

    @Override
    public long getGit() {
        return git;
    }

    @Override
    public List<DiskUsageDetail> getDetails() {
        return Arrays.asList(details);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        if (obj.getClass() != getClass()) {
            return false;
        }
        DiskUsageJSONImpl rhs = (DiskUsageJSONImpl) obj;
        return new EqualsBuilder()
                .append(this.capacity, rhs.capacity)
                .append(this.issue, rhs.issue)
                .append(this.wiki, rhs.wiki)
                .append(this.file, rhs.file)
                .append(this.subversion, rhs.subversion)
                .append(this.git, rhs.git)
                .append(this.details, rhs.details)
                .isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder()
                .append(capacity)
                .append(issue)
                .append(wiki)
                .append(file)
                .append(subversion)
                .append(git)
                .append(details)
                .toHashCode();
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("capacity", capacity)
                .append("issue", issue)
                .append("wiki", wiki)
                .append("file", file)
                .append("subversion", subversion)
                .append("git", git)
                .append("details", details)
                .toString();
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/internal/json/IssueJSONImpl.java
package com.nulabinc.backlog4j.internal.json;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.nulabinc.backlog4j.*;
import com.nulabinc.backlog4j.internal.json.customFields.CustomFieldJSONImpl;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

import java.math.BigDecimal;
import java.util.*;

/**
 * @author nulab-inc
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public class IssueJSONImpl implements Issue {

    private int id;
    private String issueKey;
    private int keyId;
    private int projectId;
    @JsonDeserialize(as = IssueTypeJSONImpl.class)
    private IssueType issueType;
    private String summary;
    private String description;
    @JsonDeserialize(as = ResolutionJSONImpl.class)
    private Resolution resolution;
    @JsonDeserialize(as = PriorityJSONImpl.class)
    private Priority priority;
    @JsonDeserialize(as = StatusJSONImpl.class)
    private Status status;
    @JsonDeserialize(as = UserJSONImpl.class)
    private User assignee;
    @JsonDeserialize(as = CategoryJSONImpl[].class)
    private Category[] category;
    @JsonDeserialize(as = VersionJSONImpl[].class)
    private Version[] versions;
    @JsonDeserialize(as = MilestoneJSONImpl[].class)
    private Milestone[] milestone;
    @JsonDeserialize(using = JacksonCustomDateDeserializer.class)
    private Date startDate;
    @JsonDeserialize(using = JacksonCustomDateDeserializer.class)
    private Date dueDate;
    @JsonDeserialize(using = JacksonCustomBigdecimalDeserializer.class)
    private BigDecimal estimatedHours;
    @JsonDeserialize(using = JacksonCustomBigdecimalDeserializer.class)
    private BigDecimal actualHours;
    private long parentIssueId;
    @JsonDeserialize(as = UserJSONImpl.class)
    private User createdUser;
    @JsonDeserialize(using = JacksonCustomDateDeserializer.class)
    private Date created;
    @JsonDeserialize(as = UserJSONImpl.class)
    private User updatedUser;
    @JsonDeserialize(using = JacksonCustomDateDeserializer.class)
    private Date updated;
    @JsonDeserialize(as = CustomFieldJSONImpl[].class)
    private CustomField[] customFields;
    @JsonDeserialize(as = AttachmentJSONImpl[].class)
    private Attachment[] attachments;
    @JsonDeserialize(as = SharedFileJSONImpl[].class)
    private SharedFile[] sharedFiles;
    @JsonDeserialize(as = StarJSONImpl[].class)
    private Star[] stars;

    @Override
    public long getId() {
        return this.id;
    }

    @Override
    public String getIdAsString() {
        return String.valueOf(this.id);
    }

    @Override
    public String getIssueKey() {
        return this.issueKey;
    }

    @Override
    public long getKeyId() {
        return this.keyId;
    }

    @Override
    public String getKeyIdAsString() {
        return String.valueOf(this.keyId);
    }

    @Override
    public long getProjectId() {
        return this.projectId;
    }

    @Override
    public String getProjectIdAsString() {
        return String.valueOf(this.projectId);
    }

    @Override
    public IssueType getIssueType() {
        return this.issueType;
    }

    @Override
    public String getSummary() {
        return this.summary;
    }

    @Override
    public String getDescription() {
        return this.description;
    }


    @Override
    public Resolution getResolution() {
        return resolution;
    }

    @Override
    public Priority getPriority() {
        return priority;
    }

    @Override
    public Status getStatus() {
        return status;
    }

    @Override
    public User getAssignee() {
        return assignee;
    }

    @Override
    public List<Category> getCategory() {
        if(category == null || category.length == 0){
            return Collections.emptyList();
        }
        return Arrays.asList(category);
    }

    @Override
    public List<Version> getVersions() {
        if(versions == null || versions.length == 0){
            return Collections.emptyList();
        }
        return Arrays.asList(versions);
    }

    @Override
    public List<Milestone> getMilestone() {
        if(milestone == null || milestone.length == 0){
            return Collections.emptyList();
        }
        return Arrays.asList(milestone);
    }

    @Override
    public Date getStartDate() {
        return startDate;
    }

    @Override
    public Date getDueDate() {
        return dueDate;
    }

    @Override
    public BigDecimal getEstimatedHours() {
        return estimatedHours;
    }

    @Override
    public BigDecimal getActualHours() {
        return actualHours;
    }

    @Override
    public long getParentIssueId() {
        return parentIssueId;
    }

    @Override
    public User getCreatedUser() {
        return createdUser;
    }

    @Override
    public Date getCreated() {
        return created;
    }

    @Override
    public User getUpdatedUser() {
        return updatedUser;
    }

    @Override
    public Date getUpdated() {
        return updated;
    }

    @Override
    public List<CustomField> getCustomFields() {
        if(customFields == null || customFields.length == 0){
            return Collections.emptyList();
        }
        return Arrays.asList(customFields);
    }

    @Override
    public List<Attachment> getAttachments() {
        if(attachments == null || attachments.length == 0){
            return Collections.emptyList();
        }
        return Arrays.asList(attachments);
    }

    @Override
    public List<SharedFile> getSharedFiles() {
        if(sharedFiles == null || sharedFiles.length == 0){
            return Collections.emptyList();
        }
        return Arrays.asList(sharedFiles);
    }

    @Override
    public List<Star> getStars() {
        if(stars == null || stars.length == 0){
            return Collections.emptyList();
        }
        return Arrays.asList(stars);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        if (obj.getClass() != getClass()) {
            return false;
        }
        IssueJSONImpl rhs = (IssueJSONImpl) obj;
        return new EqualsBuilder()
                .append(this.id, rhs.id)
                .append(this.issueKey, rhs.issueKey)
                .append(this.keyId, rhs.keyId)
                .append(this.projectId, rhs.projectId)
                .append(this.issueType, rhs.issueType)
                .append(this.summary, rhs.summary)
                .append(this.description, rhs.description)
                .append(this.resolution, rhs.resolution)
                .append(this.priority, rhs.priority)
                .append(this.status, rhs.status)
                .append(this.assignee, rhs.assignee)
                .append(this.category, rhs.category)
                .append(this.versions, rhs.versions)
                .append(this.milestone, rhs.milestone)
                .append(this.startDate, rhs.startDate)
                .append(this.dueDate, rhs.dueDate)
                .append(this.estimatedHours, rhs.estimatedHours)
                .append(this.actualHours, rhs.actualHours)
                .append(this.parentIssueId, rhs.parentIssueId)
                .append(this.createdUser, rhs.createdUser)
                .append(this.created, rhs.created)
                .append(this.updatedUser, rhs.updatedUser)
                .append(this.updated, rhs.updated)
                .append(this.customFields, rhs.customFields)
                .append(this.attachments, rhs.attachments)
                .append(this.sharedFiles, rhs.sharedFiles)
                .append(this.stars, rhs.stars)
                .isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder()
                .append(id)
                .append(issueKey)
                .append(keyId)
                .append(projectId)
                .append(issueType)
                .append(summary)
                .append(description)
                .append(resolution)
                .append(priority)
                .append(status)
                .append(assignee)
                .append(category)
                .append(versions)
                .append(milestone)
                .append(startDate)
                .append(dueDate)
                .append(estimatedHours)
                .append(actualHours)
                .append(parentIssueId)
                .append(createdUser)
                .append(created)
                .append(updatedUser)
                .append(updated)
                .append(customFields)
                .append(attachments)
                .append(sharedFiles)
                .append(stars)
                .toHashCode();
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("id", id)
                .append("issueKey", issueKey)
                .append("keyId", keyId)
                .append("projectId", projectId)
                .append("issueType", issueType)
                .append("summary", summary)
                .append("description", description)
                .append("resolution", resolution)
                .append("priority", priority)
                .append("status", status)
                .append("assignee", assignee)
                .append("category", category)
                .append("versions", versions)
                .append("milestone", milestone)
                .append("startDate", startDate)
                .append("dueDate", dueDate)
                .append("estimatedHours", estimatedHours)
                .append("actualHours", actualHours)
                .append("parentIssueId", parentIssueId)
                .append("createdUser", createdUser)
                .append("created", created)
                .append("updatedUser", updatedUser)
                .append("updated", updated)
                .append("customFields", customFields)
                .append("attachments", attachments)
                .append("sharedFiles", sharedFiles)
                .append("stars", stars)
                .toString();
    }
}<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/CreateWikiParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.http.NameValuePair;

/**
 * Parameters for create wiki API.
 *
 * @author nulab-inc
 */
public class CreateWikiParams extends PostParams {

    public CreateWikiParams(Object projectId, String name, String content) {
        parameters.add(new NameValuePair("projectId", projectId.toString()));
        parameters.add(new NameValuePair("name", name));
        parameters.add(new NameValuePair("content", content));
    }

    public CreateWikiParams mailNotify(boolean mailNotify) {
        parameters.add(new NameValuePair("mailNotify", String.valueOf(mailNotify)));
        return this;
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/AddIssueTypeParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.BacklogAPIException;
import com.nulabinc.backlog4j.Project;
import com.nulabinc.backlog4j.http.NameValuePair;

/**
 * Parameters for add issue type API.
 *
 * @author nulab-inc
 */
public class AddIssueTypeParams extends PostParams {
    private Object projectIdOrKey;

    public AddIssueTypeParams(Object projectIdOrKey, String name, Project.IssueTypeColor color){
        parameters.add(new NameValuePair("name", name));
        parameters.add(new NameValuePair("color", color.getStrValue()));
        this.projectIdOrKey = projectIdOrKey;
    }

    public String getProjectIdOrKeyString() {
        return projectIdOrKey.toString();
    }

    public AddIssueTypeParams templateSummary(String templateSummary) {
        parameters.add(new NameValuePair("templateSummary", templateSummary));
        return this;
    }

    public AddIssueTypeParams templateDescription(String templateDescription) {
        parameters.add(new NameValuePair("templateDescription", templateDescription));
        return this;
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/internal/json/DiskUsageDetailJSONImpl.java
package com.nulabinc.backlog4j.internal.json;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.nulabinc.backlog4j.DiskUsageDetail;
import com.nulabinc.backlog4j.User;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

/**
 * @author nulab-inc
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public class DiskUsageDetailJSONImpl implements DiskUsageDetail {
    private long projectId;

    private long issue;
    private long wiki;
    private long file;
    private long subversion;
    private long git;

    @Override
    public long getProjectId() {
        return projectId;
    }

    @Override
    public long getIssue() {
        return issue;
    }

    @Override
    public long getWiki() {
        return wiki;
    }

    @Override
    public long getFile() {
        return file;
    }

    @Override
    public long getSubversion() {
        return subversion;
    }

    @Override
    public long getGit() {
        return git;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        if (obj.getClass() != getClass()) {
            return false;
        }
        DiskUsageDetailJSONImpl rhs = (DiskUsageDetailJSONImpl) obj;
        return new EqualsBuilder()
                .append(this.projectId, rhs.projectId)
                .append(this.issue, rhs.issue)
                .append(this.wiki, rhs.wiki)
                .append(this.file, rhs.file)
                .append(this.subversion, rhs.subversion)
                .append(this.git, rhs.git)
                .isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder()
                .append(projectId)
                .append(issue)
                .append(wiki)
                .append(file)
                .append(subversion)
                .append(git)
                .toHashCode();
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("projectId", projectId)
                .append("issue", issue)
                .append("wiki", wiki)
                .append("file", file)
                .append("subversion", subversion)
                .append("git", git)
                .toString();
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/SharedFileData.java
package com.nulabinc.backlog4j;

/**
 * The interface for Backlog activity shared file data.
 *
 * @author nulab-inc
 */
public interface SharedFileData extends FileData{

}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/GitMethods.java
package com.nulabinc.backlog4j.api;

import com.nulabinc.backlog4j.BacklogException;
import com.nulabinc.backlog4j.Repository;
import com.nulabinc.backlog4j.ResponseList;

/**
 * Executes Backlog Git APIs.
 *
 * @author nulab-inc
 */
public interface GitMethods {

    /**
     * Returns the git repositories in the project.
     *
     * @param projectIdOrKey the project identifier
     * @return the git repositories in a list.
     * @throws BacklogException
     */
    ResponseList<Repository> getGitRepositories(Object projectIdOrKey) throws BacklogException;

    /**
     * Returns the git repository.
     *
     * @param projectIdOrKey the project identifier
     * @param repoIdOrName the repository name
     * @return the git repository.
     * @throws BacklogException
     */
    Repository getGitRepository(Object projectIdOrKey, Object repoIdOrName) throws BacklogException;
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/UpdatePullRequestParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.http.NameValuePair;

import java.util.List;

/**
 * Parameters for update pull request API.
 *
 * @author nulab-inc
 */
public class UpdatePullRequestParams extends PatchParams {

    private Object projectIdOrKey;
    private Object repoIdOrName;
    private Object number;

    /**
     * Constructor
     *
     * @param projectIdOrKey    the project identifier
     * @param repoIdOrName      the repository identifier
     * @param number        the pull request title
     */
    public UpdatePullRequestParams(Object projectIdOrKey, Object repoIdOrName, Object number) {
        this.projectIdOrKey = projectIdOrKey;
        this.repoIdOrName = repoIdOrName;
        this.number = number;
    }

    public String getProjectIdOrKeyString() {
        return projectIdOrKey.toString();
    }

    public String getRepoIdOrNameString() {
        return repoIdOrName.toString();
    }

    public String getNumber() {
        return this.number.toString();
    }

    /**
     * Sets the summary.
     *
     * @param summary the pull request description
     * @return UpdatePullRequestParams instance
     */
    public UpdatePullRequestParams summary(String summary) {
        parameters.add(new NameValuePair("summary", summary));
        return this;
    }

    /**
     * Sets the description.
     *
     * @param description the pull request description
     * @return UpdatePullRequestParams instance
     */
    public UpdatePullRequestParams description(String description) {
        parameters.add(new NameValuePair("description", description));
        return this;
    }

    /**
     * Sets the base parameter.
     *
     * @param base the base branch name
     * @return UpdatePullRequestParams instance
     */
    public UpdatePullRequestParams base(String base) {
        parameters.add(new NameValuePair("base", base));
        return this;
    }

    /**
     * Sets the branch parameter.
     *
     * @param branch the branch name merge to
     * @return UpdatePullRequestParams instance
     */
    public UpdatePullRequestParams branch(String branch) {
        parameters.add(new NameValuePair("branch", branch));
        return this;
    }

    /**
     * Sets the related issue parameter.
     *
     * @param issueId the isssue identifier
     * @return UpdatePullRequestParams instance
     */
    public UpdatePullRequestParams issueId(long issueId) {
        parameters.add(new NameValuePair("issueId", String.valueOf(issueId)));
        return this;
    }

    /**
     * Sets the pull request assignee user.
     *
     * @param assigneeId the assignee user id
     * @return UpdatePullRequestParams instance
     */
    public UpdatePullRequestParams assigneeId(long assigneeId) {
        parameters.add(new NameValuePair("assigneeId", String.valueOf(assigneeId)));
        return this;

    }

    /**
     * Sets the pull request notified users.
     *
     * @param notifiedUserIds notified user identifiers
     * @return UpdatePullRequestParams instance
     */
    public UpdatePullRequestParams notifiedUserIds(List<Long> notifiedUserIds) {
        for (Long notifiedUserId : notifiedUserIds) {
            parameters.add(new NameValuePair("notifiedUserId[]", notifiedUserId.toString()));
        }
        return this;

    }

    /**
     * Sets the pull request attachment files.
     *
     * @param attachmentIds the attachment file identifiers
     * @return UpdatePullRequestParams instance
     */
    public UpdatePullRequestParams attachmentIds(List<Long> attachmentIds) {
        for (Long attachmentId : attachmentIds) {
            parameters.add(new NameValuePair("attachmentId[]", attachmentId.toString()));
        }
        return this;
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/ViewedIssue.java
package com.nulabinc.backlog4j;

import java.util.Date;

/**
 * The interface for Backlog viewed issue data.
 *
 * @author nulab-inc
 */
public interface ViewedIssue {
    Issue getIssue();

    Date getUpdated();
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/SpaceNotification.java
package com.nulabinc.backlog4j;

import java.util.Date;

/**
 * The interface for Backlog space notification data.
 *
 * @author nulab-inc
 */
public interface SpaceNotification {
    String getContent();

    Date getUpdated();

}
<file_sep>/src/main/java/com/nulabinc/backlog4j/BacklogAuthErrorMessage.java
package com.nulabinc.backlog4j;

import com.fasterxml.jackson.annotation.JsonProperty;

/**
 * The error message class for Backlog auth exception.
 *
 * @author nulab-inc
 */
public class BacklogAuthErrorMessage {

    private String error;
    @JsonProperty("error_description")
    private String description;

    public String getError() {
        return error;
    }

    public String getDescription() {
        return description;
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/AddCustomFieldParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.BacklogAPIException;
import com.nulabinc.backlog4j.CustomFieldSetting;
import com.nulabinc.backlog4j.http.NameValuePair;

import java.util.List;

/**
 * Parameters for add custom field API.
 *
 * @author nulab-inc
 */
public abstract class AddCustomFieldParams extends PostParams {

    private Object projectIdOrKey;

    public AddCustomFieldParams(Object projectIdOrKey, CustomFieldSetting.FieldType fieldType, String name){
        this.projectIdOrKey = projectIdOrKey;
        parameters.add(new NameValuePair("typeId", String.valueOf(fieldType.getIntValue())));
        parameters.add(new NameValuePair("name", name));
    }

    public String getProjectIdOrKeyString() {
        return projectIdOrKey.toString();
    }

    public AddCustomFieldParams applicableIssueTypes(List<Long> applicableIssueTypes) {
        for (Long applicableIssueType :applicableIssueTypes) {
            parameters.add(new NameValuePair("applicableIssueTypes[]", String.valueOf(applicableIssueType)));
        }
        return this;
    }

    public AddCustomFieldParams description(String description) {
        parameters.add(new NameValuePair("description", description));
        return this;
    }

    public AddCustomFieldParams required(boolean required) {
        parameters.add(new NameValuePair("required", String.valueOf(required)));
        return this;
    }

}
<file_sep>/src/main/java/com/nulabinc/backlog4j/auth/OnAccessTokenRefreshListener.java
package com.nulabinc.backlog4j.auth;

/**
 * @author nulab-inc
 */
public class OnAccessTokenRefreshListener {
    public void onAccessTokenRefresh(AccessToken accessToken){

    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/CreateGroupParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.http.NameValuePair;

import java.util.List;

/**
 * Parameters for create group API.
 *
 * @author nulab-inc
 */
public class CreateGroupParams extends PostParams {

    public CreateGroupParams(String name){
        parameters.add(new NameValuePair("name", name));
    }

    public CreateGroupParams members(List members) {
        for (Object member : members) {
            parameters.add(new NameValuePair("members[]", member.toString()));
        }
        return this;
    }
}
<file_sep>/src/test/java/com/nulabinc/backlog4j/internal/json/WikiJSONImplTest.java
package com.nulabinc.backlog4j.internal.json;

import com.nulabinc.backlog4j.Attachment;
import com.nulabinc.backlog4j.ResponseList;
import com.nulabinc.backlog4j.SharedFile;
import com.nulabinc.backlog4j.Star;
import com.nulabinc.backlog4j.User;
import com.nulabinc.backlog4j.Wiki;
import com.nulabinc.backlog4j.WikiTag;
import org.exparity.hamcrest.date.DateMatchers;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.util.Calendar;
import java.util.List;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;

/**
 * @author nulab-inc
 */
public class WikiJSONImplTest extends AbstractJSONImplTest {
    @Test
    public void createWikiListTest() throws IOException {
        String fileContentStr = getJsonString("json/wikis.json");
        ResponseList<Wiki> wikis = factory.createWikiList(fileContentStr);

        assertEquals(2, wikis.size());
    }

    @Test
    public void createWikiTest() throws IOException {
        String fileContentStr = getJsonString("json/wiki.json");
        Wiki wiki = factory.createWiki(fileContentStr);


        assertEquals(1073768473, wiki.getId());
        assertEquals(1073760709, wiki.getProjectId());
        assertEquals("テストwiki", wiki.getName());
        assertEquals("テスト用wikiです", wiki.getContent());

        List<WikiTag> tags = wiki.getTags();
        assertEquals(2, tags.size());
        assertEquals(4677, tags.get(0).getId());
        assertEquals("tag1", tags.get(0).getName());

        List<Attachment> attachments = wiki.getAttachments();
        assertEquals(1, attachments.size());

        List<SharedFile> sharedFiles = wiki.getSharedFiles();
        assertEquals(1, sharedFiles.size());

        List<Star> stars = wiki.getStars();
        assertEquals(2, stars.size());

        User user = wiki.getCreatedUser();
        assertEquals(1073751781, user.getId());
        assertEquals("test_admin", user.getUserId());
        assertEquals("あどみにさん", user.getName());
        assertEquals(User.RoleType.Admin, user.getRoleType());
        assertNull(user.getLang());
        assertEquals("<EMAIL>", user.getMailAddress());

        Calendar calendar = Calendar.getInstance();
        calendar.set(2014, Calendar.AUGUST, 5, 0, 59, 45);
        assertThat(calendar.getTime(), DateMatchers.sameDay(wiki.getCreated()));

        User updateUser = wiki.getUpdatedUser();
        assertEquals(1073751782, updateUser.getId());
        assertEquals("test_admin2", updateUser.getUserId());
        assertEquals("あどみにさん2", updateUser.getName());
        assertEquals(User.RoleType.Admin, updateUser.getRoleType());
        assertNull(updateUser.getLang());
        assertEquals("<EMAIL>", updateUser.getMailAddress());

        calendar = Calendar.getInstance();
        calendar.set(2014, Calendar.AUGUST, 5, 1, 0, 11);
        assertThat(calendar.getTime(), DateMatchers.sameDay(wiki.getUpdated()));

    }

    @Test
    public void equalsTest() throws IOException {
        String fileContentStr = getJsonString("json/wiki.json");
        Wiki wiki1 = factory.createWiki(fileContentStr);
        Wiki wiki2 = factory.createWiki(fileContentStr);
        assertEquals(wiki1, wiki2);

    }

    @Test
    public void hashCodeTest() throws IOException {
        String fileContentStr = getJsonString("json/wiki.json");
        Wiki wiki1 = factory.createWiki(fileContentStr);
        Wiki wiki2 = factory.createWiki(fileContentStr);
        assertEquals(wiki1.hashCode(), wiki2.hashCode());

    }
}
<file_sep>/src/test/java/com/nulabinc/backlog4j/internal/json/PullRequestJSONImplTest.java
package com.nulabinc.backlog4j.internal.json;

import com.nulabinc.backlog4j.Issue;
import com.nulabinc.backlog4j.PullRequest;
import com.nulabinc.backlog4j.PullRequestStatus;
import com.nulabinc.backlog4j.ResponseList;
import com.nulabinc.backlog4j.User;
import org.exparity.hamcrest.date.DateMatchers;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.util.Calendar;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;

/**
 * @author nulab-inc
 */
public class PullRequestJSONImplTest extends AbstractJSONImplTest {
    @Test
    public void createPullRequestListTest() throws IOException {
        String fileContentStr = getJsonString("json/pull_requests.json");
        ResponseList<PullRequest> pullRequests = factory.createPullRequestList(fileContentStr);

        assertEquals(1, pullRequests.size());

        PullRequest pullRequest = pullRequests.get(0);
        assertEquals(2, pullRequest.getId());
        assertEquals(3, pullRequest.getProjectId());
        assertEquals(4, pullRequest.getRepositoryId());
        assertEquals(1, pullRequest.getNumber());
        assertEquals("test", pullRequest.getSummary());
        assertEquals("test data", pullRequest.getDescription());
        assertEquals("master", pullRequest.getBase());
        assertEquals("develop", pullRequest.getBranch());

        PullRequestStatus pullRequestStatus = pullRequest.getStatus();
        assertEquals(1, pullRequestStatus.getId());
        assertEquals("Open", pullRequestStatus.getName());

        User assignee = pullRequest.getAssignee();
        assertEquals(5, assignee.getId());

        Issue issue = pullRequest.getIssue();
        assertEquals(31, issue.getId());

        // todo add test
        /*
        "mergeCommit": null,
        "baseCommit": null,
        "branchCommit": null,
        "closeAt": null,
        "mergeAt": null,
        */
        User user = pullRequest.getCreatedUser();
        assertEquals(1, user.getId());

        Calendar calendar = Calendar.getInstance();
        calendar.set(2015, Calendar.APRIL, 23, 0, 0, 0);
        assertThat(calendar.getTime(), DateMatchers.sameDay(pullRequest.getCreated()));

        User updateUser = pullRequest.getUpdatedUser();
        assertEquals(2, updateUser.getId());

        calendar = Calendar.getInstance();
        calendar.set(2015, Calendar.APRIL, 24, 0, 0, 0);
        assertThat(calendar.getTime(), DateMatchers.sameDay(pullRequest.getUpdated()));

        assertEquals(1, pullRequest.getAttachments().size());
        assertEquals(2, pullRequest.getStars().size());

    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/GetWatchesParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.http.NameValuePair;

/**
 * Parameters for get watch API.
 *
 * @author nulab-inc
 */
public class GetWatchesParams extends GetParams {

    public GetWatchesParams alreadyRead(boolean alreadyRead) {
        parameters.add(new NameValuePair("alreadyRead", String.valueOf(alreadyRead)));
        return this;
    }

    public GetWatchesParams resourceAlreadyRead(boolean resourceAlreadyRead) {
        parameters.add(new NameValuePair("resourceAlreadyRead", String.valueOf(resourceAlreadyRead)));
        return this;
    }
    
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/GroupMethods.java
package com.nulabinc.backlog4j.api;

import com.nulabinc.backlog4j.BacklogException;
import com.nulabinc.backlog4j.Group;
import com.nulabinc.backlog4j.ResponseList;
import com.nulabinc.backlog4j.api.option.CreateGroupParams;
import com.nulabinc.backlog4j.api.option.OffsetParams;
import com.nulabinc.backlog4j.api.option.UpdateGroupParams;

/**
 * Executes Backlog Group APIs.
 *
 * @author nulab-inc
 */
public interface GroupMethods {

    /**
     * Returns all the groups.
     *
     * @return the groups in a list.
     * @throws BacklogException
     */
    ResponseList<Group> getGroups() throws BacklogException;

    /**
     * Returns all the groups.
     *
     * @param params the offset parameters
     * @return the groups in a list.
     * @throws BacklogException
     */
    ResponseList<Group> getGroups(OffsetParams params) throws BacklogException;

    /**
     * Creates a group.
     *
     * @param params the group creating parameters
     * @return the created Group
     * @throws BacklogException
     */
    Group createGroup(CreateGroupParams params) throws BacklogException;

    /**
     * Returns the groups identified by the group's id.
     *
     * @param groupId the group identifier
     * @return the Group.
     * @throws BacklogException
     */
    Group getGroup(Object groupId) throws BacklogException;

    /**
     * Updates the existing group.
     *
     * @param params the group updating parameters
     * @return the updated Group.
     * @throws BacklogException
     */
    Group updateGroup(UpdateGroupParams params) throws BacklogException;

    /**
     * Deletes the existing groups.
     *
     * @param groupId the group identifier
     * @return the deleted Group.
     * @throws BacklogException
     */
    Group deleteGroup(Object groupId) throws BacklogException;
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/internal/json/CountJSONImpl.java
package com.nulabinc.backlog4j.internal.json;

/**
 * @author nulab-inc
 */

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.nulabinc.backlog4j.Count;
import com.nulabinc.backlog4j.User;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

/**
 * @author nulab-inc
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public class CountJSONImpl implements Count {

    private int count;

    public int getCount() {
        return count;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        if (obj.getClass() != getClass()) {
            return false;
        }
        CountJSONImpl rhs = (CountJSONImpl) obj;
        return new EqualsBuilder()
                .append(this.count, rhs.count)
                .isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder()
                .append(count)
                .toHashCode();
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("count", count)
                .toString();
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/WikiHistory.java
package com.nulabinc.backlog4j;

import java.util.Date;

/**
 * The interface for Backlog Wiki page history data.
 *
 * @author nulab-inc
 */
public interface WikiHistory {

    long getPageId();

    String getPageIdAsString();

    int getVersion();

    String getName();

    String getContent();

    User getCreatedUser();

    Date getCreated();

    User getUpdatedUser();

    Date getUpdated();
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/Resolution.java
package com.nulabinc.backlog4j;

/**
 * The interface for Backlog resolution data.
 *
 * @author nulab-inc
 */
public interface Resolution {

    long getId();

    String getIdAsString();

    String getName();

    Issue.ResolutionType getResolutionType();
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/ResolutionMethods.java
package com.nulabinc.backlog4j.api;

import com.nulabinc.backlog4j.BacklogException;
import com.nulabinc.backlog4j.Resolution;
import com.nulabinc.backlog4j.ResponseList;

/**
 * Executes Backlog Resolution APIs.
 *
 * @author nulab-inc
 */
public interface ResolutionMethods {

    /**
     * Returns the resolutions.
     *
     * @return the resolutions in a list
     * @throws BacklogException
     */
    ResponseList<Resolution> getResolutions() throws BacklogException;

}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/GetNotificationCountParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.http.NameValuePair;

/**
 * Parameters for get notification API.
 *
 * @author nulab-inc
 */
public class GetNotificationCountParams extends GetParams {

    public GetNotificationCountParams alreadyRead(boolean alreadyRead) {
        parameters.add(new NameValuePair("alreadyRead", String.valueOf(alreadyRead)));
        return this;
    }

    public GetNotificationCountParams resourceAlreadyRead(boolean resourceAlreadyRead) {
        parameters.add(new NameValuePair("resourceAlreadyRead", String.valueOf(resourceAlreadyRead)));
        return this;
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/WikiMethods.java
package com.nulabinc.backlog4j.api;

import com.nulabinc.backlog4j.*;
import com.nulabinc.backlog4j.api.option.*;

import java.util.List;

/**
 * Executes Backlog Wiki APIs.
 *
 * @author nulab-inc
 */
public interface WikiMethods {

    /**
     * Returns Wiki pages in the project.
     *
     * @param projectIdOrKey the project identifier
     * @return the Wiki pages in a list
     */
    ResponseList<Wiki> getWikis(Object projectIdOrKey);

    /**
     * Returns Wiki pages in the project.
     *
     * @param params the finding wiki parameters
     * @return the Wiki pages in a list
     */
    ResponseList<Wiki> getWikis(GetWikisParams params);

    /**
     * Returns Wiki pages count.
     *
     * @param projectIdOrKey the project identifier
     * @return the Wiki pages count
     */
    int getWikiCount(Object projectIdOrKey);

    /**
     * Returns Wiki page's tags in the project.
     *
     * @param projectIdOrKey the project identifier
     * @return the Wiki page's tags in a list
     */
    ResponseList<WikiTag> getWikiTags(Object projectIdOrKey);

    /**
     * Create a Wiki page in the project.
     *
     * @param params the creating Wiki page parameters
     * @return the created Wiki page
     */
    Wiki createWiki(CreateWikiParams params);

    /**
     * Returns the Wiki page.
     *
     * @param wikiId the Wiki page identifier
     * @return the Wiki page
     */
    Wiki getWiki(Object wikiId);

    /**
     * Updates an existing Wiki page in the project.
     *
     * @param params the updating Wiki page parameters
     * @return the updated Wiki page
     */
    Wiki updateWiki(UpdateWikiParams params);

    /**
     * Deletes the Wiki page.
     *
     * @param wikiId     the Wiki page identifier
     * @param mailNotify
     * @return the deleted Wiki page
     */
    Wiki deleteWiki(Object wikiId, boolean mailNotify);

    // Wiki添付ファイル一覧の取得

    /**
     * Returns the Wiki page's attachment files.
     *
     * @param wikiId the Wiki page identifier
     * @return the Wiki page's attachment files in a list
     */
    ResponseList<Attachment> getWikiAttachments(Object wikiId);

    /**
     * Attaches the files to the Wiki page.
     *
     * @param params the Wiki page's attachment parameters
     * @return the added Wiki page's attachment file
     */
    ResponseList<Attachment> addWikiAttachment(AddWikiAttachmentParams params);

    /**
     * Downloads the Wiki page's attachment file.
     *
     * @param wikiId       the Wiki page identifier
     * @param attachmentId the attachment file identifier
     * @return downloaded file data
     */
    AttachmentData downloadWikiAttachment(Object wikiId, Object attachmentId);
    /**
     * Deletes the Wiki page's attachment file
     *
     * @param wikiId       the Wiki page identifier
     * @param attachmentId the attachment file identifier
     * @return deleted Wiki page's attachment file
     */
    Attachment deleteWikiAttachment(Object wikiId, Object attachmentId);

    /**
     * Returns the Wiki page's shared files.
     *
     * @param wikiId the Wiki page identifier
     * @return the Wiki page's shared files in a list
     */
    ResponseList<SharedFile> getWikiSharedFiles(Object wikiId);

    /**
     * Links the shared files to Wiki.
     *
     * @param wikiId  the Wiki page identifier
     * @param fileIds the shared file identifiers
     * @return the linked shared files
     */
    ResponseList<SharedFile> linkWikiSharedFile(Object wikiId, List fileIds);

    /**
     * Removes link to shared Files from the Wiki.
     *
     * @param wikiId the Wiki page identifier
     * @param fileId the shared file identifier
     * @return the removed link shared file
     */
    SharedFile unlinkWikiSharedFile(Object wikiId, Object fileId);

    /**
     * Returns history of the Wiki page.
     *
     * @param wikiId the Wiki page identifier
     * @return the wiki histories in a list
     */
    ResponseList<WikiHistory> getWikiHistories(Object wikiId);

    /**
     * Returns history of the Wiki page.
     *
     * @param wikiId      the Wiki page identifier
     * @param queryParams the query parameters
     * @return the wiki histories in a list
     */
    ResponseList<WikiHistory> getWikiHistories(Object wikiId, QueryParams queryParams);

    /**
     * Returns list of stars received on the Wiki page.
     *
     * @param wikiId the Wiki page identifier
     * @return the wiki stars in a list
     */
    ResponseList<Star> getWikiStars(Object wikiId);

}
<file_sep>/src/main/java/com/nulabinc/backlog4j/internal/json/customFields/NumericCustomField.java
package com.nulabinc.backlog4j.internal.json.customFields;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.nulabinc.backlog4j.internal.json.JacksonCustomBigdecimalDeserializer;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

import java.math.BigDecimal;

/**
 * @author nulab-inc
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public class NumericCustomField extends CustomFieldJSONImpl {

    private int fieldTypeId = 3;
    @JsonDeserialize(using = JacksonCustomBigdecimalDeserializer.class)
    private BigDecimal value;

    @Override
    public int getFieldTypeId() {
        return fieldTypeId;
    }

    @Override
    public FieldType getFieldType() {
        return FieldType.valueOf(fieldTypeId);
    }

    public BigDecimal getValue() {
        return value;
    }

    public float getFloatValue() {
        return value.floatValue();
    }

    public int getIntValue() {
        return value.intValue();
    }

    public long getLongValue() {
        return value.longValue();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        if (obj.getClass() != getClass()) {
            return false;
        }
        NumericCustomField rhs = (NumericCustomField) obj;
        return new EqualsBuilder()
                .append(getId(), rhs.getId())
                .append(getName(), rhs.getName())
                .append(this.fieldTypeId, rhs.fieldTypeId)
                .append(this.value, rhs.value)
                .isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder()
                .append(getId())
                .append(getName())
                .append(fieldTypeId)
                .append(value)
                .toHashCode();
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("id", getId())
                .append("name", getName())
                .append("fieldTypeId", fieldTypeId)
                .append("value", value)
                .toString();
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/GetRepositoriesParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.http.NameValuePair;

/**
 * Parameters for get repositories API.
 *
 * @author nulab-inc
 */
public class GetRepositoriesParams extends GetParams {


    public GetRepositoriesParams(long projectId) {
        parameters.add(new NameValuePair("projectIdOrKey", String.valueOf(projectId)));
    }

    public GetRepositoriesParams(String projectKey) {
        parameters.add(new NameValuePair("projectIdOrKey", projectKey));
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/PriorityMethods.java
package com.nulabinc.backlog4j.api;

import com.nulabinc.backlog4j.BacklogException;
import com.nulabinc.backlog4j.Priority;
import com.nulabinc.backlog4j.ResponseList;

/**
 * Executes Backlog Priority APIs.
 *
 * @author nulab-inc
 */
public interface PriorityMethods {

    /**
     * Returns the priorities.
     *
     * @return the priorities in a list.
     * @throws BacklogException
     */
    ResponseList<Priority> getPriorities() throws BacklogException;

}
<file_sep>/src/main/java/com/nulabinc/backlog4j/internal/json/ViewedProjectJSONImpl.java
package com.nulabinc.backlog4j.internal.json;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.nulabinc.backlog4j.Project;
import com.nulabinc.backlog4j.User;
import com.nulabinc.backlog4j.ViewedProject;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

import java.util.Date;

/**
 * @author nulab-inc
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public class ViewedProjectJSONImpl implements ViewedProject {

    @JsonDeserialize(as = ProjectJSONImpl.class)
    private Project project;
    @JsonDeserialize(using = JacksonCustomDateDeserializer.class)
    private Date updated;

    @Override
    public Project getProject() {
        return project;
    }

    @Override
    public Date getUpdated() {
        return updated;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        if (obj.getClass() != getClass()) {
            return false;
        }
        ViewedProjectJSONImpl rhs = (ViewedProjectJSONImpl) obj;
        return new EqualsBuilder()
                .append(this.project, rhs.project)
                .append(this.updated, rhs.updated)
                .isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder()
                .append(project)
                .append(updated)
                .toHashCode();
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("project", project)
                .append("updated", updated)
                .toString();
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/Webhook.java
package com.nulabinc.backlog4j;

import java.util.Date;
import java.util.List;

/**
 * The interface for Backlog webhook data.
 *
 * @author nulab-inc
 */
public interface Webhook {

    long getId();

    String getIdAsString();

    String getName();

    String getDescription();

    String getHookUrl();

    boolean isAllEvent();

    List<Activity.Type> getActivityTypeIds();

    User getCreatedUser();

    Date getCreated();

    User getUpdatedUser();

    Date getUpdated();
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/UpdateOrderOfStatusParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.http.NameValuePair;

import java.util.List;

/**
 * Parameters for updates order about status API.
 *
 * @author nulab-inc
 */
public class UpdateOrderOfStatusParams extends PatchParams {

    private Object projectIdOrKey;

    public UpdateOrderOfStatusParams(Object projectIdOrKey, List statusIds) {
        this.projectIdOrKey = projectIdOrKey;
        for (Object statusId : statusIds) {
            parameters.add(new NameValuePair("statusId[]", statusId.toString()));
        }
    }

    public String getProjectIdOrKeyString() {
        return projectIdOrKey.toString();
    }

}
<file_sep>/src/main/java/com/nulabinc/backlog4j/internal/json/activities/GitPushedContent.java
package com.nulabinc.backlog4j.internal.json.activities;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.nulabinc.backlog4j.Content;
import com.nulabinc.backlog4j.Repository;
import com.nulabinc.backlog4j.Revision;
import com.nulabinc.backlog4j.internal.json.RepositoryJSONImpl;
import com.nulabinc.backlog4j.internal.json.RevisionJSONImpl;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

import java.util.Arrays;
import java.util.List;

/**
 * @author nulab-inc
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public class GitPushedContent extends Content {

    @JsonProperty("change_type")
    private String changeType;
    private String ref;
    @JsonProperty("revision_type")
    private String revisionType;
    @JsonDeserialize(as=RepositoryJSONImpl.class)
    private Repository repository;
    @JsonDeserialize(as=RevisionJSONImpl[].class)
    private Revision[] revisions;
    @JsonProperty("revision_count")
    private long revisionCount;

    public String getChangeType() {
        return changeType;
    }

    public String getRef() {
        return ref;
    }

    public String getRevisionType() {
        return revisionType;
    }

    public Repository getRepository() {
        return repository;
    }

    public List<Revision> getRevisions() {
        return Arrays.asList(revisions);
    }

    public long getRevisionCount() {
        return revisionCount;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        if (obj.getClass() != getClass()) {
            return false;
        }
        GitPushedContent rhs = (GitPushedContent) obj;
        return new EqualsBuilder()
                .append(this.changeType, rhs.changeType)
                .append(this.ref, rhs.ref)
                .append(this.revisionType, rhs.revisionType)
                .append(this.repository, rhs.repository)
                .append(this.revisions, rhs.revisions)
                .append(this.revisionCount, rhs.revisionCount)
                .isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder()
                .append(changeType)
                .append(ref)
                .append(revisionType)
                .append(repository)
                .append(revisions)
                .append(revisionCount)
                .toHashCode();
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("changeType", changeType)
                .append("ref", ref)
                .append("revisionType", revisionType)
                .append("repository", repository)
                .append("revisions", revisions)
                .append("revisionCount", revisionCount)
                .toString();
    }
}
<file_sep>/src/test/java/com/nulabinc/backlog4j/internal/json/AccessTokenJSONImplTest.java
package com.nulabinc.backlog4j.internal.json;

import com.nulabinc.backlog4j.auth.AccessToken;
import org.junit.jupiter.api.Test;

import java.io.IOException;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
 * @author nulab-inc
 */
public class AccessTokenJSONImplTest extends AbstractJSONImplTest {
    @Test
    public void createAccessTokenTest() throws IOException {
        String fileContentStr = getJsonString("json/access_token.json");
        AccessToken accessToken = factory.createAccessToken(fileContentStr);

        assertEquals("Bearer", accessToken.getType());
        assertEquals("<KEY>", accessToken.getToken());
        assertEquals(3600, accessToken.getExpires().longValue());
        assertEquals("<KEY>", accessToken.getRefresh());

    }

    @Test
    public void equalsTest() throws IOException {
        String fileContentStr = getJsonString("json/access_token.json");
        AccessToken accessToken1 = factory.createAccessToken(fileContentStr);
        AccessToken accessToken2 = factory.createAccessToken(fileContentStr);
        assertEquals(accessToken1, accessToken2);

    }

    @Test
    public void hashCodeTest() throws IOException {
        String fileContentStr = getJsonString("json/access_token.json");
        AccessToken accessToken1 = factory.createAccessToken(fileContentStr);
        AccessToken accessToken2 = factory.createAccessToken(fileContentStr);

        assertEquals(accessToken1.hashCode(), accessToken2.hashCode());

    }

}
<file_sep>/src/main/java/com/nulabinc/backlog4j/Attachment.java
package com.nulabinc.backlog4j;

import java.util.Date;

/**
 * The interface for Backlog attachment file.
 *
 * @author nulab-inc
 */
public interface Attachment {

    long getId();

    String getIdAsString();

    String getName();

    long getSize();

    User getCreatedUser();

    Date getCreated();

    boolean isImage();

}
<file_sep>/src/main/java/com/nulabinc/backlog4j/internal/json/SharedFileJSONImpl.java
package com.nulabinc.backlog4j.internal.json;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.nulabinc.backlog4j.Revision;
import com.nulabinc.backlog4j.SharedFile;
import com.nulabinc.backlog4j.User;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

import java.util.Date;

/**
 * @author nulab-inc
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public class SharedFileJSONImpl implements SharedFile {

    private long id;
    private String type;
    private String name;
    private String dir;
    private long size;
    @JsonDeserialize(as=UserJSONImpl.class)
    private User createdUser;
    @JsonDeserialize(using = JacksonCustomDateDeserializer.class)
    private Date created;
    @JsonDeserialize(as=UserJSONImpl.class)
    private User updatedUser;
    @JsonDeserialize(using = JacksonCustomDateDeserializer.class)
    private Date updated;

    @Override
    public long getId() {
        return id;
    }

    @Override
    public String getIdAsString() {
        return String.valueOf(this.id);
    }

    @Override
    public String getType() {
        return type;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public boolean isImage() {
        String lowerCase = name.toLowerCase();
        return (lowerCase.endsWith(".jpg") ||
                lowerCase.endsWith(".jpeg") ||
                lowerCase.endsWith(".png") ||
                lowerCase.endsWith(".gif"));
    }


    @Override
    public String getDir() {
        return dir;
    }

    @Override
    public long getSize() {
        return size;
    }

    @Override
    public User getCreatedUser() {
        return createdUser;
    }

    @Override
    public Date getCreated() {
        return created;
    }

    @Override
    public User getUpdatedUser() {
        return updatedUser;
    }

    @Override
    public Date getUpdated() {
        return updated;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        if (obj.getClass() != getClass()) {
            return false;
        }
        SharedFileJSONImpl rhs = (SharedFileJSONImpl) obj;
        return new EqualsBuilder()
                .append(this.id, rhs.id)
                .append(this.type, rhs.type)
                .append(this.name, rhs.name)
                .append(this.dir, rhs.dir)
                .append(this.size, rhs.size)
                .append(this.createdUser, rhs.createdUser)
                .append(this.created, rhs.created)
                .append(this.updatedUser, rhs.updatedUser)
                .append(this.updated, rhs.updated)
                .isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder()
                .append(id)
                .append(type)
                .append(name)
                .append(dir)
                .append(size)
                .append(createdUser)
                .append(created)
                .append(updatedUser)
                .append(updated)
                .toHashCode();
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("id", id)
                .append("type", type)
                .append("name", name)
                .append("dir", dir)
                .append("size", size)
                .append("createdUser", createdUser)
                .append("created", created)
                .append("updatedUser", updatedUser)
                .append("updated", updated)
                .toString();
    }
}
<file_sep>/src/main/java/com/nulabinc/backlog4j/api/option/UpdateCheckBoxCustomFieldParams.java
package com.nulabinc.backlog4j.api.option;

import com.nulabinc.backlog4j.http.NameValuePair;

import java.util.List;

/**
 * Parameters for update checkbox type custom field API.
 *
 * @author nulab-inc
 */
public class UpdateCheckBoxCustomFieldParams extends UpdateCustomFieldParams {

    public UpdateCheckBoxCustomFieldParams(Object projectIdOrKey, Object customFiledId) {
        super(projectIdOrKey, customFiledId);
    }

    public UpdateCheckBoxCustomFieldParams items(List<String> items) {
        if(items != null && items.size() > 0) {
            for (String item : items) {
                parameters.add(new NameValuePair("items[]", item));
            }
        }else{
            parameters.add(new NameValuePair("items[]", ""));
        }
        return this;
    }

    public UpdateCheckBoxCustomFieldParams allowInput(boolean allowInput) {
        parameters.add(new NameValuePair("allowInput", String.valueOf(allowInput)));
        return this;
    }

    public UpdateCheckBoxCustomFieldParams allowAddItem(boolean allowAddItem) {
        parameters.add(new NameValuePair("allowAddItem", String.valueOf(allowAddItem)));
        return this;
    }

    @Override
    public UpdateCheckBoxCustomFieldParams applicableIssueTypes(List<Long> applicableIssueTypes) {
        return (UpdateCheckBoxCustomFieldParams)super.applicableIssueTypes(applicableIssueTypes);
    }

    @Override
    public UpdateCheckBoxCustomFieldParams description(String description) {
        return (UpdateCheckBoxCustomFieldParams)super.description(description);
    }

    @Override
    public UpdateCheckBoxCustomFieldParams required(boolean required) {
        return (UpdateCheckBoxCustomFieldParams)super.required(required);
    }
}
