config) {
+ this.config = config;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/RepositoryHookResponse.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/RepositoryHookResponse.java
new file mode 100644
index 000000000..746e5ef89
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/RepositoryHookResponse.java
@@ -0,0 +1,58 @@
+/******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.github.core;
+
+import java.io.Serializable;
+
+/**
+ * Repository hook response model class
+ */
+public class RepositoryHookResponse implements Serializable {
+
+ /** serialVersionUID */
+ private static final long serialVersionUID = -1168379336046512838L;
+
+ private int code;
+
+ private String message;
+
+ /**
+ * @return code
+ */
+ public int getCode() {
+ return code;
+ }
+
+ /**
+ * @param code
+ * @return this repsonse
+ */
+ public RepositoryHookResponse setCode(int code) {
+ this.code = code;
+ return this;
+ }
+
+ /**
+ * @return message
+ */
+ public String getMessage() {
+ return message;
+ }
+
+ /**
+ * @param message
+ * @return this response
+ */
+ public RepositoryHookResponse setMessage(String message) {
+ this.message = message;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/RepositoryId.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/RepositoryId.java
new file mode 100644
index 000000000..29c2d93c8
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/RepositoryId.java
@@ -0,0 +1,155 @@
+/******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.github.core;
+
+import java.io.Serializable;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * Repository id
+ */
+public class RepositoryId implements IRepositoryIdProvider, Serializable {
+
+ /** serialVersionUID */
+ private static final long serialVersionUID = -57313931704393200L;
+
+ /**
+ * Create repository from url.
+ *
+ * @see #createFromId(String)
+ * @param url
+ * @return repository or null if parsing fails
+ */
+ public static RepositoryId createFromUrl(URL url) {
+ return url != null ? createFromId(url.getPath()) : null;
+ }
+
+ /**
+ * Create repository from id. The id is split on the '/' character and the
+ * first two non-empty segments are interpreted to be the repository owner
+ * and name.
+ *
+ * @param id
+ * @return repository
+ */
+ public static RepositoryId createFromId(String id) {
+ if (id == null || id.length() == 0)
+ return null;
+ String owner = null;
+ String name = null;
+ for (String segment : id.split("/")) //$NON-NLS-1$
+ if (segment.length() > 0)
+ if (owner == null)
+ owner = segment;
+ else if (name == null)
+ name = segment;
+ else
+ break;
+
+ return owner != null && owner.length() > 0 && name != null
+ && name.length() > 0 ? new RepositoryId(owner, name) : null;
+ }
+
+ /**
+ * Create from string URL
+ *
+ * @see #createFromUrl(URL)
+ * @param url
+ * @return repository or null if it could not be parsed from URL path
+ */
+ public static RepositoryId createFromUrl(String url) {
+ if (url == null || url.length() == 0)
+ return null;
+ try {
+ return createFromUrl(new URL(url));
+ } catch (MalformedURLException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Create repository id from given owner and name.
+ *
+ * @param owner
+ * @param name
+ * @return repository id
+ */
+ public static RepositoryId create(String owner, String name) {
+ return new RepositoryId(owner, name);
+ }
+
+ private final String owner;
+
+ private final String name;
+
+ /**
+ * Create repository id with given owner and name. This constructor
+ * validates the parameters and throws an {@link IllegalArgumentException}
+ * if either is null or empty.
+ *
+ * @param owner
+ * must be non-null and non-empty
+ * @param name
+ * must be non-null and non-empty
+ */
+ public RepositoryId(final String owner, final String name) {
+ if (owner == null)
+ throw new IllegalArgumentException("Owner cannot be null"); //$NON-NLS-1$
+ if (owner.length() == 0)
+ throw new IllegalArgumentException("Owner cannot be empty"); //$NON-NLS-1$
+ if (name == null)
+ throw new IllegalArgumentException("Name cannot be null"); //$NON-NLS-1$
+ if (name.length() == 0)
+ throw new IllegalArgumentException("Name cannot be empty"); //$NON-NLS-1$
+
+ this.owner = owner;
+ this.name = name;
+ }
+
+ /**
+ * @return owner
+ */
+ public String getOwner() {
+ return owner;
+ }
+
+ /**
+ * @return name
+ */
+ public String getName() {
+ return name;
+ }
+
+ public String generateId() {
+ return owner + "/" + name; //$NON-NLS-1$
+ }
+
+ @Override
+ public int hashCode() {
+ return generateId().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this)
+ return true;
+ if (!(obj instanceof RepositoryId))
+ return false;
+ RepositoryId other = (RepositoryId) obj;
+ return name.equals(other.name) && owner.equals(other.owner);
+ }
+
+ @Override
+ public String toString() {
+ return generateId();
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/RepositoryIssue.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/RepositoryIssue.java
new file mode 100644
index 000000000..146501fc9
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/RepositoryIssue.java
@@ -0,0 +1,45 @@
+/******************************************************************************
+ * Copyright (c) 2012 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.github.core;
+
+import org.eclipse.egit.github.core.service.IssueService;
+
+/**
+ * Extension of {@link Issue} that includes the {@link Repository} that the
+ * issue is in.
+ *
+ * This type of issue is returned from {@link IssueService} calls that don't
+ * require an {@link IRepositoryIdProvider} to be specified and therefore the
+ * repository information is needed to correlate which issues occur in which
+ * repositories.
+ */
+public class RepositoryIssue extends Issue {
+
+ private static final long serialVersionUID = 6219926097588214812L;
+
+ private Repository repository;
+
+ /**
+ * @return repository
+ */
+ public Repository getRepository() {
+ return repository;
+ }
+
+ /**
+ * @param repository
+ * @return this issue
+ */
+ public RepositoryIssue setRepository(Repository repository) {
+ this.repository = repository;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/RepositoryTag.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/RepositoryTag.java
new file mode 100644
index 000000000..56c7f17d1
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/RepositoryTag.java
@@ -0,0 +1,94 @@
+/******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.github.core;
+
+import java.io.Serializable;
+
+/**
+ * Repository tag model class
+ */
+public class RepositoryTag implements Serializable {
+
+ /** serialVersionUID */
+ private static final long serialVersionUID = 1070566274663989459L;
+
+ private String name;
+
+ private String tarballUrl;
+
+ private String zipballUrl;
+
+ private TypedResource commit;
+
+ /**
+ * @return name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param name
+ * @return this tag
+ */
+ public RepositoryTag setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * @return tarballUrl
+ */
+ public String getTarballUrl() {
+ return tarballUrl;
+ }
+
+ /**
+ * @param tarballUrl
+ * @return this tag
+ */
+ public RepositoryTag setTarballUrl(String tarballUrl) {
+ this.tarballUrl = tarballUrl;
+ return this;
+ }
+
+ /**
+ * @return zipballUrl
+ */
+ public String getZipballUrl() {
+ return zipballUrl;
+ }
+
+ /**
+ * @param zipballUrl
+ * @return this tag
+ */
+ public RepositoryTag setZipballUrl(String zipballUrl) {
+ this.zipballUrl = zipballUrl;
+ return this;
+ }
+
+ /**
+ * @return commit
+ */
+ public TypedResource getCommit() {
+ return commit;
+ }
+
+ /**
+ * @param commit
+ * @return this tag
+ */
+ public RepositoryTag setCommit(TypedResource commit) {
+ this.commit = commit;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/RequestError.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/RequestError.java
new file mode 100644
index 000000000..a8f891de1
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/RequestError.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * GitHub request error class
+ */
+public class RequestError implements Serializable {
+
+ /** serialVersionUID */
+ private static final long serialVersionUID = -7842670602124573940L;
+
+ // This field is required for legacy v2 error support
+ private String error;
+
+ private String message;
+
+ private List errors;
+
+ /**
+ * @return message
+ */
+ public String getMessage() {
+ return message != null ? message : error;
+ }
+
+ /**
+ * Get errors
+ *
+ * @return list of errors
+ */
+ public List getErrors() {
+ return errors;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/SearchIssue.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/SearchIssue.java
new file mode 100644
index 000000000..133ce8359
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/SearchIssue.java
@@ -0,0 +1,259 @@
+/******************************************************************************
+ * Copyright (c) 2012 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.github.core;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+import org.eclipse.egit.github.core.util.DateUtils;
+
+/**
+ * GitHub v2 issue model class.
+ */
+public class SearchIssue implements Serializable {
+
+ private static final long serialVersionUID = 4853048031771824016L;
+
+ private Date createdAt;
+
+ private Date updatedAt;
+
+ private int comments;
+
+ private int number;
+
+ private int position;
+
+ private int votes;
+
+ private List labels;
+
+ private String body;
+
+ private String gravatarId;
+
+ private String htmlUrl;
+
+ private String state;
+
+ private String title;
+
+ private String user;
+
+ /**
+ * @return createdAt
+ */
+ public Date getCreatedAt() {
+ return DateUtils.clone(createdAt);
+ }
+
+ /**
+ * @param createdAt
+ * @return this issue
+ */
+ public SearchIssue setCreatedAt(Date createdAt) {
+ this.createdAt = DateUtils.clone(createdAt);
+ return this;
+ }
+
+ /**
+ * @return updatedAt
+ */
+ public Date getUpdatedAt() {
+ return DateUtils.clone(updatedAt);
+ }
+
+ /**
+ * @param updatedAt
+ * @return this issue
+ */
+ public SearchIssue setUpdatedAt(Date updatedAt) {
+ this.updatedAt = DateUtils.clone(updatedAt);
+ return this;
+ }
+
+ /**
+ * @return comments
+ */
+ public int getComments() {
+ return comments;
+ }
+
+ /**
+ * @param comments
+ * @return this issue
+ */
+ public SearchIssue setComments(int comments) {
+ this.comments = comments;
+ return this;
+ }
+
+ /**
+ * @return number
+ */
+ public int getNumber() {
+ return number;
+ }
+
+ /**
+ * @param number
+ * @return this issue
+ */
+ public SearchIssue setNumber(int number) {
+ this.number = number;
+ return this;
+ }
+
+ /**
+ * @return position
+ */
+ public int getPosition() {
+ return position;
+ }
+
+ /**
+ * @param position
+ * @return this issue
+ */
+ public SearchIssue setPosition(int position) {
+ this.position = position;
+ return this;
+ }
+
+ /**
+ * @return votes
+ */
+ public int getVotes() {
+ return votes;
+ }
+
+ /**
+ * @param votes
+ * @return this issue
+ */
+ public SearchIssue setVotes(int votes) {
+ this.votes = votes;
+ return this;
+ }
+
+ /**
+ * @return labels
+ */
+ public List getLabels() {
+ return labels;
+ }
+
+ /**
+ * @param labels
+ * @return this issue
+ */
+ public SearchIssue setLabels(List labels) {
+ this.labels = labels;
+ return this;
+ }
+
+ /**
+ * @return body
+ */
+ public String getBody() {
+ return body;
+ }
+
+ /**
+ * @param body
+ * @return this issue
+ */
+ public SearchIssue setBody(String body) {
+ this.body = body;
+ return this;
+ }
+
+ /**
+ * @return gravatarId
+ */
+ public String getGravatarId() {
+ return gravatarId;
+ }
+
+ /**
+ * @param gravatarId
+ * @return this issue
+ */
+ public SearchIssue setGravatarId(String gravatarId) {
+ this.gravatarId = gravatarId;
+ return this;
+ }
+
+ /**
+ * @return htmlUrl
+ */
+ public String getHtmlUrl() {
+ return htmlUrl;
+ }
+
+ /**
+ * @param htmlUrl
+ * @return this issue
+ */
+ public SearchIssue setHtmlUrl(String htmlUrl) {
+ this.htmlUrl = htmlUrl;
+ return this;
+ }
+
+ /**
+ * @return state
+ */
+ public String getState() {
+ return state;
+ }
+
+ /**
+ * @param state
+ * @return this issue
+ */
+ public SearchIssue setState(String state) {
+ this.state = state;
+ return this;
+ }
+
+ /**
+ * @return title
+ */
+ public String getTitle() {
+ return title;
+ }
+
+ /**
+ * @param title
+ * @return this issue
+ */
+ public SearchIssue setTitle(String title) {
+ this.title = title;
+ return this;
+ }
+
+ /**
+ * @return user
+ */
+ public String getUser() {
+ return user;
+ }
+
+ /**
+ * @param user
+ * @return this issue
+ */
+ public SearchIssue setUser(String user) {
+ this.user = user;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/SearchRepository.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/SearchRepository.java
new file mode 100644
index 000000000..68e104343
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/SearchRepository.java
@@ -0,0 +1,240 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import org.eclipse.egit.github.core.util.DateUtils;
+
+/**
+ * GitHub v2 repository model class.
+ */
+public class SearchRepository implements IRepositoryIdProvider, Serializable {
+
+ /** serialVersionUID */
+ private static final long serialVersionUID = 978627174722864632L;
+
+ private boolean fork;
+ private boolean hasDownloads;
+ private boolean hasIssues;
+ private boolean hasWiki;
+ @SerializedName("private")
+ private boolean isPrivate;
+
+ private Date createdAt;
+ private Date pushedAt;
+
+ private String description;
+ private String homepage;
+ private String language;
+ private String name;
+ private String owner;
+ private String url;
+
+ private int forks;
+ private int openIssues;
+ private int size;
+ private int watchers;
+
+ /**
+ * Create repository with owner and name
+ *
+ * @param owner
+ * @param name
+ */
+ public SearchRepository(String owner, String name) {
+ if (owner == null)
+ throw new IllegalArgumentException("Owner cannot be null"); //$NON-NLS-1$
+ if (owner.length() == 0)
+ throw new IllegalArgumentException("Owner cannot be empty"); //$NON-NLS-1$
+ if (name == null)
+ throw new IllegalArgumentException("Name cannot be null"); //$NON-NLS-1$
+ if (name.length() == 0)
+ throw new IllegalArgumentException("Name cannot be empty"); //$NON-NLS-1$
+
+ this.owner = owner;
+ this.name = name;
+ }
+
+ /**
+ * Create repository
+ */
+ SearchRepository() {
+
+ }
+
+ /**
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode() {
+ return getId().hashCode();
+ }
+
+ /**
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object obj) {
+ if (obj == this)
+ return true;
+ else if (obj instanceof SearchRepository)
+ return getId().equals(((SearchRepository) obj).getId());
+ else
+ return false;
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ public String toString() {
+ return getId();
+ }
+
+ /**
+ * Get unique identifier for repository
+ *
+ * @return id
+ */
+ public String getId() {
+ return owner + '/' + name;
+ }
+
+ /**
+ * @return owner
+ */
+ public String getOwner() {
+ return owner;
+ }
+
+ /**
+ * @return name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @return fork
+ */
+ public boolean isFork() {
+ return fork;
+ }
+
+ /**
+ * @return hasDownloads
+ */
+ public boolean isHasDownloads() {
+ return hasDownloads;
+ }
+
+ /**
+ * @return hasIssues
+ */
+ public boolean isHasIssues() {
+ return hasIssues;
+ }
+
+ /**
+ * @return hasWiki
+ */
+ public boolean isHasWiki() {
+ return hasWiki;
+ }
+
+ /**
+ * @return isPrivate
+ */
+ public boolean isPrivate() {
+ return isPrivate;
+ }
+
+ /**
+ * @return createdAt
+ */
+ public Date getCreatedAt() {
+ return DateUtils.clone(createdAt);
+ }
+
+ /**
+ * @return pushedAt
+ */
+ public Date getPushedAt() {
+ return DateUtils.clone(pushedAt);
+ }
+
+ /**
+ * @return description
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * @return homepage
+ */
+ public String getHomepage() {
+ return homepage;
+ }
+
+ /**
+ * @return language
+ */
+ public String getLanguage() {
+ return language;
+ }
+
+ /**
+ * @return url
+ */
+ public String getUrl() {
+ return url;
+ }
+
+ /**
+ * @return forks
+ */
+ public int getForks() {
+ return forks;
+ }
+
+ /**
+ * @return openIssues
+ */
+ public int getOpenIssues() {
+ return openIssues;
+ }
+
+ /**
+ * @return size
+ */
+ public int getSize() {
+ return size;
+ }
+
+ /**
+ * @return watchers
+ */
+ public int getWatchers() {
+ return watchers;
+ }
+
+ public String generateId() {
+ final String owner = this.owner;
+ if (owner == null || owner.length() == 0)
+ return null;
+ final String name = this.name;
+ if (name == null || name.length() == 0)
+ return null;
+ return owner + "/" + name; //$NON-NLS-1$
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/ShaResource.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/ShaResource.java
new file mode 100644
index 000000000..1f083a1ef
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/ShaResource.java
@@ -0,0 +1,40 @@
+/******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.github.core;
+
+import java.io.Serializable;
+
+/**
+ * Model class for resources identified by a SHA-1
+ */
+public class ShaResource implements Serializable {
+
+ /** serialVersionUID */
+ private static final long serialVersionUID = 7029184412278953778L;
+
+ private String sha;
+
+ /**
+ * @return sha
+ */
+ public String getSha() {
+ return sha;
+ }
+
+ /**
+ * @param sha
+ * @return this resource
+ */
+ public ShaResource setSha(String sha) {
+ this.sha = sha;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/Tag.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/Tag.java
new file mode 100644
index 000000000..cb8ce472f
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/Tag.java
@@ -0,0 +1,130 @@
+/******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.github.core;
+
+import java.io.Serializable;
+
+/**
+ * Tag model class
+ */
+public class Tag implements Serializable {
+
+ /** serialVersionUID */
+ private static final long serialVersionUID = 8505182933582492676L;
+
+ private CommitUser tagger;
+
+ private String message;
+
+ private String sha;
+
+ private String tag;
+
+ private String url;
+
+ private TypedResource object;
+
+ /**
+ * @return tagger
+ */
+ public CommitUser getTagger() {
+ return tagger;
+ }
+
+ /**
+ * @param tagger
+ * @return this tag
+ */
+ public Tag setTagger(CommitUser tagger) {
+ this.tagger = tagger;
+ return this;
+ }
+
+ /**
+ * @return message
+ */
+ public String getMessage() {
+ return message;
+ }
+
+ /**
+ * @param message
+ * @return this tag
+ */
+ public Tag setMessage(String message) {
+ this.message = message;
+ return this;
+ }
+
+ /**
+ * @return sha
+ */
+ public String getSha() {
+ return sha;
+ }
+
+ /**
+ * @param sha
+ * @return this tag
+ */
+ public Tag setSha(String sha) {
+ this.sha = sha;
+ return this;
+ }
+
+ /**
+ * @return tag
+ */
+ public String getTag() {
+ return tag;
+ }
+
+ /**
+ * @param tag
+ * @return this tag
+ */
+ public Tag setTag(String tag) {
+ this.tag = tag;
+ return this;
+ }
+
+ /**
+ * @return url
+ */
+ public String getUrl() {
+ return url;
+ }
+
+ /**
+ * @param url
+ * @return this tag
+ */
+ public Tag setUrl(String url) {
+ this.url = url;
+ return this;
+ }
+
+ /**
+ * @return object
+ */
+ public TypedResource getObject() {
+ return object;
+ }
+
+ /**
+ * @param object
+ * @return this tag
+ */
+ public Tag setObject(TypedResource object) {
+ this.object = object;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/Team.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/Team.java
new file mode 100644
index 000000000..7c5f568e6
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/Team.java
@@ -0,0 +1,131 @@
+/******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.github.core;
+
+import java.io.Serializable;
+
+/**
+ * Team model class.
+ */
+public class Team implements Serializable {
+
+ /** serialVersionUID */
+ private static final long serialVersionUID = -1844276044857264413L;
+
+ private int id;
+
+ private int membersCount;
+
+ private int reposCount;
+
+ private String name;
+
+ private String permission;
+
+ private String url;
+
+ /**
+ * @return id
+ */
+ public int getId() {
+ return id;
+ }
+
+ /**
+ * @param id
+ * @return this team
+ */
+ public Team setId(int id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * @return membersCount
+ */
+ public int getMembersCount() {
+ return membersCount;
+ }
+
+ /**
+ * @param membersCount
+ * @return this team
+ */
+ public Team setMembersCount(int membersCount) {
+ this.membersCount = membersCount;
+ return this;
+ }
+
+ /**
+ * @return reposCount
+ */
+ public int getReposCount() {
+ return reposCount;
+ }
+
+ /**
+ * @param reposCount
+ * @return this team
+ */
+ public Team setReposCount(int reposCount) {
+ this.reposCount = reposCount;
+ return this;
+ }
+
+ /**
+ * @return name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param name
+ * @return this team
+ */
+ public Team setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * @return permission
+ */
+ public String getPermission() {
+ return permission;
+ }
+
+ /**
+ * @param permission
+ * @return this team
+ */
+ public Team setPermission(String permission) {
+ this.permission = permission;
+ return this;
+ }
+
+ /**
+ * @return url
+ */
+ public String getUrl() {
+ return url;
+ }
+
+ /**
+ * @param url
+ * @return this team
+ */
+ public Team setUrl(String url) {
+ this.url = url;
+ return this;
+ }
+
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/TeamMembership.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/TeamMembership.java
new file mode 100644
index 000000000..cbc3ad8be
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/TeamMembership.java
@@ -0,0 +1,61 @@
+/******************************************************************************
+ * Copyright (c) 2014, 2015 Arizona Board of Regents
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Michael Mathews (Arizona Board of Regents) - (Bug: 447419)
+ * Team Membership API implementation
+ *****************************************************************************/
+package org.eclipse.egit.github.core;
+
+import java.io.Serializable;
+
+/**
+ * Team Membership model class.
+ */
+public class TeamMembership implements Serializable {
+
+ private static final long serialVersionUID = -8207728181588115431L;
+
+ /**
+ * The possible states of a Team Membership
+ */
+ public static enum TeamMembershipState {
+ ACTIVE, PENDING;
+ }
+
+ private TeamMembershipState state;
+
+ private String url;
+
+ /**
+ * @return state
+ */
+ public TeamMembershipState getState() {
+ return state;
+ }
+
+ /**
+ * @param state
+ */
+ public void setState(TeamMembershipState state) {
+ this.state = state;
+ }
+
+ /**
+ * @return url
+ */
+ public String getUrl() {
+ return url;
+ }
+
+ /**
+ * @param url
+ */
+ public void setUrl(String url) {
+ this.url = url;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/Tree.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/Tree.java
new file mode 100644
index 000000000..cb99c3563
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/Tree.java
@@ -0,0 +1,77 @@
+/******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.github.core;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * Tree model class
+ */
+public class Tree implements Serializable {
+
+ /** serialVersionUID */
+ private static final long serialVersionUID = 6518261551932913340L;
+
+ private List tree;
+
+ private String sha;
+
+ private String url;
+
+ /**
+ * @return tree
+ */
+ public List getTree() {
+ return tree;
+ }
+
+ /**
+ * @param tree
+ * @return this tree
+ */
+ public Tree setTree(List tree) {
+ this.tree = tree;
+ return this;
+ }
+
+ /**
+ * @return sha
+ */
+ public String getSha() {
+ return sha;
+ }
+
+ /**
+ * @param sha
+ * @return this tree
+ */
+ public Tree setSha(String sha) {
+ this.sha = sha;
+ return this;
+ }
+
+ /**
+ * @return url
+ */
+ public String getUrl() {
+ return url;
+ }
+
+ /**
+ * @param url
+ * @return this tree
+ */
+ public Tree setUrl(String url) {
+ this.url = url;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/TreeEntry.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/TreeEntry.java
new file mode 100644
index 000000000..f196b1ce6
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/TreeEntry.java
@@ -0,0 +1,165 @@
+/******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.github.core;
+
+import java.io.Serializable;
+
+/**
+ * Tree entry model class
+ */
+public class TreeEntry implements Serializable {
+
+ /** serialVersionUID */
+ private static final long serialVersionUID = -6181332657279059683L;
+
+ /**
+ * TYPE_BLOB
+ */
+ public static final String TYPE_BLOB = "blob"; //$NON-NLS-1$
+
+ /**
+ * TYPE_TREE
+ */
+ public static final String TYPE_TREE = "tree"; //$NON-NLS-1$
+
+ /**
+ * MODE_BLOB
+ */
+ public static final String MODE_BLOB = "100644"; //$NON-NLS-1$
+
+ /**
+ * MODE_BLOB_EXECUTABLE
+ */
+ public static final String MODE_BLOB_EXECUTABLE = "100755"; //$NON-NLS-1$
+
+ /**
+ * MODE_BLOB_SYMLINK
+ */
+ public static final String MODE_BLOB_SYMLINK = "120000"; //$NON-NLS-1$
+
+ /**
+ * MODE_DIRECTORY
+ */
+ public static final String MODE_DIRECTORY = "040000"; //$NON-NLS-1$
+
+ /**
+ * MODE_SUBMODULE
+ */
+ public static final String MODE_SUBMODULE = "160000"; //$NON-NLS-1$
+
+ private long size;
+
+ private String mode;
+
+ private String path;
+
+ private String sha;
+
+ private String type;
+
+ private String url;
+
+ /**
+ * @return size
+ */
+ public long getSize() {
+ return size;
+ }
+
+ /**
+ * @param size
+ * @return this tree entry
+ */
+ public TreeEntry setSize(long size) {
+ this.size = size;
+ return this;
+ }
+
+ /**
+ * @return mode
+ */
+ public String getMode() {
+ return mode;
+ }
+
+ /**
+ * @param mode
+ * @return this tree entry
+ */
+ public TreeEntry setMode(String mode) {
+ this.mode = mode;
+ return this;
+ }
+
+ /**
+ * @return path
+ */
+ public String getPath() {
+ return path;
+ }
+
+ /**
+ * @param path
+ * @return this tree entry
+ */
+ public TreeEntry setPath(String path) {
+ this.path = path;
+ return this;
+ }
+
+ /**
+ * @return sha
+ */
+ public String getSha() {
+ return sha;
+ }
+
+ /**
+ * @param sha
+ * @return this tree entry
+ */
+ public TreeEntry setSha(String sha) {
+ this.sha = sha;
+ return this;
+ }
+
+ /**
+ * @return type
+ */
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * @param type
+ * @return this tree entry
+ */
+ public TreeEntry setType(String type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * @return url
+ */
+ public String getUrl() {
+ return url;
+ }
+
+ /**
+ * @param url
+ * @return this tree entry
+ */
+ public TreeEntry setUrl(String url) {
+ this.url = url;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/TypedResource.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/TypedResource.java
new file mode 100644
index 000000000..88109ee6b
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/TypedResource.java
@@ -0,0 +1,71 @@
+/******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.github.core;
+
+/**
+ * Resource that has type and URL fields
+ */
+public class TypedResource extends ShaResource {
+
+ /** serialVersionUID */
+ private static final long serialVersionUID = -7285665432528832240L;
+
+ /**
+ * TYPE_COMMIT
+ */
+ public static final String TYPE_COMMIT = "commit"; //$NON-NLS-1$
+
+ /**
+ * TYPE_TAG
+ */
+ public static final String TYPE_TAG = "tag"; //$NON-NLS-1$
+
+ /**
+ * TYPE_BLOB
+ */
+ public static final String TYPE_BLOB = "blob"; //$NON-NLS-1$
+
+ private String type;
+
+ private String url;
+
+ /**
+ * @return type
+ */
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * @param type
+ * @return this resource
+ */
+ public TypedResource setType(String type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * @return url
+ */
+ public String getUrl() {
+ return url;
+ }
+
+ /**
+ * @param url
+ * @return this resource
+ */
+ public TypedResource setUrl(String url) {
+ this.url = url;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/User.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/User.java
new file mode 100644
index 000000000..1fe5743f2
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/User.java
@@ -0,0 +1,489 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import org.eclipse.egit.github.core.util.DateUtils;
+
+/**
+ * GitHub user model class.
+ */
+public class User implements Serializable {
+
+ /** serialVersionUID */
+ private static final long serialVersionUID = -1211802439119529774L;
+
+ /**
+ * TYPE_USER
+ */
+ public static final String TYPE_USER = "User"; //$NON-NLS-1$
+
+ /**
+ * TYPE_ORG
+ */
+ public static final String TYPE_ORG = "Organization"; //$NON-NLS-1$
+
+ private boolean hireable;
+
+ private Date createdAt;
+
+ private int collaborators;
+
+ private int diskUsage;
+
+ private int followers;
+
+ private int following;
+
+ private int id;
+
+ private int ownedPrivateRepos;
+
+ private int privateGists;
+
+ private int publicGists;
+
+ private int publicRepos;
+
+ private int totalPrivateRepos;
+
+ private String avatarUrl;
+
+ private String bio;
+
+ private String blog;
+
+ private String company;
+
+ private String email;
+
+ private String gravatarId;
+
+ private String htmlUrl;
+
+ private String location;
+
+ private String login;
+
+ private String name;
+
+ private String type;
+
+ private String url;
+
+ private UserPlan plan;
+
+ /**
+ * @return hireable
+ */
+ public boolean isHireable() {
+ return hireable;
+ }
+
+ /**
+ * @param hireable
+ * @return this user
+ */
+ public User setHireable(boolean hireable) {
+ this.hireable = hireable;
+ return this;
+ }
+
+ /**
+ * @return createdAt
+ */
+ public Date getCreatedAt() {
+ return DateUtils.clone(createdAt);
+ }
+
+ /**
+ * @param createdAt
+ * @return this user
+ */
+ public User setCreatedAt(Date createdAt) {
+ this.createdAt = DateUtils.clone(createdAt);
+ return this;
+ }
+
+ /**
+ * @return collaborators
+ */
+ public int getCollaborators() {
+ return collaborators;
+ }
+
+ /**
+ * @param collaborators
+ * @return this user
+ */
+ public User setCollaborators(int collaborators) {
+ this.collaborators = collaborators;
+ return this;
+ }
+
+ /**
+ * @return diskUsage
+ */
+ public int getDiskUsage() {
+ return diskUsage;
+ }
+
+ /**
+ * @param diskUsage
+ * @return this user
+ */
+ public User setDiskUsage(int diskUsage) {
+ this.diskUsage = diskUsage;
+ return this;
+ }
+
+ /**
+ * @return followers
+ */
+ public int getFollowers() {
+ return followers;
+ }
+
+ /**
+ * @param followers
+ * @return this user
+ */
+ public User setFollowers(int followers) {
+ this.followers = followers;
+ return this;
+ }
+
+ /**
+ * @return following
+ */
+ public int getFollowing() {
+ return following;
+ }
+
+ /**
+ * @param following
+ * @return this user
+ */
+ public User setFollowing(int following) {
+ this.following = following;
+ return this;
+ }
+
+ /**
+ * @return id
+ */
+ public int getId() {
+ return id;
+ }
+
+ /**
+ * @param id
+ * @return this user
+ */
+ public User setId(int id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * @return ownedPrivateRepos
+ */
+ public int getOwnedPrivateRepos() {
+ return ownedPrivateRepos;
+ }
+
+ /**
+ * @param ownedPrivateRepos
+ * @return this user
+ */
+ public User setOwnedPrivateRepos(int ownedPrivateRepos) {
+ this.ownedPrivateRepos = ownedPrivateRepos;
+ return this;
+ }
+
+ /**
+ * @return privateGists
+ */
+ public int getPrivateGists() {
+ return privateGists;
+ }
+
+ /**
+ * @param privateGists
+ * @return this user
+ */
+ public User setPrivateGists(int privateGists) {
+ this.privateGists = privateGists;
+ return this;
+ }
+
+ /**
+ * @return publicGists
+ */
+ public int getPublicGists() {
+ return publicGists;
+ }
+
+ /**
+ * @param publicGists
+ * @return this user
+ */
+ public User setPublicGists(int publicGists) {
+ this.publicGists = publicGists;
+ return this;
+ }
+
+ /**
+ * @return publicRepos
+ */
+ public int getPublicRepos() {
+ return publicRepos;
+ }
+
+ /**
+ * @param publicRepos
+ * @return this user
+ */
+ public User setPublicRepos(int publicRepos) {
+ this.publicRepos = publicRepos;
+ return this;
+ }
+
+ /**
+ * @return totalPrivateRepos
+ */
+ public int getTotalPrivateRepos() {
+ return totalPrivateRepos;
+ }
+
+ /**
+ * @param totalPrivateRepos
+ * @return this user
+ */
+ public User setTotalPrivateRepos(int totalPrivateRepos) {
+ this.totalPrivateRepos = totalPrivateRepos;
+ return this;
+ }
+
+ /**
+ * @return avatarUrl
+ */
+ public String getAvatarUrl() {
+ return avatarUrl;
+ }
+
+ /**
+ * @param avatarUrl
+ * @return this user
+ */
+ public User setAvatarUrl(String avatarUrl) {
+ this.avatarUrl = avatarUrl;
+ return this;
+ }
+
+ /**
+ * @return bio
+ */
+ public String getBio() {
+ return bio;
+ }
+
+ /**
+ * @param bio
+ * @return this user
+ */
+ public User setBio(String bio) {
+ this.bio = bio;
+ return this;
+ }
+
+ /**
+ * @return blog
+ */
+ public String getBlog() {
+ return blog;
+ }
+
+ /**
+ * @param blog
+ * @return this user
+ */
+ public User setBlog(String blog) {
+ this.blog = blog;
+ return this;
+ }
+
+ /**
+ * @return company
+ */
+ public String getCompany() {
+ return company;
+ }
+
+ /**
+ * @param company
+ * @return this user
+ */
+ public User setCompany(String company) {
+ this.company = company;
+ return this;
+ }
+
+ /**
+ * @return email
+ */
+ public String getEmail() {
+ return email;
+ }
+
+ /**
+ * @param email
+ * @return this user
+ */
+ public User setEmail(String email) {
+ this.email = email;
+ return this;
+ }
+
+ /**
+ * @return gravatarId
+ * @deprecated
+ */
+ @Deprecated
+ public String getGravatarId() {
+ return gravatarId;
+ }
+
+ /**
+ * @param gravatarId
+ * @return this user
+ * @deprecated
+ */
+ @Deprecated
+ public User setGravatarId(String gravatarId) {
+ this.gravatarId = gravatarId;
+ return this;
+ }
+
+ /**
+ * @return htmlUrl
+ */
+ public String getHtmlUrl() {
+ return htmlUrl;
+ }
+
+ /**
+ * @param htmlUrl
+ * @return this user
+ */
+ public User setHtmlUrl(String htmlUrl) {
+ this.htmlUrl = htmlUrl;
+ return this;
+ }
+
+ /**
+ * @return location
+ */
+ public String getLocation() {
+ return location;
+ }
+
+ /**
+ * @param location
+ * @return this user
+ */
+ public User setLocation(String location) {
+ this.location = location;
+ return this;
+ }
+
+ /**
+ * @return login
+ */
+ public String getLogin() {
+ return login;
+ }
+
+ /**
+ * @param login
+ * @return this user
+ */
+ public User setLogin(String login) {
+ this.login = login;
+ return this;
+ }
+
+ /**
+ * @return name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param name
+ * @return this user
+ */
+ public User setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * @return type
+ */
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * @param type
+ * @return this user
+ */
+ public User setType(String type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * @return url
+ */
+ public String getUrl() {
+ return url;
+ }
+
+ /**
+ * @param url
+ * @return this user
+ */
+ public User setUrl(String url) {
+ this.url = url;
+ return this;
+ }
+
+ /**
+ * @return plan
+ */
+ public UserPlan getPlan() {
+ return plan;
+ }
+
+ /**
+ * @param plan
+ * @return this user
+ */
+ public User setPlan(UserPlan plan) {
+ this.plan = plan;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/UserPlan.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/UserPlan.java
new file mode 100644
index 000000000..1fc2f6024
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/UserPlan.java
@@ -0,0 +1,94 @@
+/******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.github.core;
+
+import java.io.Serializable;
+
+/**
+ * User plan model class.
+ */
+public class UserPlan implements Serializable {
+
+ /** serialVersionUID */
+ private static final long serialVersionUID = 4759542049129654659L;
+
+ private long collaborators;
+
+ private long privateRepos;
+
+ private long space;
+
+ private String name;
+
+ /**
+ * @return collaborators
+ */
+ public long getCollaborators() {
+ return collaborators;
+ }
+
+ /**
+ * @param collaborators
+ * @return this user plan
+ */
+ public UserPlan setCollaborators(long collaborators) {
+ this.collaborators = collaborators;
+ return this;
+ }
+
+ /**
+ * @return privateRepos
+ */
+ public long getPrivateRepos() {
+ return privateRepos;
+ }
+
+ /**
+ * @param privateRepos
+ * @return this user plan
+ */
+ public UserPlan setPrivateRepos(long privateRepos) {
+ this.privateRepos = privateRepos;
+ return this;
+ }
+
+ /**
+ * @return space
+ */
+ public long getSpace() {
+ return space;
+ }
+
+ /**
+ * @param space
+ * @return this user plan
+ */
+ public UserPlan setSpace(long space) {
+ this.space = space;
+ return this;
+ }
+
+ /**
+ * @return name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param name
+ * @return this user plan
+ */
+ public UserPlan setName(String name) {
+ this.name = name;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/DateFormatter.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/DateFormatter.java
new file mode 100644
index 000000000..e51f1efca
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/DateFormatter.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core.client;
+
+import static org.eclipse.egit.github.core.client.IGitHubConstants.DATE_FORMAT;
+import static org.eclipse.egit.github.core.client.IGitHubConstants.DATE_FORMAT_V2_1;
+import static org.eclipse.egit.github.core.client.IGitHubConstants.DATE_FORMAT_V2_2;
+
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+
+import java.lang.reflect.Type;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * Formatter for date formats present in the GitHub v2 and v3 API.
+ */
+public class DateFormatter implements JsonDeserializer,
+ JsonSerializer {
+
+ private final DateFormat[] formats;
+
+ /**
+ * Create date formatter
+ */
+ public DateFormatter() {
+ formats = new DateFormat[3];
+ formats[0] = new SimpleDateFormat(DATE_FORMAT);
+ formats[1] = new SimpleDateFormat(DATE_FORMAT_V2_1);
+ formats[2] = new SimpleDateFormat(DATE_FORMAT_V2_2);
+ final TimeZone timeZone = TimeZone.getTimeZone("Zulu"); //$NON-NLS-1$
+ for (DateFormat format : formats)
+ format.setTimeZone(timeZone);
+ }
+
+ public Date deserialize(JsonElement json, Type typeOfT,
+ JsonDeserializationContext context) throws JsonParseException {
+ JsonParseException exception = null;
+ final String value = json.getAsString();
+ for (DateFormat format : formats)
+ try {
+ synchronized (format) {
+ return format.parse(value);
+ }
+ } catch (ParseException e) {
+ exception = new JsonParseException(e);
+ }
+ throw exception;
+ }
+
+ public JsonElement serialize(Date date, Type type,
+ JsonSerializationContext context) {
+ final DateFormat primary = formats[0];
+ String formatted;
+ synchronized (primary) {
+ formatted = primary.format(date);
+ }
+ return new JsonPrimitive(formatted);
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/EventFormatter.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/EventFormatter.java
new file mode 100644
index 000000000..cc53bcad4
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/EventFormatter.java
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Jason Tsay (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core.client;
+
+import static com.google.gson.FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES;
+import static org.eclipse.egit.github.core.event.Event.TYPE_COMMIT_COMMENT;
+import static org.eclipse.egit.github.core.event.Event.TYPE_CREATE;
+import static org.eclipse.egit.github.core.event.Event.TYPE_DELETE;
+import static org.eclipse.egit.github.core.event.Event.TYPE_DOWNLOAD;
+import static org.eclipse.egit.github.core.event.Event.TYPE_FOLLOW;
+import static org.eclipse.egit.github.core.event.Event.TYPE_FORK;
+import static org.eclipse.egit.github.core.event.Event.TYPE_FORK_APPLY;
+import static org.eclipse.egit.github.core.event.Event.TYPE_GIST;
+import static org.eclipse.egit.github.core.event.Event.TYPE_GOLLUM;
+import static org.eclipse.egit.github.core.event.Event.TYPE_ISSUES;
+import static org.eclipse.egit.github.core.event.Event.TYPE_ISSUE_COMMENT;
+import static org.eclipse.egit.github.core.event.Event.TYPE_MEMBER;
+import static org.eclipse.egit.github.core.event.Event.TYPE_PUBLIC;
+import static org.eclipse.egit.github.core.event.Event.TYPE_PULL_REQUEST;
+import static org.eclipse.egit.github.core.event.Event.TYPE_PULL_REQUEST_REVIEW_COMMENT;
+import static org.eclipse.egit.github.core.event.Event.TYPE_PUSH;
+import static org.eclipse.egit.github.core.event.Event.TYPE_TEAM_ADD;
+import static org.eclipse.egit.github.core.event.Event.TYPE_WATCH;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+
+import java.lang.reflect.Type;
+import java.util.Date;
+
+import org.eclipse.egit.github.core.event.CommitCommentPayload;
+import org.eclipse.egit.github.core.event.CreatePayload;
+import org.eclipse.egit.github.core.event.DeletePayload;
+import org.eclipse.egit.github.core.event.DownloadPayload;
+import org.eclipse.egit.github.core.event.Event;
+import org.eclipse.egit.github.core.event.EventPayload;
+import org.eclipse.egit.github.core.event.FollowPayload;
+import org.eclipse.egit.github.core.event.ForkApplyPayload;
+import org.eclipse.egit.github.core.event.ForkPayload;
+import org.eclipse.egit.github.core.event.GistPayload;
+import org.eclipse.egit.github.core.event.GollumPayload;
+import org.eclipse.egit.github.core.event.IssueCommentPayload;
+import org.eclipse.egit.github.core.event.IssuesPayload;
+import org.eclipse.egit.github.core.event.MemberPayload;
+import org.eclipse.egit.github.core.event.PublicPayload;
+import org.eclipse.egit.github.core.event.PullRequestPayload;
+import org.eclipse.egit.github.core.event.PullRequestReviewCommentPayload;
+import org.eclipse.egit.github.core.event.PushPayload;
+import org.eclipse.egit.github.core.event.TeamAddPayload;
+import org.eclipse.egit.github.core.event.WatchPayload;
+
+/**
+ * Formats an event's payload with the appropriate class given a certain event
+ * type
+ */
+public class EventFormatter implements JsonDeserializer {
+
+ private final Gson gson = new GsonBuilder()
+ .registerTypeAdapter(Date.class, new DateFormatter())
+ .setFieldNamingPolicy(LOWER_CASE_WITH_UNDERSCORES).create();
+
+ public Event deserialize(JsonElement json, Type typeOfT,
+ JsonDeserializationContext context) throws JsonParseException {
+ final Event event = gson.fromJson(json, Event.class);
+ if (event == null || !json.isJsonObject())
+ return event;
+ final JsonElement rawPayload = json.getAsJsonObject().get("payload");
+ if (rawPayload == null || !rawPayload.isJsonObject())
+ return event;
+ final String type = event.getType();
+ if (type == null || type.length() == 0)
+ return event;
+
+ Class extends EventPayload> payloadClass;
+ if (TYPE_COMMIT_COMMENT.equals(type))
+ payloadClass = CommitCommentPayload.class;
+ else if (TYPE_CREATE.equals(type))
+ payloadClass = CreatePayload.class;
+ else if (TYPE_DELETE.equals(type))
+ payloadClass = DeletePayload.class;
+ else if (TYPE_DOWNLOAD.equals(type))
+ payloadClass = DownloadPayload.class;
+ else if (TYPE_FOLLOW.equals(type))
+ payloadClass = FollowPayload.class;
+ else if (TYPE_FORK.equals(type))
+ payloadClass = ForkPayload.class;
+ else if (TYPE_FORK_APPLY.equals(type))
+ payloadClass = ForkApplyPayload.class;
+ else if (TYPE_GIST.equals(type))
+ payloadClass = GistPayload.class;
+ else if (TYPE_GOLLUM.equals(type))
+ payloadClass = GollumPayload.class;
+ else if (TYPE_ISSUE_COMMENT.equals(type))
+ payloadClass = IssueCommentPayload.class;
+ else if (TYPE_ISSUES.equals(type))
+ payloadClass = IssuesPayload.class;
+ else if (TYPE_MEMBER.equals(type))
+ payloadClass = MemberPayload.class;
+ else if (TYPE_PUBLIC.equals(type))
+ payloadClass = PublicPayload.class;
+ else if (TYPE_PULL_REQUEST.equals(type))
+ payloadClass = PullRequestPayload.class;
+ else if (TYPE_PULL_REQUEST_REVIEW_COMMENT.equals(type))
+ payloadClass = PullRequestReviewCommentPayload.class;
+ else if (TYPE_PUSH.equals(type))
+ payloadClass = PushPayload.class;
+ else if (TYPE_TEAM_ADD.equals(type))
+ payloadClass = TeamAddPayload.class;
+ else if (TYPE_WATCH.equals(type))
+ payloadClass = WatchPayload.class;
+ else
+ return event;
+
+ try {
+ EventPayload typedPayload = context.deserialize(rawPayload,
+ payloadClass);
+ return event.setPayload(typedPayload);
+ } catch (JsonParseException jpe) {
+ // Parse exception here denotes legacy payloads with differing
+ // fields than built-in payload classes provide
+ return event;
+ }
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/GitHubClient.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/GitHubClient.java
new file mode 100644
index 000000000..6fdd52745
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/GitHubClient.java
@@ -0,0 +1,867 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ * Christian Trutz - HttpClient 4.1
+ *******************************************************************************/
+package org.eclipse.egit.github.core.client;
+
+import static com.google.gson.stream.JsonToken.BEGIN_ARRAY;
+import static java.net.HttpURLConnection.HTTP_ACCEPTED;
+import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
+import static java.net.HttpURLConnection.HTTP_CONFLICT;
+import static java.net.HttpURLConnection.HTTP_CREATED;
+import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
+import static java.net.HttpURLConnection.HTTP_GONE;
+import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;
+import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
+import static java.net.HttpURLConnection.HTTP_NO_CONTENT;
+import static java.net.HttpURLConnection.HTTP_OK;
+import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
+import static org.eclipse.egit.github.core.client.IGitHubConstants.AUTH_TOKEN;
+import static org.eclipse.egit.github.core.client.IGitHubConstants.CHARSET_UTF8;
+import static org.eclipse.egit.github.core.client.IGitHubConstants.CONTENT_TYPE_JSON;
+import static org.eclipse.egit.github.core.client.IGitHubConstants.HOST_API;
+import static org.eclipse.egit.github.core.client.IGitHubConstants.HOST_DEFAULT;
+import static org.eclipse.egit.github.core.client.IGitHubConstants.HOST_GISTS;
+import static org.eclipse.egit.github.core.client.IGitHubConstants.PROTOCOL_HTTPS;
+import static org.eclipse.egit.github.core.client.IGitHubConstants.SEGMENT_V3_API;
+import static org.eclipse.egit.github.core.service.GitHubService.ACCEPT_FULL;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonParseException;
+import com.google.gson.stream.JsonReader;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.reflect.Type;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import org.eclipse.egit.github.core.RequestError;
+import org.eclipse.egit.github.core.util.EncodingUtils;
+
+/**
+ * Client class for interacting with GitHub HTTP/JSON API.
+ */
+public class GitHubClient {
+
+ /**
+ * Create API v3 client from URL.
+ *
+ * This creates an HTTPS-based client with a host that contains the host
+ * value of the given URL prefixed with 'api' if the given URL is github.com
+ * or gist.github.com
+ *
+ * @param url
+ * @return client
+ */
+ public static GitHubClient createClient(String url) {
+ try {
+ String host = new URL(url).getHost();
+ if (HOST_DEFAULT.equals(host) || HOST_GISTS.equals(host))
+ host = HOST_API;
+ return new GitHubClient(host);
+ } catch (IOException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /**
+ * Content-Type header
+ */
+ protected static final String HEADER_CONTENT_TYPE = "Content-Type"; //$NON-NLS-1$
+
+ /**
+ * Accept header
+ */
+ protected static final String HEADER_ACCEPT = "Accept"; //$NON-NLS-1$
+
+ /**
+ * Authorization header
+ */
+ protected static final String HEADER_AUTHORIZATION = "Authorization"; //$NON-NLS-1$
+
+ /**
+ * User-Agent header
+ */
+ protected static final String HEADER_USER_AGENT = "User-Agent"; //$NON-NLS-1$
+
+ /**
+ * METHOD_GET
+ */
+ protected static final String METHOD_GET = "GET"; //$NON-NLS-1$
+
+ /**
+ * METHOD_PUT
+ */
+ protected static final String METHOD_PUT = "PUT"; //$NON-NLS-1$
+
+ /**
+ * METHOD_POST
+ */
+ protected static final String METHOD_POST = "POST"; //$NON-NLS-1$
+
+ /**
+ * METHOD_DELETE
+ */
+ protected static final String METHOD_DELETE = "DELETE"; //$NON-NLS-1$
+
+ /**
+ * Default user agent request header value
+ */
+ protected static final String USER_AGENT = "GitHubJava/2.1.0"; //$NON-NLS-1$
+
+ /**
+ * 422 status code for unprocessable entity
+ */
+ protected static final int HTTP_UNPROCESSABLE_ENTITY = 422;
+
+ /**
+ * Base URI
+ */
+ protected final String baseUri;
+
+ /**
+ * Prefix to apply to base URI
+ */
+ protected final String prefix;
+
+ /**
+ * {@link Gson} instance
+ */
+ protected Gson gson = GsonUtils.getGson();
+
+ private String user;
+
+ private String credentials;
+
+ private String userAgent = USER_AGENT;
+
+ private String headerAccept = ACCEPT_FULL;
+
+ private int bufferSize = 8192;
+
+ private int requestLimit = -1;
+
+ private int remainingRequests = -1;
+
+ /**
+ * Create default client
+ */
+ public GitHubClient() {
+ this(HOST_API);
+ }
+
+ /**
+ * Create client for host name
+ *
+ * @param hostname
+ */
+ public GitHubClient(String hostname) {
+ this(hostname, -1, PROTOCOL_HTTPS);
+ }
+
+ /**
+ * Create client for host, port, and scheme
+ *
+ * @param hostname
+ * @param port
+ * @param scheme
+ */
+ public GitHubClient(final String hostname, final int port,
+ final String scheme) {
+ final StringBuilder uri = new StringBuilder(scheme);
+ uri.append("://"); //$NON-NLS-1$
+ uri.append(hostname);
+ if (port > 0)
+ uri.append(':').append(port);
+ baseUri = uri.toString();
+
+ // Use URI prefix on non-standard host names
+ if (HOST_API.equals(hostname))
+ prefix = null;
+ else
+ prefix = SEGMENT_V3_API;
+ }
+
+ /**
+ * Set whether or not serialized data should include fields that are null.
+ *
+ * @param serializeNulls
+ * @return this client
+ */
+ public GitHubClient setSerializeNulls(boolean serializeNulls) {
+ gson = GsonUtils.getGson(serializeNulls);
+ return this;
+ }
+
+ /**
+ * Set the value to set as the user agent header on every request created.
+ * Specifying a null or empty agent parameter will reset this client to use
+ * the default user agent header value.
+ *
+ * @param agent
+ * @return this client
+ */
+ public GitHubClient setUserAgent(final String agent) {
+ if (agent != null && agent.length() > 0)
+ userAgent = agent;
+ else
+ userAgent = USER_AGENT;
+ return this;
+ }
+
+ /**
+ * Set the value to set as the accept header on every request created.
+ * Specifying a null or empty header parameter will reset this client to use
+ * the default accept header value.
+ *
+ * @param header
+ * @return this client
+ */
+ public GitHubClient setHeaderAccept(final String header) {
+ if (header != null && header.length() > 0)
+ headerAccept = header;
+ else
+ headerAccept = ACCEPT_FULL;
+ return this;
+ }
+
+ /**
+ * Configure request with standard headers
+ *
+ * @param request
+ * @return configured request
+ */
+ protected HttpURLConnection configureRequest(final HttpURLConnection request) {
+ if (credentials != null)
+ request.setRequestProperty(HEADER_AUTHORIZATION, credentials);
+ request.setRequestProperty(HEADER_USER_AGENT, userAgent);
+ request.setRequestProperty(HEADER_ACCEPT, headerAccept);
+ return request;
+ }
+
+ /**
+ * Configure request URI
+ *
+ * @param uri
+ * @return configured URI
+ */
+ protected String configureUri(final String uri) {
+ if (prefix == null || uri.startsWith(prefix))
+ return uri;
+ else
+ return prefix + uri;
+ }
+
+ /**
+ * Create connection to URI
+ *
+ * @param uri
+ * @return connection
+ * @throws IOException
+ */
+ protected HttpURLConnection createConnection(String uri) throws IOException {
+ URL url = new URL(createUri(uri));
+ return (HttpURLConnection) url.openConnection();
+ }
+
+ /**
+ * Create connection to URI
+ *
+ * @param uri
+ * @param method
+ * @return connection
+ * @throws IOException
+ */
+ protected HttpURLConnection createConnection(String uri, String method)
+ throws IOException {
+ HttpURLConnection connection = createConnection(uri);
+ connection.setRequestMethod(method);
+ return configureRequest(connection);
+ }
+
+ /**
+ * Create a GET request connection to the URI
+ *
+ * @param uri
+ * @return connection
+ * @throws IOException
+ */
+ protected HttpURLConnection createGet(String uri) throws IOException {
+ return createConnection(uri, METHOD_GET);
+ }
+
+ /**
+ * Create a POST request connection to the URI
+ *
+ * @param uri
+ * @return connection
+ * @throws IOException
+ */
+ protected HttpURLConnection createPost(String uri) throws IOException {
+ return createConnection(uri, METHOD_POST);
+ }
+
+ /**
+ * Create a PUT request connection to the URI
+ *
+ * @param uri
+ * @return connection
+ * @throws IOException
+ */
+ protected HttpURLConnection createPut(String uri) throws IOException {
+ return createConnection(uri, METHOD_PUT);
+ }
+
+ /**
+ * Create a DELETE request connection to the URI
+ *
+ * @param uri
+ * @return connection
+ * @throws IOException
+ */
+ protected HttpURLConnection createDelete(String uri) throws IOException {
+ return createConnection(uri, METHOD_DELETE);
+ }
+
+ /**
+ * Set credentials
+ *
+ * @param user
+ * @param password
+ * @return this client
+ */
+ public GitHubClient setCredentials(final String user, final String password) {
+ this.user = user;
+ if (user != null && user.length() > 0 && password != null
+ && password.length() > 0)
+ credentials = "Basic " //$NON-NLS-1$
+ + EncodingUtils.toBase64(user + ':' + password);
+ else
+ credentials = null;
+ return this;
+ }
+
+ /**
+ * Set OAuth2 token
+ *
+ * @param token
+ * @return this client
+ */
+ public GitHubClient setOAuth2Token(String token) {
+ if (token != null && token.length() > 0)
+ credentials = AUTH_TOKEN + ' ' + token;
+ else
+ credentials = null;
+ return this;
+ }
+
+ /**
+ * Set buffer size used to send the request and read the response
+ *
+ * @param bufferSize
+ * @return this client
+ */
+ public GitHubClient setBufferSize(int bufferSize) {
+ if (bufferSize < 1)
+ throw new IllegalArgumentException(
+ "Buffer size must be greater than zero"); //$NON-NLS-1$
+
+ this.bufferSize = bufferSize;
+ return this;
+ }
+
+ /**
+ * Get the user that this client is currently authenticating as
+ *
+ * @return user or null if not authentication
+ */
+ public String getUser() {
+ return user;
+ }
+
+ /**
+ * Convert object to a JSON string
+ *
+ * @param object
+ * @return JSON string
+ * @throws IOException
+ */
+ protected String toJson(Object object) throws IOException {
+ try {
+ return gson.toJson(object);
+ } catch (JsonParseException jpe) {
+ IOException ioe = new IOException(
+ "Parse exception converting object to JSON"); //$NON-NLS-1$
+ ioe.initCause(jpe);
+ throw ioe;
+ }
+ }
+
+ /**
+ * Parse JSON to specified type
+ *
+ * @param
+ * @param stream
+ * @param type
+ * @return parsed type
+ * @throws IOException
+ */
+ protected V parseJson(InputStream stream, Type type) throws IOException {
+ return parseJson(stream, type, null);
+ }
+
+ /**
+ * Parse JSON to specified type
+ *
+ * @param
+ * @param stream
+ * @param type
+ * @param listType
+ * @return parsed type
+ * @throws IOException
+ */
+ protected V parseJson(InputStream stream, Type type, Type listType)
+ throws IOException {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(
+ stream, CHARSET_UTF8), bufferSize);
+ if (listType == null)
+ try {
+ return gson.fromJson(reader, type);
+ } catch (JsonParseException jpe) {
+ IOException ioe = new IOException(
+ "Parse exception converting JSON to object"); //$NON-NLS-1$
+ ioe.initCause(jpe);
+ throw ioe;
+ } finally {
+ try {
+ reader.close();
+ } catch (IOException ignored) {
+ // Ignored
+ }
+ }
+ else {
+ JsonReader jsonReader = new JsonReader(reader);
+ try {
+ if (jsonReader.peek() == BEGIN_ARRAY)
+ return gson.fromJson(jsonReader, listType);
+ else
+ return gson.fromJson(jsonReader, type);
+ } catch (JsonParseException jpe) {
+ IOException ioe = new IOException(
+ "Parse exception converting JSON to object"); //$NON-NLS-1$
+ ioe.initCause(jpe);
+ throw ioe;
+ } finally {
+ try {
+ jsonReader.close();
+ } catch (IOException ignored) {
+ // Ignored
+ }
+ }
+ }
+ }
+
+ /**
+ * Does status code denote an error
+ *
+ * @param code
+ * @return true if error, false otherwise
+ */
+ protected boolean isError(final int code) {
+ switch (code) {
+ case HTTP_BAD_REQUEST:
+ case HTTP_UNAUTHORIZED:
+ case HTTP_FORBIDDEN:
+ case HTTP_NOT_FOUND:
+ case HTTP_CONFLICT:
+ case HTTP_GONE:
+ case HTTP_UNPROCESSABLE_ENTITY:
+ case HTTP_INTERNAL_ERROR:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Does status code denote a non-error response?
+ *
+ * @param code
+ * @return true if okay, false otherwise
+ */
+ protected boolean isOk(final int code) {
+ switch (code) {
+ case HTTP_OK:
+ case HTTP_CREATED:
+ case HTTP_ACCEPTED:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Is the response empty?
+ *
+ * @param code
+ * @return true if empty, false otherwise
+ */
+ protected boolean isEmpty(final int code) {
+ return HTTP_NO_CONTENT == code;
+ }
+
+ /**
+ * Parse error from response
+ *
+ * @param response
+ * @return request error
+ * @throws IOException
+ */
+ protected RequestError parseError(InputStream response) throws IOException {
+ return parseJson(response, RequestError.class);
+ }
+
+ /**
+ * Get body from response inputs stream
+ *
+ * @param request
+ * @param stream
+ * @return parsed body
+ * @throws IOException
+ */
+ protected Object getBody(GitHubRequest request, InputStream stream)
+ throws IOException {
+ Type type = request.getType();
+ if (type != null)
+ return parseJson(stream, type, request.getArrayType());
+ else
+ return null;
+ }
+
+ /**
+ * Create error exception from response and throw it
+ *
+ * @param response
+ * @param code
+ * @param status
+ * @return non-null newly created {@link IOException}
+ */
+ protected IOException createException(InputStream response, int code,
+ String status) {
+ if (isError(code)) {
+ final RequestError error;
+ try {
+ error = parseError(response);
+ } catch (IOException e) {
+ return e;
+ }
+ if (error != null)
+ return new RequestException(error, code);
+ } else
+ try {
+ response.close();
+ } catch (IOException ignored) {
+ // Ignored
+ }
+ String message;
+ if (status != null && status.length() > 0)
+ message = status + " (" + code + ')'; //$NON-NLS-1$
+ else
+ message = "Unknown error occurred (" + code + ')'; //$NON-NLS-1$
+ return new IOException(message);
+ }
+
+ /**
+ * Post to URI
+ *
+ * @param uri
+ * @throws IOException
+ */
+ public void post(String uri) throws IOException {
+ post(uri, null, null);
+ }
+
+ /**
+ * Put to URI
+ *
+ * @param uri
+ * @throws IOException
+ */
+ public void put(String uri) throws IOException {
+ put(uri, null, null);
+ }
+
+ /**
+ * Delete resource at URI. This method will throw an {@link IOException}
+ * when the response status is not a 204 (No Content).
+ *
+ * @param uri
+ * @throws IOException
+ */
+ public void delete(String uri) throws IOException {
+ delete(uri, null);
+ }
+
+ /**
+ * Send parameters to output stream of request
+ *
+ * @param request
+ * @param params
+ * @throws IOException
+ */
+ protected void sendParams(HttpURLConnection request, Object params)
+ throws IOException {
+ request.setDoOutput(true);
+ if (params != null) {
+ request.setRequestProperty(HEADER_CONTENT_TYPE, CONTENT_TYPE_JSON
+ + "; charset=" + CHARSET_UTF8); //$NON-NLS-1$
+ byte[] data = toJson(params).getBytes(CHARSET_UTF8);
+ request.setFixedLengthStreamingMode(data.length);
+ BufferedOutputStream output = new BufferedOutputStream(
+ request.getOutputStream(), bufferSize);
+ try {
+ output.write(data);
+ output.flush();
+ } finally {
+ try {
+ output.close();
+ } catch (IOException ignored) {
+ // Ignored
+ }
+ }
+ } else {
+ request.setFixedLengthStreamingMode(0);
+ request.setRequestProperty("Content-Length", "0");
+ }
+ }
+
+ private V sendJson(final HttpURLConnection request,
+ final Object params, final Type type) throws IOException {
+ sendParams(request, params);
+ final int code = request.getResponseCode();
+ updateRateLimits(request);
+ if (isOk(code))
+ if (type != null)
+ return parseJson(getStream(request), type);
+ else
+ return null;
+ if (isEmpty(code))
+ return null;
+ throw createException(getStream(request), code,
+ request.getResponseMessage());
+ }
+
+ /**
+ * Create full URI from path
+ *
+ * @param path
+ * @return uri
+ */
+ protected String createUri(final String path) {
+ return baseUri + configureUri(path);
+ }
+
+ /**
+ * Get response stream from GET to URI. It is the responsibility of the
+ * calling method to close the returned stream.
+ *
+ * @param request
+ * @return stream
+ * @throws IOException
+ */
+ public InputStream getStream(final GitHubRequest request)
+ throws IOException {
+ return getResponseStream(createGet(request.generateUri()));
+ }
+
+ /**
+ * Get response stream from POST to URI. It is the responsibility of the
+ * calling method to close the returned stream.
+ *
+ * @param uri
+ * @param params
+ * @return stream
+ * @throws IOException
+ */
+ public InputStream postStream(final String uri, final Object params)
+ throws IOException {
+ HttpURLConnection connection = createPost(uri);
+ sendParams(connection, params);
+ return getResponseStream(connection);
+ }
+
+ /**
+ * Get response stream for request
+ *
+ * @param request
+ * @return stream
+ * @throws IOException
+ */
+ protected InputStream getResponseStream(final HttpURLConnection request)
+ throws IOException {
+ InputStream stream = getStream(request);
+ int code = request.getResponseCode();
+ updateRateLimits(request);
+ if (isOk(code))
+ return stream;
+ else
+ throw createException(stream, code, request.getResponseMessage());
+ }
+
+ /**
+ * Get stream from request
+ *
+ * @param request
+ * @return stream
+ * @throws IOException
+ */
+ protected InputStream getStream(HttpURLConnection request)
+ throws IOException {
+ if (request.getResponseCode() < HTTP_BAD_REQUEST)
+ return request.getInputStream();
+ else {
+ InputStream stream = request.getErrorStream();
+ return stream != null ? stream : request.getInputStream();
+ }
+ }
+
+ /**
+ * Get response from URI and bind to specified type
+ *
+ * @param request
+ * @return response
+ * @throws IOException
+ */
+ public GitHubResponse get(GitHubRequest request) throws IOException {
+ HttpURLConnection httpRequest = createGet(request.generateUri());
+ String accept = request.getResponseContentType();
+ if (accept != null)
+ httpRequest.setRequestProperty(HEADER_ACCEPT, accept);
+ final int code = httpRequest.getResponseCode();
+ updateRateLimits(httpRequest);
+ if (isOk(code))
+ return new GitHubResponse(httpRequest, getBody(request,
+ getStream(httpRequest)));
+ if (isEmpty(code))
+ return new GitHubResponse(httpRequest, null);
+ throw createException(getStream(httpRequest), code,
+ httpRequest.getResponseMessage());
+ }
+
+ /**
+ * Post data to URI
+ *
+ * @param
+ * @param uri
+ * @param params
+ * @param type
+ * @return response
+ * @throws IOException
+ */
+ public V post(final String uri, final Object params, final Type type)
+ throws IOException {
+ HttpURLConnection request = createPost(uri);
+ return sendJson(request, params, type);
+ }
+
+ /**
+ * Put data to URI
+ *
+ * @param
+ * @param uri
+ * @param params
+ * @param type
+ * @return response
+ * @throws IOException
+ */
+ public V put(final String uri, final Object params, final Type type)
+ throws IOException {
+ HttpURLConnection request = createPut(uri);
+ return sendJson(request, params, type);
+ }
+
+ /**
+ * Delete resource at URI. This method will throw an {@link IOException}
+ * when the response status is not a 204 (No Content).
+ *
+ * @param uri
+ * @param params
+ * @throws IOException
+ */
+ public void delete(final String uri, final Object params)
+ throws IOException {
+ HttpURLConnection request = createDelete(uri);
+ if (params != null)
+ sendParams(request, params);
+ final int code = request.getResponseCode();
+ updateRateLimits(request);
+ if (!isEmpty(code))
+ throw new RequestException(parseError(getStream(request)), code);
+ }
+
+ /**
+ * Update rate limits present in response headers
+ *
+ * @param request
+ * @return this client
+ */
+ protected GitHubClient updateRateLimits(HttpURLConnection request) {
+ String limit = request.getHeaderField("X-RateLimit-Limit");
+ if (limit != null && limit.length() > 0)
+ try {
+ requestLimit = Integer.parseInt(limit);
+ } catch (NumberFormatException nfe) {
+ requestLimit = -1;
+ }
+ else
+ requestLimit = -1;
+
+ String remaining = request.getHeaderField("X-RateLimit-Remaining");
+ if (remaining != null && remaining.length() > 0)
+ try {
+ remainingRequests = Integer.parseInt(remaining);
+ } catch (NumberFormatException nfe) {
+ remainingRequests = -1;
+ }
+ else
+ remainingRequests = -1;
+
+ return this;
+ }
+
+ /**
+ * Get number of requests remaining before rate limiting occurs
+ *
+ * This will be the value of the 'X-RateLimit-Remaining' header from the
+ * last request made
+ *
+ * @return remainingRequests or -1 if not present in the response
+ */
+ public int getRemainingRequests() {
+ return remainingRequests;
+ }
+
+ /**
+ * Get number of requests that {@link #getRemainingRequests()} counts down
+ * from as each request is made
+ *
+ * This will be the value of the 'X-RateLimit-Limit' header from the last
+ * request made
+ *
+ * @return requestLimit or -1 if not present in the response
+ */
+ public int getRequestLimit() {
+ return requestLimit;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/GitHubRequest.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/GitHubRequest.java
new file mode 100644
index 000000000..235027470
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/GitHubRequest.java
@@ -0,0 +1,183 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core.client;
+
+import java.lang.reflect.Type;
+import java.util.Map;
+
+import org.eclipse.egit.github.core.util.UrlUtils;
+
+/**
+ * GitHub API request class that contains the URI and parameters of the request
+ * as well as the expected {@link Type} of the response.
+ *
+ * The {@link #generateUri()} method should be used to build a full URI that
+ * contains both the base uri and the parameters set.
+ */
+public class GitHubRequest {
+
+ private String uri;
+
+ private Map params;
+
+ private Type type;
+
+ private String responseContentType;
+
+ private Type arrayType;
+
+ /**
+ * Create empty request
+ */
+ public GitHubRequest() {
+
+ }
+
+ /**
+ * Set type to expect if first token is a beginning of an array
+ *
+ * @param arrayType
+ * @return this request
+ */
+ public GitHubRequest setArrayType(Type arrayType) {
+ this.arrayType = arrayType;
+ return this;
+ }
+
+ /**
+ * @return arrayType
+ */
+ public Type getArrayType() {
+ return arrayType;
+ }
+
+ /**
+ * @return uri
+ */
+ public String getUri() {
+ return uri;
+ }
+
+ /**
+ * Add request params to URI
+ *
+ * @param uri
+ */
+ protected void addParams(final StringBuilder uri) {
+ UrlUtils.addParams(getParams(), uri);
+ }
+
+ /**
+ * Generate full uri
+ *
+ * @return uri
+ */
+ public String generateUri() {
+ final String baseUri = uri;
+ if (baseUri == null)
+ return null;
+ if (baseUri.indexOf('?') != -1)
+ return baseUri;
+ final StringBuilder params = new StringBuilder();
+ addParams(params);
+ if (params.length() > 0)
+ return baseUri + '?' + params;
+ else
+ return baseUri;
+ }
+
+ /**
+ * @param uri
+ * @return this request
+ */
+ public GitHubRequest setUri(StringBuilder uri) {
+ return setUri(uri != null ? uri.toString() : null);
+ }
+
+ /**
+ * @param uri
+ * @return this request
+ */
+ public GitHubRequest setUri(String uri) {
+ this.uri = uri;
+ return this;
+ }
+
+ /**
+ * @return params
+ */
+ public Map getParams() {
+ return params;
+ }
+
+ /**
+ * @param params
+ * @return this request
+ */
+ public GitHubRequest setParams(Map params) {
+ this.params = params;
+ return this;
+ }
+
+ /**
+ * @return type
+ */
+ public Type getType() {
+ return type;
+ }
+
+ /**
+ * @param type
+ * @return this request
+ */
+ public GitHubRequest setType(Type type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * @return responseContentType
+ */
+ public String getResponseContentType() {
+ return responseContentType;
+ }
+
+ /**
+ * Set the desired response content type
+ *
+ * @param responseContentType
+ * @return this request
+ */
+ public GitHubRequest setResponseContentType(String responseContentType) {
+ this.responseContentType = responseContentType;
+ return this;
+ }
+
+ public int hashCode() {
+ final String fullUri = generateUri();
+ return fullUri != null ? fullUri.hashCode() : super.hashCode();
+ }
+
+ public boolean equals(final Object obj) {
+ if (obj == this)
+ return true;
+ if (!(obj instanceof GitHubRequest))
+ return false;
+ final String fullUri = generateUri();
+ final String objUri = ((GitHubRequest) obj).generateUri();
+ return fullUri != null && objUri != null && fullUri.equals(objUri);
+ }
+
+ public String toString() {
+ final String fullUri = generateUri();
+ return fullUri != null ? fullUri : super.toString();
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/GitHubResponse.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/GitHubResponse.java
new file mode 100644
index 000000000..df426edcb
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/GitHubResponse.java
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core.client;
+
+import java.net.HttpURLConnection;
+
+/**
+ * GitHub API response class that provides the parsed response body as well as
+ * any links to the first, previous, next, and last responses.
+ */
+public class GitHubResponse {
+
+ /**
+ * HTTP response
+ */
+ protected final HttpURLConnection response;
+
+ /**
+ * Response body
+ */
+ protected final Object body;
+
+ /**
+ * Links to other pages
+ */
+ protected PageLinks links;
+
+ /**
+ * Create response
+ *
+ * @param response
+ * @param body
+ */
+ public GitHubResponse(HttpURLConnection response, Object body) {
+ this.response = response;
+ this.body = body;
+ }
+
+ /**
+ * Get header value
+ *
+ * @param name
+ * @return value
+ */
+ public String getHeader(String name) {
+ return response.getHeaderField(name);
+ }
+
+ /**
+ * Get page links
+ *
+ * @return links
+ */
+ protected PageLinks getLinks() {
+ if (links == null)
+ links = new PageLinks(this);
+ return links;
+ }
+
+ /**
+ * Get link uri to first page
+ *
+ * @return possibly null uri
+ */
+ public String getFirst() {
+ return getLinks().getFirst();
+ }
+
+ /**
+ * Get link uri to previous page
+ *
+ * @return possibly null uri
+ */
+ public String getPrevious() {
+ return getLinks().getPrev();
+ }
+
+ /**
+ * Get link uri to next page
+ *
+ * @return possibly null uri
+ */
+ public String getNext() {
+ return getLinks().getNext();
+ }
+
+ /**
+ * Get link uri to last page
+ *
+ * @return possibly null uri
+ */
+ public String getLast() {
+ return getLinks().getLast();
+ }
+
+ /**
+ * Parsed response body
+ *
+ * @return body
+ */
+ public Object getBody() {
+ return body;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/GsonUtils.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/GsonUtils.java
new file mode 100644
index 000000000..29bcc9d4f
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/GsonUtils.java
@@ -0,0 +1,145 @@
+/******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.github.core.client;
+
+import static com.google.gson.FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import java.io.Reader;
+import java.lang.reflect.Type;
+import java.util.Date;
+
+import org.eclipse.egit.github.core.event.Event;
+
+/**
+ * Gson utilities.
+ */
+public abstract class GsonUtils {
+
+ private static final Gson GSON = createGson(true);
+
+ private static final Gson GSON_NO_NULLS = createGson(false);
+
+ /**
+ * Create the standard {@link Gson} configuration
+ *
+ * @return created gson, never null
+ */
+ public static final Gson createGson() {
+ return createGson(true);
+ }
+
+ /**
+ * Create the standard {@link Gson} configuration
+ *
+ * @param serializeNulls
+ * whether nulls should be serialized
+ *
+ * @return created gson, never null
+ */
+ public static final Gson createGson(final boolean serializeNulls) {
+ final GsonBuilder builder = new GsonBuilder();
+ builder.registerTypeAdapter(Date.class, new DateFormatter());
+ builder.registerTypeAdapter(Event.class, new EventFormatter());
+ builder.setFieldNamingPolicy(LOWER_CASE_WITH_UNDERSCORES);
+ if (serializeNulls)
+ builder.serializeNulls();
+ return builder.create();
+ }
+
+ /**
+ * Get reusable pre-configured {@link Gson} instance
+ *
+ * @return Gson instance
+ */
+ public static final Gson getGson() {
+ return GSON;
+ }
+
+ /**
+ * Get reusable pre-configured {@link Gson} instance
+ *
+ * @param serializeNulls
+ * @return Gson instance
+ */
+ public static final Gson getGson(final boolean serializeNulls) {
+ return serializeNulls ? GSON : GSON_NO_NULLS;
+ }
+
+ /**
+ * Convert object to json
+ *
+ * @param object
+ * @return json string
+ */
+ public static final String toJson(final Object object) {
+ return toJson(object, true);
+ }
+
+ /**
+ * Convert object to json
+ *
+ * @param object
+ * @param includeNulls
+ * @return json string
+ */
+ public static final String toJson(final Object object,
+ final boolean includeNulls) {
+ return includeNulls ? GSON.toJson(object) : GSON_NO_NULLS
+ .toJson(object);
+ }
+
+ /**
+ * Convert string to given type
+ *
+ * @param json
+ * @param type
+ * @return instance of type
+ */
+ public static final V fromJson(String json, Class type) {
+ return GSON.fromJson(json, type);
+ }
+
+ /**
+ * Convert string to given type
+ *
+ * @param json
+ * @param type
+ * @return instance of type
+ */
+ public static final V fromJson(String json, Type type) {
+ return GSON.fromJson(json, type);
+ }
+
+ /**
+ * Convert content of reader to given type
+ *
+ * @param reader
+ * @param type
+ * @return instance of type
+ */
+ public static final V fromJson(Reader reader, Class type) {
+ return GSON.fromJson(reader, type);
+ }
+
+ /**
+ * Convert content of reader to given type
+ *
+ * @param reader
+ * @param type
+ * @return instance of type
+ */
+ public static final V fromJson(Reader reader, Type type) {
+ return GSON.fromJson(reader, type);
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/IGitHubConstants.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/IGitHubConstants.java
new file mode 100644
index 000000000..eb0518b99
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/IGitHubConstants.java
@@ -0,0 +1,196 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core.client;
+
+/**
+ * GitHub constants
+ */
+public interface IGitHubConstants {
+
+ /** */
+ String AUTH_TOKEN = "token"; //$NON-NLS-1$
+
+ /** */
+ String CHARSET_UTF8 = "UTF-8"; //$NON-NLS-1$
+ /** */
+ String CHARSET_ISO_8859_1 = "ISO-8859-1"; //$NON-NLS-1$
+ /** */
+ String CONTENT_TYPE_JSON = "application/json"; //$NON-NLS-1$
+
+ /** */
+ String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; //$NON-NLS-1$
+ /** */
+ String DATE_FORMAT_V2_1 = "yyyy/MM/dd HH:mm:ss Z"; //$NON-NLS-1$
+ /** */
+ String DATE_FORMAT_V2_2 = "yyyy-MM-dd'T'HH:mm:ss"; //$NON-NLS-1$
+
+ /** */
+ String HEADER_LINK = "Link"; //$NON-NLS-1$
+ /** */
+ String HEADER_NEXT = "X-Next"; //$NON-NLS-1$
+ /** */
+ String HEADER_LAST = "X-Last"; //$NON-NLS-1$
+
+ /** */
+ String HOST_API = "api.github.com"; //$NON-NLS-1$
+ /** */
+ String HOST_DEFAULT = "github.com"; //$NON-NLS-1$
+ /** */
+ String HOST_GISTS = "gist.github.com"; //$NON-NLS-1$
+
+ /** */
+ String META_REL = "rel"; //$NON-NLS-1$
+ /** */
+ String META_LAST = "last"; //$NON-NLS-1$
+ /** */
+ String META_NEXT = "next"; //$NON-NLS-1$
+ /** */
+ String META_FIRST = "first"; //$NON-NLS-1$
+ /** */
+ String META_PREV = "prev"; //$NON-NLS-1$
+
+ /** */
+ String PARAM_LANGUAGE = "language"; //$NON-NLS-1$
+ /** */
+ String PARAM_PAGE = "page"; //$NON-NLS-1$
+ /** */
+ String PARAM_PER_PAGE = "per_page"; //$NON-NLS-1$
+ /** */
+ String PARAM_START_PAGE = "start_page"; //$NON-NLS-1$
+
+ /** */
+ String PROTOCOL_HTTPS = "https"; //$NON-NLS-1$
+
+ /** */
+ String SCHEME_OAUTH2 = "oauth2"; //$NON-NLS-1$
+
+ /** */
+ String SEGMENT_AUTHORIZATIONS = "/authorizations"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_BLOBS = "/blobs"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_BRANCHES = "/branches"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_COLLABORATORS = "/collaborators"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_COMMENTS = "/comments"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_CONTENTS= "/contents"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_CONTRIBUTORS = "/contributors"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_COMMITS = "/commits"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_COMPARE = "/compare"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_CREATE = "/create"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_DOWNLOADS = "/downloads"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_EMAILS = "/emails"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_EVENTS = "/events"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_FILES = "/files"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_FOLLOWERS = "/followers"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_FOLLOWING = "/following"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_FORK = "/fork"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_FORKS = "/forks"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_GISTS = "/gists"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_GIT = "/git"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_HOOKS = "/hooks"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_ISSUES = "/issues"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_KEYS = "/keys"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_LABELS = "/labels"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_LEGACY = "/legacy"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_LANGUAGES = "/languages"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_MARKDOWN = "/markdown"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_MEMBERS = "/members"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_MERGE = "/merge"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_MILESTONES = "/milestones"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_NETWORKS = "/networks"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_ORGANIZATIONS = "/organizations"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_MEMBERSHIPS = "/memberships"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_ORGS = "/orgs"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_PUBLIC = "/public"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_PUBLIC_MEMBERS = "/public_members"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_PULLS = "/pulls"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_README = "/readme"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_RECEIVED_EVENTS = "/received_events"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_REFS = "/refs"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_REPOS = "/repos"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_REPOSITORIES = "/repositories"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_SEARCH = "/search"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_SHOW = "/show"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_STAR = "/star"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_STARRED = "/starred"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_STATUSES = "/statuses"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_TAGS = "/tags"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_TEAMS = "/teams"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_TEST = "/test"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_TREES = "/trees"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_USER = "/user"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_USERS = "/users"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_WATCHED = "/watched"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_WATCHERS = "/watchers"; //$NON-NLS-1$
+ /** */
+ String SEGMENT_V3_API = "/api/v3"; //$NON-NLS-1$
+
+ /** */
+ String SUBDOMAIN_API = "api"; //$NON-NLS-1$
+
+ /** */
+ String SUFFIX_GIT = ".git"; //$NON-NLS-1$
+
+ /** */
+ String URL_API = PROTOCOL_HTTPS + "://" + HOST_API; //$NON-NLS-1$
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/NoSuchPageException.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/NoSuchPageException.java
new file mode 100644
index 000000000..83846ab8a
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/NoSuchPageException.java
@@ -0,0 +1,51 @@
+/******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.github.core.client;
+
+import java.io.IOException;
+import java.util.NoSuchElementException;
+
+/**
+ * Exception class to be thrown when iterating over pages fails. This exception
+ * wraps an {@link IOException} that is the actual exception that occurred when
+ * the page request was made.
+ */
+public class NoSuchPageException extends NoSuchElementException {
+
+ /**
+ * serialVersionUID
+ */
+ private static final long serialVersionUID = 6795637952359586293L;
+
+ /**
+ * Cause exception
+ */
+ protected final IOException cause;
+
+ /**
+ * Create no such page exception
+ *
+ * @param cause
+ */
+ public NoSuchPageException(IOException cause) {
+ this.cause = cause;
+ }
+
+ @Override
+ public String getMessage() {
+ return cause != null ? cause.getMessage() : super.getMessage();
+ }
+
+ @Override
+ public IOException getCause() {
+ return cause;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/PageIterator.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/PageIterator.java
new file mode 100644
index 000000000..1a10cce99
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/PageIterator.java
@@ -0,0 +1,211 @@
+/******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.github.core.client;
+
+import static org.eclipse.egit.github.core.client.IGitHubConstants.PARAM_PAGE;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import org.eclipse.egit.github.core.IResourceProvider;
+import org.eclipse.egit.github.core.util.UrlUtils;
+
+/**
+ * Iterator for getting paged responses. Each call to {@link #next()} will make
+ * a client request for the next page of resources using the URI returned from
+ * the previous request.
+ *
+ * The {@link #hasNext()} method can be used to determine if the last executed
+ * request contained the location of the next page of results.
+ *
+ * This iterator also provides the next and last page numbers as well as the
+ * next and last URIs.
+ *
+ * @param
+ * type of resource being iterated over
+ */
+public class PageIterator implements Iterator>,
+ Iterable> {
+
+ /**
+ * Request
+ */
+ protected final PagedRequest request;
+
+ /**
+ * Client
+ */
+ protected final GitHubClient client;
+
+ /**
+ * Current page number
+ */
+ protected int nextPage;
+
+ /**
+ * Last page number
+ */
+ protected int lastPage;
+
+ /**
+ * Next uri to be fetched
+ */
+ protected String next;
+
+ /**
+ * Last uri to be fetched
+ */
+ protected String last;
+
+ /**
+ * Create page iterator
+ *
+ * @param request
+ * @param client
+ */
+ public PageIterator(PagedRequest request, GitHubClient client) {
+ this.request = request;
+ this.client = client;
+ next = request.getUri();
+ nextPage = parsePageNumber(next);
+ }
+
+ /**
+ * Parse page number from uri
+ *
+ * @param uri
+ * @return page number
+ */
+ protected int parsePageNumber(String uri) {
+ if (uri == null || uri.length() == 0)
+ return -1;
+ final URI parsed;
+ try {
+ parsed = new URI(uri);
+ } catch (URISyntaxException e) {
+ return -1;
+ }
+ final String param = UrlUtils.getParam(parsed, PARAM_PAGE);
+ if (param == null || param.length() == 0)
+ return -1;
+ try {
+ return Integer.parseInt(param);
+ } catch (NumberFormatException nfe) {
+ return -1;
+ }
+ }
+
+ /**
+ * Get number of next page to be read
+ *
+ * @return next page
+ */
+ public int getNextPage() {
+ return nextPage;
+ }
+
+ /**
+ * Get number of last page
+ *
+ * @return page number
+ */
+ public int getLastPage() {
+ return lastPage;
+ }
+
+ /**
+ * Get URI of next request
+ *
+ * @return next page uri
+ */
+ public String getNextUri() {
+ return next;
+ }
+
+ /**
+ * Get uri of last page
+ *
+ * @return last page uri
+ */
+ public String getLastUri() {
+ return last;
+ }
+
+ public boolean hasNext() {
+ return nextPage == 0 || next != null;
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("Remove not supported"); //$NON-NLS-1$
+ }
+
+ @SuppressWarnings("unchecked")
+ public Collection next() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+ if (next != null)
+ if (nextPage < 1)
+ request.setUri(next);
+ else
+ try {
+ request.setUri(new URL(next).getFile());
+ } catch (MalformedURLException e) {
+ request.setUri(next);
+ }
+
+ GitHubResponse response;
+ try {
+ response = client.get(request);
+ } catch (IOException e) {
+ throw new NoSuchPageException(e);
+ }
+ Collection resources = null;
+ Object body = response.getBody();
+ if (body != null)
+ if (body instanceof Collection)
+ resources = (Collection) body;
+ else if (body instanceof IResourceProvider)
+ resources = ((IResourceProvider) body).getResources();
+ else
+ resources = (Collection) Collections.singletonList(body);
+ if (resources == null)
+ resources = Collections.emptyList();
+ nextPage++;
+ next = response.getNext();
+ nextPage = parsePageNumber(next);
+ last = response.getLast();
+ lastPage = parsePageNumber(last);
+ return resources;
+ }
+
+ /**
+ * Get request being executed
+ *
+ * @return request
+ */
+ public PagedRequest getRequest() {
+ return request;
+ }
+
+ /**
+ * @return this page iterator
+ */
+ public Iterator> iterator() {
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/PageLinks.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/PageLinks.java
new file mode 100644
index 000000000..0fa3dff13
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/PageLinks.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core.client;
+
+import static org.eclipse.egit.github.core.client.IGitHubConstants.HEADER_LAST;
+import static org.eclipse.egit.github.core.client.IGitHubConstants.HEADER_LINK;
+import static org.eclipse.egit.github.core.client.IGitHubConstants.HEADER_NEXT;
+import static org.eclipse.egit.github.core.client.IGitHubConstants.META_FIRST;
+import static org.eclipse.egit.github.core.client.IGitHubConstants.META_LAST;
+import static org.eclipse.egit.github.core.client.IGitHubConstants.META_NEXT;
+import static org.eclipse.egit.github.core.client.IGitHubConstants.META_PREV;
+import static org.eclipse.egit.github.core.client.IGitHubConstants.META_REL;
+
+/**
+ * Page link class to be used to determine the links to other pages of request
+ * responses encoded in the current response. These will be present if the
+ * result set size exceeds the per page limit.
+ */
+public class PageLinks {
+
+ private static final String DELIM_LINKS = ","; //$NON-NLS-1$
+
+ private static final String DELIM_LINK_PARAM = ";"; //$NON-NLS-1$
+
+ private String first;
+ private String last;
+ private String next;
+ private String prev;
+
+ /**
+ * Parse links from executed method
+ *
+ * @param response
+ */
+ public PageLinks(GitHubResponse response) {
+ String linkHeader = response.getHeader(HEADER_LINK);
+ if (linkHeader != null) {
+ String[] links = linkHeader.split(DELIM_LINKS);
+ for (String link : links) {
+ String[] segments = link.split(DELIM_LINK_PARAM);
+ if (segments.length < 2)
+ continue;
+
+ String linkPart = segments[0].trim();
+ if (!linkPart.startsWith("<") || !linkPart.endsWith(">")) //$NON-NLS-1$ //$NON-NLS-2$
+ continue;
+ linkPart = linkPart.substring(1, linkPart.length() - 1);
+
+ for (int i = 1; i < segments.length; i++) {
+ String[] rel = segments[i].trim().split("="); //$NON-NLS-1$
+ if (rel.length < 2 || !META_REL.equals(rel[0]))
+ continue;
+
+ String relValue = rel[1];
+ if (relValue.startsWith("\"") && relValue.endsWith("\"")) //$NON-NLS-1$ //$NON-NLS-2$
+ relValue = relValue.substring(1, relValue.length() - 1);
+
+ if (META_FIRST.equals(relValue))
+ first = linkPart;
+ else if (META_LAST.equals(relValue))
+ last = linkPart;
+ else if (META_NEXT.equals(relValue))
+ next = linkPart;
+ else if (META_PREV.equals(relValue))
+ prev = linkPart;
+ }
+ }
+ } else {
+ next = response.getHeader(HEADER_NEXT);
+ last = response.getHeader(HEADER_LAST);
+ }
+ }
+
+ /**
+ * @return first
+ */
+ public String getFirst() {
+ return first;
+ }
+
+ /**
+ * @return last
+ */
+ public String getLast() {
+ return last;
+ }
+
+ /**
+ * @return next
+ */
+ public String getNext() {
+ return next;
+ }
+
+ /**
+ * @return prev
+ */
+ public String getPrev() {
+ return prev;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/PagedRequest.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/PagedRequest.java
new file mode 100644
index 000000000..4ebe20226
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/PagedRequest.java
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core.client;
+
+import static org.eclipse.egit.github.core.client.IGitHubConstants.PARAM_PAGE;
+import static org.eclipse.egit.github.core.client.IGitHubConstants.PARAM_PER_PAGE;
+
+import org.eclipse.egit.github.core.util.UrlUtils;
+
+/**
+ * Paged request class that contains the initial page size and page number of
+ * the request.
+ *
+ * @param
+ */
+public class PagedRequest extends GitHubRequest {
+
+ /**
+ * First page
+ */
+ public static final int PAGE_FIRST = 1;
+
+ /**
+ * Default page size
+ */
+ public static final int PAGE_SIZE = 100;
+
+ private final int pageSize;
+
+ private final int page;
+
+ /**
+ * Create paged request with default size
+ */
+ public PagedRequest() {
+ this(PAGE_FIRST, PAGE_SIZE);
+ }
+
+ /**
+ * Create paged request with given starting page and page size.
+ *
+ * @param start
+ * @param size
+ */
+ public PagedRequest(int start, int size) {
+ page = start;
+ pageSize = size;
+ }
+
+ /**
+ * Get initial page size
+ *
+ * @return pageSize
+ */
+ public int getPageSize() {
+ return pageSize;
+ }
+
+ @Override
+ protected void addParams(final StringBuilder uri) {
+ super.addParams(uri);
+ final int size = getPageSize();
+ if (size > 0)
+ UrlUtils.addParam(PARAM_PER_PAGE, Integer.toString(size), uri);
+ final int number = getPage();
+ if (number > 0)
+ UrlUtils.addParam(PARAM_PAGE, Integer.toString(number), uri);
+ }
+
+ /**
+ * Get initial page number
+ *
+ * @return page
+ */
+ public int getPage() {
+ return page;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/RequestException.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/RequestException.java
new file mode 100644
index 000000000..6ea08a263
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/client/RequestException.java
@@ -0,0 +1,139 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core.client;
+
+import static org.eclipse.egit.github.core.FieldError.CODE_ALREADY_EXISTS;
+import static org.eclipse.egit.github.core.FieldError.CODE_INVALID;
+import static org.eclipse.egit.github.core.FieldError.CODE_MISSING_FIELD;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.List;
+
+import org.eclipse.egit.github.core.FieldError;
+import org.eclipse.egit.github.core.RequestError;
+
+/**
+ * Request exception class that wraps a {@link RequestError} object.
+ */
+public class RequestException extends IOException {
+
+ private static final String FIELD_INVALID_WITH_VALUE = "Invalid value of ''{0}'' for ''{1}'' field"; //$NON-NLS-1$
+
+ private static final String FIELD_INVALID = "Invalid value for ''{0}'' field"; //$NON-NLS-1$
+
+ private static final String FIELD_MISSING = "Missing required ''{0}'' field"; //$NON-NLS-1$
+
+ private static final String FIELD_ERROR = "Error with ''{0}'' field in {1} resource"; //$NON-NLS-1$
+
+ private static final String FIELD_EXISTS = "{0} resource with ''{1}'' field already exists"; //$NON-NLS-1$
+
+ /**
+ * serialVersionUID
+ */
+ private static final long serialVersionUID = 1197051396535284852L;
+
+ private final RequestError error;
+ private final int status;
+
+ /**
+ * Create request exception
+ *
+ * @param error
+ * @param status
+ */
+ public RequestException(RequestError error, int status) {
+ super();
+ this.error = error;
+ this.status = status;
+ }
+
+ public String getMessage() {
+ return error != null ? formatErrors() : super.getMessage();
+ }
+
+ /**
+ * Get error
+ *
+ * @return error
+ */
+ public RequestError getError() {
+ return error;
+ }
+
+ /**
+ * Get status
+ *
+ * @return status
+ */
+ public int getStatus() {
+ return status;
+ }
+
+ /**
+ * Format field error into human-readable message
+ *
+ * @param error
+ * @return formatted field error
+ */
+ protected String format(FieldError error) {
+ String code = error.getCode();
+ String value = error.getValue();
+ String field = error.getField();
+
+ if (CODE_INVALID.equals(code))
+ if (value != null)
+ return MessageFormat.format(FIELD_INVALID_WITH_VALUE, value,
+ field);
+ else
+ return MessageFormat.format(FIELD_INVALID, field);
+
+ if (CODE_MISSING_FIELD.equals(code))
+ return MessageFormat.format(FIELD_MISSING, field);
+
+ if (CODE_ALREADY_EXISTS.equals(code))
+ return MessageFormat.format(FIELD_EXISTS, error.getResource(),
+ field);
+
+ // Use field error message as is if custom code
+ if (FieldError.CODE_CUSTOM.equals(code)) {
+ String message = error.getMessage();
+ if (message != null && message.length() > 0)
+ return message;
+ }
+
+ return MessageFormat.format(FIELD_ERROR, field, error.getResource());
+ }
+
+ /**
+ * Format all field errors into single human-readable message.
+ *
+ * @return formatted message
+ */
+ public String formatErrors() {
+ String errorMessage = error.getMessage();
+ if (errorMessage == null)
+ errorMessage = ""; //$NON-NLS-1$
+ StringBuilder message = new StringBuilder(errorMessage);
+ if (message.length() > 0)
+ message.append(' ').append('(').append(status).append(')');
+ else
+ message.append(status);
+ List errors = error.getErrors();
+ if (errors != null && errors.size() > 0) {
+ message.append(':');
+ for (FieldError fieldError : errors)
+ message.append(' ').append(format(fieldError)).append(',');
+ message.deleteCharAt(message.length() - 1);
+ }
+ return message.toString();
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/CommitCommentPayload.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/CommitCommentPayload.java
new file mode 100644
index 000000000..052c2f538
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/CommitCommentPayload.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Jason Tsay (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core.event;
+
+import java.io.Serializable;
+
+import org.eclipse.egit.github.core.CommitComment;
+
+/**
+ * CommitCommentEvent payload model class.
+ */
+public class CommitCommentPayload extends EventPayload implements
+ Serializable {
+
+ private static final long serialVersionUID = -2606554911096551099L;
+
+ private CommitComment comment;
+
+ /**
+ * @return comment
+ */
+ public CommitComment getComment() {
+ return comment;
+ }
+
+ /**
+ * @param comment
+ * @return this CommitCommentPayload
+ */
+ public CommitCommentPayload setComment(CommitComment comment) {
+ this.comment = comment;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/CreatePayload.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/CreatePayload.java
new file mode 100644
index 000000000..8ee565fb3
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/CreatePayload.java
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Jason Tsay (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core.event;
+
+import java.io.Serializable;
+
+/**
+ * CreateEvent payload model class.
+ */
+public class CreatePayload extends EventPayload implements Serializable {
+
+ private static final long serialVersionUID = -7033027645721954674L;
+
+ private String refType;
+
+ private String ref;
+
+ private String masterBranch;
+
+ private String description;
+
+ /**
+ * @return refType
+ */
+ public String getRefType() {
+ return refType;
+ }
+
+ /**
+ * @param refType
+ * @return this CreatePayload
+ */
+ public CreatePayload setRefType(String refType) {
+ this.refType = refType;
+ return this;
+ }
+
+ /**
+ * @return ref
+ */
+ public String getRef() {
+ return ref;
+ }
+
+ /**
+ * @param ref
+ * @return this CreatePayload
+ */
+ public CreatePayload setRef(String ref) {
+ this.ref = ref;
+ return this;
+ }
+
+ /**
+ * @return masterBranch
+ */
+ public String getMasterBranch() {
+ return masterBranch;
+ }
+
+ /**
+ * @param masterBranch
+ * @return this CreatePayload
+ */
+ public CreatePayload setMasterBranch(String masterBranch) {
+ this.masterBranch = masterBranch;
+ return this;
+ }
+
+ /**
+ * @return description
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * @param description
+ * @return this CreatePayload
+ */
+ public CreatePayload setDescription(String description) {
+ this.description = description;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/DeletePayload.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/DeletePayload.java
new file mode 100644
index 000000000..2015364e7
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/DeletePayload.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Jason Tsay (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core.event;
+
+import java.io.Serializable;
+
+/**
+ * DeleteEvent payload model class.
+ */
+public class DeletePayload extends EventPayload implements Serializable {
+
+ private static final long serialVersionUID = -7571623946339106873L;
+
+ private String refType;
+
+ private String ref;
+
+ /**
+ * @return refType
+ */
+ public String getRefType() {
+ return refType;
+ }
+
+ /**
+ * @param refType
+ * @return this DeletePayload
+ */
+ public DeletePayload setRefType(String refType) {
+ this.refType = refType;
+ return this;
+ }
+
+ /**
+ * @return ref
+ */
+ public String getRef() {
+ return ref;
+ }
+
+ /**
+ * @param ref
+ * @return this DeletePayload
+ */
+ public DeletePayload setRef(String ref) {
+ this.ref = ref;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/DownloadPayload.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/DownloadPayload.java
new file mode 100644
index 000000000..dff4337c5
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/DownloadPayload.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Jason Tsay (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core.event;
+
+import java.io.Serializable;
+
+import org.eclipse.egit.github.core.Download;
+
+/**
+ * DownloadEvent payload model class.
+ */
+public class DownloadPayload extends EventPayload implements Serializable {
+
+ private static final long serialVersionUID = 4246935370658381214L;
+
+ private Download download;
+
+ /**
+ * @return download
+ */
+ public Download getDownload() {
+ return download;
+ }
+
+ /**
+ * @param download
+ * @return this DownloadPayload
+ */
+ public DownloadPayload setDownload(Download download) {
+ this.download = download;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/Event.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/Event.java
new file mode 100644
index 000000000..4883e909c
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/Event.java
@@ -0,0 +1,266 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Jason Tsay (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core.event;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import org.eclipse.egit.github.core.User;
+import org.eclipse.egit.github.core.util.DateUtils;
+
+/**
+ * Event model class.
+ */
+public class Event implements Serializable {
+
+ /**
+ * Event type denoting a {@link CommitCommentPayload}
+ */
+ public static final String TYPE_COMMIT_COMMENT = "CommitCommentEvent";
+
+ /**
+ * Event type denoting a {@link CreatePayload}
+ */
+ public static final String TYPE_CREATE = "CreateEvent";
+
+ /**
+ * Event type denoting a {@link DeletePayload}
+ */
+ public static final String TYPE_DELETE = "DeleteEvent";
+
+ /**
+ * Event type denoting a {@link DownloadPayload}
+ */
+ public static final String TYPE_DOWNLOAD = "DownloadEvent";
+
+ /**
+ * Event type dneoting a {@link FollowPayload}
+ */
+ public static final String TYPE_FOLLOW = "FollowEvent";
+
+ /**
+ * Event type denoting a {@link ForkPayload}
+ */
+ public static final String TYPE_FORK = "ForkEvent";
+
+ /**
+ * Event type denoting a {@link ForkApplyPayload}
+ */
+ public static final String TYPE_FORK_APPLY = "ForkApplyEvent";
+
+ /**
+ * Event type denoting a {@link GistPayload}
+ */
+ public static final String TYPE_GIST = "GistEvent";
+
+ /**
+ * Event type denoting a {@link GollumPayload}
+ */
+ public static final String TYPE_GOLLUM = "GollumEvent";
+
+ /**
+ * Event type denoting a {@link IssueCommentPayload}
+ */
+ public static final String TYPE_ISSUE_COMMENT = "IssueCommentEvent";
+
+ /**
+ * Event type denoting a {@link IssuesPayload}
+ */
+ public static final String TYPE_ISSUES = "IssuesEvent";
+
+ /**
+ * Event type denoting a {@link MemberPayload}
+ */
+ public static final String TYPE_MEMBER = "MemberEvent";
+
+ /**
+ * Event type denoting a {@link PublicPayload}
+ */
+ public static final String TYPE_PUBLIC = "PublicEvent";
+
+ /**
+ * Event type denoting a {@link PullRequestPayload}
+ */
+ public static final String TYPE_PULL_REQUEST = "PullRequestEvent";
+
+ /**
+ * Event type denoting a {@link PullRequestReviewCommentPayload}
+ */
+ public static final String TYPE_PULL_REQUEST_REVIEW_COMMENT = "PullRequestReviewCommentEvent";
+
+ /**
+ * Event type denoting a {@link PushPayload}
+ */
+ public static final String TYPE_PUSH = "PushEvent";
+
+ /**
+ * Event type denoting a {@link TeamAddPayload}
+ */
+ public static final String TYPE_TEAM_ADD = "TeamAddEvent";
+
+ /**
+ * Event type denoting a {@link WatchPayload}
+ */
+ public static final String TYPE_WATCH = "WatchEvent";
+
+ private static final long serialVersionUID = 3633702964380402233L;
+
+ /**
+ * Make sure this is above payload. Payload deserialization depends on being
+ * able to read the type first.
+ */
+ private String type;
+
+ @SerializedName("public")
+ private boolean isPublic;
+
+ private EventPayload payload;
+
+ private EventRepository repo;
+
+ private String id;
+
+ private User actor;
+
+ private User org;
+
+ private Date createdAt;
+
+ /**
+ * @return type
+ */
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * @param type
+ * @return this Event
+ */
+ public Event setType(String type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * @return isPublic
+ */
+ public boolean isPublic() {
+ return isPublic;
+ }
+
+ /**
+ * @param isPublic
+ * @return this Event
+ */
+ public Event setPublic(boolean isPublic) {
+ this.isPublic = isPublic;
+ return this;
+ }
+
+ /**
+ * @return the repo
+ */
+ public EventRepository getRepo() {
+ return repo;
+ }
+
+ /**
+ * @param repo
+ * @return this Event
+ */
+ public Event setRepo(EventRepository repo) {
+ this.repo = repo;
+ return this;
+ }
+
+ /**
+ * @return the actor
+ */
+ public User getActor() {
+ return actor;
+ }
+
+ /**
+ * @param actor
+ * @return this Event
+ */
+ public Event setActor(User actor) {
+ this.actor = actor;
+ return this;
+ }
+
+ /**
+ * @return the org
+ */
+ public User getOrg() {
+ return org;
+ }
+
+ /**
+ * @param org
+ * @return this Event
+ */
+ public Event setOrg(User org) {
+ this.org = org;
+ return this;
+ }
+
+ /**
+ * @return the createdAt
+ */
+ public Date getCreatedAt() {
+ return DateUtils.clone(createdAt);
+ }
+
+ /**
+ * @param createdAt
+ * @return this Event
+ */
+ public Event setCreatedAt(Date createdAt) {
+ this.createdAt = DateUtils.clone(createdAt);
+ return this;
+ }
+
+ /**
+ * @return payload
+ */
+ public EventPayload getPayload() {
+ return payload;
+ }
+
+ /**
+ * @param payload
+ * @return this event
+ */
+ public Event setPayload(EventPayload payload) {
+ this.payload = payload;
+ return this;
+ }
+
+ /**
+ * @return id
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * @param id
+ * @return this event
+ */
+ public Event setId(String id) {
+ this.id = id;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/EventPayload.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/EventPayload.java
new file mode 100644
index 000000000..86ef29721
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/EventPayload.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Jason Tsay (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core.event;
+
+import java.io.Serializable;
+
+/**
+ * Parent class for event payloads
+ *
+ * @see GitHub Event types
+ * API documentation
+ */
+public class EventPayload implements Serializable {
+
+ private static final long serialVersionUID = 1022083387039340606L;
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/EventRepository.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/EventRepository.java
new file mode 100644
index 000000000..107858eff
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/EventRepository.java
@@ -0,0 +1,76 @@
+/******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.github.core.event;
+
+import java.io.Serializable;
+
+/**
+ * Model class for repository information contained in an {@link Event}
+ */
+public class EventRepository implements Serializable {
+
+ /** serialVersionUID */
+ private static final long serialVersionUID = -8910798454171899699L;
+
+ private long id;
+
+ private String name;
+
+ private String url;
+
+ /**
+ * @return id
+ */
+ public long getId() {
+ return id;
+ }
+
+ /**
+ * @param id
+ * @return this event repository
+ */
+ public EventRepository setId(long id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * @return name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param name
+ * @return this event repository
+ */
+ public EventRepository setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * @return url
+ */
+ public String getUrl() {
+ return url;
+ }
+
+ /**
+ * @param url
+ * @return this event repository
+ */
+ public EventRepository setUrl(String url) {
+ this.url = url;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/FollowPayload.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/FollowPayload.java
new file mode 100644
index 000000000..9b88b496e
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/FollowPayload.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Jason Tsay (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core.event;
+
+import java.io.Serializable;
+
+import org.eclipse.egit.github.core.User;
+
+/**
+ * FollowEvent payload model class.
+ */
+public class FollowPayload extends EventPayload implements Serializable {
+
+ private static final long serialVersionUID = -4345668254608800406L;
+
+ private User target;
+
+ /**
+ * @return target
+ */
+ public User getTarget() {
+ return target;
+ }
+
+ /**
+ * @param target
+ * @return this FollowPayload
+ */
+ public FollowPayload setTarget(User target) {
+ this.target = target;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/ForkApplyPayload.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/ForkApplyPayload.java
new file mode 100644
index 000000000..9a863e3df
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/ForkApplyPayload.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Jason Tsay (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core.event;
+
+import java.io.Serializable;
+
+/**
+ * ForkApplyEvent payload model class.
+ */
+public class ForkApplyPayload extends EventPayload implements Serializable {
+
+ private static final long serialVersionUID = -7527740351672699770L;
+
+ private String head;
+
+ private String before;
+
+ private String after;
+
+ /**
+ * @return head
+ */
+ public String getHead() {
+ return head;
+ }
+
+ /**
+ * @param head
+ * @return this ForkApplyPayload
+ */
+ public ForkApplyPayload setHead(String head) {
+ this.head = head;
+ return this;
+ }
+
+ /**
+ * @return before
+ */
+ public String getBefore() {
+ return before;
+ }
+
+ /**
+ * @param before
+ * @return this ForkApplyPayload
+ */
+ public ForkApplyPayload setBefore(String before) {
+ this.before = before;
+ return this;
+ }
+
+ /**
+ * @return after
+ */
+ public String getAfter() {
+ return after;
+ }
+
+ /**
+ * @param after
+ * @return this ForkApplyPayload
+ */
+ public ForkApplyPayload setAfter(String after) {
+ this.after = after;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/ForkPayload.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/ForkPayload.java
new file mode 100644
index 000000000..7b5cbd367
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/ForkPayload.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Jason Tsay (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core.event;
+
+import java.io.Serializable;
+
+import org.eclipse.egit.github.core.Repository;
+
+/**
+ * ForkEvent payload model class.
+ */
+public class ForkPayload extends EventPayload implements Serializable {
+
+ private static final long serialVersionUID = 2110456722558520113L;
+
+ private Repository forkee;
+
+ /**
+ * @return forkee
+ */
+ public Repository getForkee() {
+ return forkee;
+ }
+
+ /**
+ * @param forkee
+ * @return this ForkPayload
+ */
+ public ForkPayload setForkee(Repository forkee) {
+ this.forkee = forkee;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/GistPayload.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/GistPayload.java
new file mode 100644
index 000000000..b2fc69f79
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/GistPayload.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Jason Tsay (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core.event;
+
+import java.io.Serializable;
+
+import org.eclipse.egit.github.core.Gist;
+
+/**
+ * GistEvent payload model class.
+ */
+public class GistPayload extends EventPayload implements Serializable {
+
+ private static final long serialVersionUID = 8916400800708594462L;
+
+ private String action;
+
+ private Gist gist;
+
+ /**
+ * @return action
+ */
+ public String getAction() {
+ return action;
+ }
+
+ /**
+ * @param action
+ * @return this GistPayload
+ */
+ public GistPayload setAction(String action) {
+ this.action = action;
+ return this;
+ }
+
+ /**
+ * @return gist
+ */
+ public Gist getGist() {
+ return gist;
+ }
+
+ /**
+ * @param gist
+ * @return this GistPayload
+ */
+ public GistPayload setGist(Gist gist) {
+ this.gist = gist;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/GollumPayload.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/GollumPayload.java
new file mode 100644
index 000000000..c8b8ccff9
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/GollumPayload.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Jason Tsay (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core.event;
+
+import java.io.Serializable;
+import java.util.List;
+
+import org.eclipse.egit.github.core.GollumPage;
+
+/**
+ * GollumEvent payload model class.
+ */
+public class GollumPayload extends EventPayload implements Serializable {
+
+ private static final long serialVersionUID = 7111499446827257290L;
+
+ private List pages;
+
+ /**
+ * @return pages
+ */
+ public List getPages() {
+ return pages;
+ }
+
+ /**
+ * @param pages
+ * @return this GollumPayload
+ */
+ public GollumPayload setPages(List pages) {
+ this.pages = pages;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/IssueCommentPayload.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/IssueCommentPayload.java
new file mode 100644
index 000000000..b272d76d9
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/IssueCommentPayload.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Jason Tsay (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core.event;
+
+import java.io.Serializable;
+
+import org.eclipse.egit.github.core.Comment;
+import org.eclipse.egit.github.core.Issue;
+
+/**
+ * IssueCommentEvent payload model class.
+ */
+public class IssueCommentPayload extends EventPayload implements Serializable {
+
+ private static final long serialVersionUID = 2661548417314120170L;
+
+ private String action;
+
+ private Issue issue;
+
+ private Comment comment;
+
+ /**
+ * @return action
+ */
+ public String getAction() {
+ return action;
+ }
+
+ /**
+ * @param action
+ * @return this IssueCommentPayload
+ */
+ public IssueCommentPayload setAction(String action) {
+ this.action = action;
+ return this;
+ }
+
+ /**
+ * @return issue
+ */
+ public Issue getIssue() {
+ return issue;
+ }
+
+ /**
+ * @param issue
+ * @return this IssueCommentPayload
+ */
+ public IssueCommentPayload setIssue(Issue issue) {
+ this.issue = issue;
+ return this;
+ }
+
+ /**
+ * @return comment
+ */
+ public Comment getComment() {
+ return comment;
+ }
+
+ /**
+ * @param comment
+ * @return this IssueCommentPayload
+ */
+ public IssueCommentPayload setComment(Comment comment) {
+ this.comment = comment;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/IssuesPayload.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/IssuesPayload.java
new file mode 100644
index 000000000..eec591b02
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/IssuesPayload.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Jason Tsay (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core.event;
+
+import java.io.Serializable;
+
+import org.eclipse.egit.github.core.Issue;
+
+/**
+ * IssuesEvent payload model class.
+ */
+public class IssuesPayload extends EventPayload implements Serializable {
+
+ private static final long serialVersionUID = 3210795492806809443L;
+
+ private String action;
+
+ private Issue issue;
+
+ /**
+ * @return action
+ */
+ public String getAction() {
+ return action;
+ }
+
+ /**
+ * @param action
+ * @return this IssuesPayload
+ */
+ public IssuesPayload setAction(String action) {
+ this.action = action;
+ return this;
+ }
+
+ /**
+ * @return issue
+ */
+ public Issue getIssue() {
+ return issue;
+ }
+
+ /**
+ * @param issue
+ * @return this IssuesPayload
+ */
+ public IssuesPayload setIssue(Issue issue) {
+ this.issue = issue;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/MemberPayload.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/MemberPayload.java
new file mode 100644
index 000000000..64399a592
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/MemberPayload.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Jason Tsay (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core.event;
+
+import java.io.Serializable;
+
+import org.eclipse.egit.github.core.User;
+
+/**
+ * MemberEvent payload model class.
+ */
+public class MemberPayload extends EventPayload implements Serializable {
+
+ private static final long serialVersionUID = -4261757812093447848L;
+
+ private User member;
+
+ private String action;
+
+ /**
+ * @return member
+ */
+ public User getMember() {
+ return member;
+ }
+
+ /**
+ * @param member
+ * @return this MemberPayload
+ */
+ public MemberPayload setMember(User member) {
+ this.member = member;
+ return this;
+ }
+
+ /**
+ * @return action
+ */
+ public String getAction() {
+ return action;
+ }
+
+ /**
+ * @param action
+ * @return this MemberPayload
+ */
+ public MemberPayload setAction(String action) {
+ this.action = action;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/PublicPayload.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/PublicPayload.java
new file mode 100644
index 000000000..9758eca0b
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/PublicPayload.java
@@ -0,0 +1,22 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Jason Tsay (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core.event;
+
+import java.io.Serializable;
+
+/**
+ * PublicEvent payload model class.
+ */
+public class PublicPayload extends EventPayload implements Serializable {
+
+ private static final long serialVersionUID = 5262549236565872052L;
+
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/PullRequestPayload.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/PullRequestPayload.java
new file mode 100644
index 000000000..19a90241c
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/PullRequestPayload.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Jason Tsay (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.egit.github.core.event;
+
+import java.io.Serializable;
+
+import org.eclipse.egit.github.core.PullRequest;
+
+/**
+ * PullRequestEvent payload model class.
+ */
+public class PullRequestPayload extends EventPayload implements Serializable {
+
+ private static final long serialVersionUID = -8234504270587265625L;
+
+ private String action;
+
+ private int number;
+
+ private PullRequest pullRequest;
+
+ /**
+ * @return action
+ */
+ public String getAction() {
+ return action;
+ }
+
+ /**
+ * @param action
+ * @return this PullRequestPayload
+ */
+ public PullRequestPayload setAction(String action) {
+ this.action = action;
+ return this;
+ }
+
+ /**
+ * @return number
+ */
+ public int getNumber() {
+ return number;
+ }
+
+ /**
+ * @param number
+ * @return this PullRequestPayload
+ */
+ public PullRequestPayload setNumber(int number) {
+ this.number = number;
+ return this;
+ }
+
+ /**
+ * @return pullRequest
+ */
+ public PullRequest getPullRequest() {
+ return pullRequest;
+ }
+
+ /**
+ * @param pullRequest
+ * @return this PullRequestPayload
+ */
+ public PullRequestPayload setPullRequest(PullRequest pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/PullRequestReviewCommentPayload.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/PullRequestReviewCommentPayload.java
new file mode 100644
index 000000000..04f6d8a07
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/PullRequestReviewCommentPayload.java
@@ -0,0 +1,42 @@
+/******************************************************************************
+ * Copyright (c) 2012 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.github.core.event;
+
+import java.io.Serializable;
+
+import org.eclipse.egit.github.core.CommitComment;
+
+/**
+ * Payload for an event with type {@link Event#TYPE_PULL_REQUEST_REVIEW_COMMENT}
+ */
+public class PullRequestReviewCommentPayload extends EventPayload implements
+ Serializable {
+
+ private static final long serialVersionUID = -2403658752886394741L;
+
+ private CommitComment comment;
+
+ /**
+ * @return comment
+ */
+ public CommitComment getComment() {
+ return comment;
+ }
+
+ /**
+ * @param comment
+ * @return this payload
+ */
+ public PullRequestReviewCommentPayload setComment(CommitComment comment) {
+ this.comment = comment;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/PushPayload.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/PushPayload.java
new file mode 100644
index 000000000..e1315c7bc
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/PushPayload.java
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Jason Tsay (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core.event;
+
+import java.io.Serializable;
+import java.util.List;
+
+import org.eclipse.egit.github.core.Commit;
+
+/**
+ * PushEvent payload model class.
+ */
+public class PushPayload extends EventPayload implements Serializable {
+
+ private static final long serialVersionUID = -1542484898531583478L;
+
+ private String before;
+
+ private String head;
+
+ private String ref;
+
+ private int size;
+
+ private List commits;
+
+ /**
+ * @return before
+ */
+ public String getBefore() {
+ return before;
+ }
+
+ /**
+ * @param before
+ * @return this payload
+ */
+ public PushPayload setBefore(String before) {
+ this.before = before;
+ return this;
+ }
+
+ /**
+ * @return head
+ */
+ public String getHead() {
+ return head;
+ }
+
+ /**
+ * @param head
+ * @return this PushEvent
+ */
+ public PushPayload setHead(String head) {
+ this.head = head;
+ return this;
+ }
+
+ /**
+ * @return ref
+ */
+ public String getRef() {
+ return ref;
+ }
+
+ /**
+ * @param ref
+ * @return this PushEvent
+ */
+ public PushPayload setRef(String ref) {
+ this.ref = ref;
+ return this;
+ }
+
+ /**
+ * @return size
+ */
+ public int getSize() {
+ return size;
+ }
+
+ /**
+ * @param size
+ * @return this PushEvent
+ */
+ public PushPayload setSize(int size) {
+ this.size = size;
+ return this;
+ }
+
+ /**
+ * @return commits
+ */
+ public List getCommits() {
+ return commits;
+ }
+
+ /**
+ * @param commits
+ * @return this PushEvent
+ */
+ public PushPayload setCommits(List commits) {
+ this.commits = commits;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/TeamAddPayload.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/TeamAddPayload.java
new file mode 100644
index 000000000..5cb024743
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/TeamAddPayload.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Jason Tsay (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core.event;
+
+import java.io.Serializable;
+
+import org.eclipse.egit.github.core.Repository;
+import org.eclipse.egit.github.core.Team;
+import org.eclipse.egit.github.core.User;
+
+/**
+ * TeamAddEvent payload model class
+ */
+public class TeamAddPayload extends EventPayload implements Serializable {
+
+ private static final long serialVersionUID = 7660176723347977144L;
+
+ private Team team;
+
+ private User user;
+
+ private Repository repo;
+
+ /**
+ * @return team
+ */
+ public Team getTeam() {
+ return team;
+ }
+
+ /**
+ * @param team
+ * @return this TeamAddPayload
+ */
+ public TeamAddPayload setTeam(Team team) {
+ this.team = team;
+ return this;
+ }
+
+ /**
+ * @return user
+ */
+ public User getUser() {
+ return user;
+ }
+
+ /**
+ * @param user
+ * @return this TeamAddPayload
+ */
+ public TeamAddPayload setUser(User user) {
+ this.user = user;
+ return this;
+ }
+
+ /**
+ * @return repo
+ */
+ public Repository getRepo() {
+ return repo;
+ }
+
+ /**
+ * @param repo
+ * @return this TeamAddPayload
+ */
+ public TeamAddPayload setRepo(Repository repo) {
+ this.repo = repo;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/WatchPayload.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/WatchPayload.java
new file mode 100644
index 000000000..0dd4f061e
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/event/WatchPayload.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Jason Tsay (GitHub Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.github.core.event;
+
+import java.io.Serializable;
+
+/**
+ * WatchEvent payload model class.
+ */
+public class WatchPayload extends EventPayload implements Serializable {
+
+ private static final long serialVersionUID = -1600566006173513492L;
+
+ private String action;
+
+ /**
+ * @return action
+ */
+ public String getAction() {
+ return action;
+ }
+
+ /**
+ * @param action
+ * @return this WatchPayload
+ */
+ public WatchPayload setAction(String action) {
+ this.action = action;
+ return this;
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/service/CollaboratorService.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/service/CollaboratorService.java
new file mode 100644
index 000000000..1e3f1288a
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/service/CollaboratorService.java
@@ -0,0 +1,128 @@
+/******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.github.core.service;
+
+import static org.eclipse.egit.github.core.client.IGitHubConstants.SEGMENT_COLLABORATORS;
+import static org.eclipse.egit.github.core.client.IGitHubConstants.SEGMENT_REPOS;
+
+import com.google.gson.reflect.TypeToken;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.eclipse.egit.github.core.IRepositoryIdProvider;
+import org.eclipse.egit.github.core.User;
+import org.eclipse.egit.github.core.client.GitHubClient;
+import org.eclipse.egit.github.core.client.PagedRequest;
+
+/**
+ * Service for interacting with the collaborators on a GitHub repository
+ *
+ * @see GitHub
+ * collaborator API documentation
+ */
+public class CollaboratorService extends GitHubService {
+
+ /**
+ * Create collaborator service
+ */
+ public CollaboratorService() {
+ super();
+ }
+
+ /**
+ * Create collaborator service
+ *
+ * @param client
+ */
+ public CollaboratorService(GitHubClient client) {
+ super(client);
+ }
+
+ /**
+ * Get collaborators for given repository
+ *
+ * @param repository
+ * @return non-null list of collaborators
+ * @throws IOException
+ */
+ public List getCollaborators(IRepositoryIdProvider repository)
+ throws IOException {
+ String id = getId(repository);
+ StringBuilder uri = new StringBuilder(SEGMENT_REPOS);
+ uri.append('/').append(id);
+ uri.append(SEGMENT_COLLABORATORS);
+ PagedRequest request = createPagedRequest();
+ request.setUri(uri);
+ request.setType(new TypeToken>() {
+ }.getType());
+ return getAll(request);
+ }
+
+ /**
+ * Create URI for updating collaborators
+ *
+ * @param repository
+ * @param user
+ * @return URI
+ */
+ protected String createUpdateUri(IRepositoryIdProvider repository,
+ String user) {
+ String id = getId(repository);
+ if (user == null)
+ throw new IllegalArgumentException("User cannot be null"); //$NON-NLS-1$
+ if (user.length() == 0)
+ throw new IllegalArgumentException("User cannot be empty"); //$NON-NLS-1$
+
+ StringBuilder uri = new StringBuilder(SEGMENT_REPOS);
+ uri.append('/').append(id);
+ uri.append(SEGMENT_COLLABORATORS);
+ uri.append('/').append(user);
+ return uri.toString();
+ }
+
+ /**
+ * Is given user a collaborator on the given repository?
+ *
+ * @param repository
+ * @param user
+ * @return true if collaborator, false otherwise
+ * @throws IOException
+ */
+ public boolean isCollaborator(IRepositoryIdProvider repository, String user)
+ throws IOException {
+ return check(createUpdateUri(repository, user));
+ }
+
+ /**
+ * Add given user as a collaborator on the given repository
+ *
+ * @param repository
+ * @param user
+ * @throws IOException
+ */
+ public void addCollaborator(IRepositoryIdProvider repository, String user)
+ throws IOException {
+ client.put(createUpdateUri(repository, user));
+ }
+
+ /**
+ * Remove given user as a collaborator on the given repository
+ *
+ * @param repository
+ * @param user
+ * @throws IOException
+ */
+ public void removeCollaborator(IRepositoryIdProvider repository, String user)
+ throws IOException {
+ client.delete(createUpdateUri(repository, user));
+ }
+}
diff --git a/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/service/CommitService.java b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/service/CommitService.java
new file mode 100644
index 000000000..75fb5ec89
--- /dev/null
+++ b/org.eclipse.egit.github.core/src/org/eclipse/egit/github/core/service/CommitService.java
@@ -0,0 +1,487 @@
+/******************************************************************************
+ * Copyright (c) 2011 GitHub Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.github.core.service;
+
+import static org.eclipse.egit.github.core.client.IGitHubConstants.SEGMENT_COMMENTS;
+import static org.eclipse.egit.github.core.client.IGitHubConstants.SEGMENT_COMMITS;
+import static org.eclipse.egit.github.core.client.IGitHubConstants.SEGMENT_COMPARE;
+import static org.eclipse.egit.github.core.client.IGitHubConstants.SEGMENT_REPOS;
+import static org.eclipse.egit.github.core.client.IGitHubConstants.SEGMENT_STATUSES;
+import static org.eclipse.egit.github.core.client.PagedRequest.PAGE_FIRST;
+import static org.eclipse.egit.github.core.client.PagedRequest.PAGE_SIZE;
+
+import com.google.gson.reflect.TypeToken;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.egit.github.core.CommitComment;
+import org.eclipse.egit.github.core.CommitStatus;
+import org.eclipse.egit.github.core.IRepositoryIdProvider;
+import org.eclipse.egit.github.core.RepositoryCommit;
+import org.eclipse.egit.github.core.RepositoryCommitCompare;
+import org.eclipse.egit.github.core.client.GitHubClient;
+import org.eclipse.egit.github.core.client.GitHubRequest;
+import org.eclipse.egit.github.core.client.PageIterator;
+import org.eclipse.egit.github.core.client.PagedRequest;
+
+/**
+ * Service for interacting with repository commits
+ *
+ * @see GitHub commit API
+ * documentation
+ */
+public class CommitService extends GitHubService {
+
+ /**
+ * Create commit service
+ */
+ public CommitService() {
+ super();
+ }
+
+ /**
+ * Create commit service
+ *
+ * @param client
+ */
+ public CommitService(GitHubClient client) {
+ super(client);
+ }
+
+ /**
+ * Get all commits in given repository
+ *
+ * @param repository
+ * @return non-null but possibly empty list of repository commits
+ * @throws IOException
+ */
+ public List getCommits(IRepositoryIdProvider repository)
+ throws IOException {
+ return getCommits(repository, null, null);
+ }
+
+ /**
+ * Get all commits in given repository beginning at an optional commit SHA-1
+ * and affecting an optional path.
+ *
+ * @param repository
+ * @param sha
+ * @param path
+ * @return non-null but possibly empty list of repository commits
+ * @throws IOException
+ */
+ public List getCommits(IRepositoryIdProvider repository,
+ String sha, String path) throws IOException {
+ return getAll(pageCommits(repository, sha, path));
+ }
+
+ /**
+ * Page commits in given repository
+ *
+ * @param repository
+ * @return page iterator
+ */
+ public PageIterator pageCommits(
+ IRepositoryIdProvider repository) {
+ return pageCommits(repository, null, null);
+ }
+
+ /**
+ * Page commits in given repository
+ *
+ * @param repository
+ * @param size
+ * @return page iterator
+ */
+ public PageIterator pageCommits(
+ IRepositoryIdProvider repository, int size) {
+ return pageCommits(repository, null, null, size);
+ }
+
+ /**
+ * Page commits in given repository
+ *
+ * @param repository
+ * @param sha
+ * @param path
+ * @return page iterator
+ */
+ public PageIterator pageCommits(
+ IRepositoryIdProvider repository, String sha, String path) {
+ return pageCommits(repository, sha, path, PAGE_SIZE);
+ }
+
+ /**
+ * Page commits in given repository
+ *
+ * @param repository
+ * @param sha
+ * @param path
+ * @param size
+ * @return page iterator
+ */
+ public PageIterator pageCommits(
+ IRepositoryIdProvider repository, String sha, String path, int size) {
+ String id = getId(repository);
+ StringBuilder uri = new StringBuilder(SEGMENT_REPOS);
+ uri.append('/').append(id);
+ uri.append(SEGMENT_COMMITS);
+ PagedRequest request = createPagedRequest(PAGE_FIRST,
+ size);
+ request.setUri(uri);
+ request.setType(new TypeToken>() {
+ }.getType());
+
+ if (sha != null || path != null) {
+ Map params = new HashMap();
+ if (sha != null)
+ params.put("sha", sha); //$NON-NLS-1$
+ if (path != null)
+ params.put("path", path); //$NON-NLS-1$
+ request.setParams(params);
+ }
+
+ return createPageIterator(request);
+ }
+
+ /**
+ * Get commit with given SHA-1 from given repository
+ *
+ * @param repository
+ * @param sha
+ * @return repository commit
+ * @throws IOException
+ */
+ public RepositoryCommit getCommit(IRepositoryIdProvider repository,
+ String sha) throws IOException {
+ String id = getId(repository);
+ if (sha == null)
+ throw new IllegalArgumentException("Sha cannot be null"); //$NON-NLS-1$
+ if (sha.length() == 0)
+ throw new IllegalArgumentException("Sha cannot be empty"); //$NON-NLS-1$
+
+ StringBuilder uri = new StringBuilder(SEGMENT_REPOS);
+ uri.append('/').append(id);
+ uri.append(SEGMENT_COMMITS);
+ uri.append('/').append(sha);
+ GitHubRequest request = createRequest();
+ request.setUri(uri);
+ request.setType(RepositoryCommit.class);
+ return (RepositoryCommit) client.get(request).getBody();
+ }
+
+ /**
+ * Get all comments on commit with given SHA-1
+ *
+ * @param repository
+ * @param sha
+ * @return non-null but possibly empty list of commits
+ * @throws IOException
+ */
+ public List getComments(IRepositoryIdProvider repository,
+ String sha) throws IOException {
+ return getAll(pageComments(repository, sha));
+ }
+
+ /**
+ * Page comments on commit with given SHA-1
+ *
+ * @param repository
+ * @param sha
+ * @return page iterator over comments
+ */
+ public PageIterator pageComments(
+ IRepositoryIdProvider repository, String sha) {
+ return pageComments(repository, sha, PAGE_SIZE);
+ }
+
+ /**
+ * Page comments on commit with given SHA-1
+ *
+ * @param repository
+ * @param sha
+ * @param size
+ * @return page iterator over comments
+ */
+ public PageIterator pageComments(
+ IRepositoryIdProvider repository, String sha, int size) {
+ return pageComments(repository, sha, PAGE_FIRST, size);
+ }
+
+ /**
+ * Page comments on commit with given SHA-1
+ *
+ * @param repository
+ * @param sha
+ * @param start
+ * @param size
+ * @return page iterator over comments
+ */
+ public PageIterator pageComments(
+ IRepositoryIdProvider repository, String sha, int start, int size) {
+ String id = getId(repository);
+ if (sha == null)
+ throw new IllegalArgumentException("Sha cannot be null"); //$NON-NLS-1$
+ if (sha.length() == 0)
+ throw new IllegalArgumentException("Sha cannot be empty"); //$NON-NLS-1$
+
+ StringBuilder uri = new StringBuilder(SEGMENT_REPOS);
+ uri.append('/').append(id);
+ uri.append(SEGMENT_COMMITS);
+ uri.append('/').append(sha);
+ uri.append(SEGMENT_COMMENTS);
+ PagedRequest request = createPagedRequest(start, size);
+ request.setUri(uri);
+ request.setType(new TypeToken>() {
+ }.getType());
+ return createPageIterator(request);
+ }
+
+ /**
+ * Get commit comment with given id
+ *
+ * @param repository
+ * @param commentId
+ * @return commit comment
+ * @throws IOException
+ */
+ public CommitComment getComment(IRepositoryIdProvider repository,
+ long commentId) throws IOException {
+ String repoId = getId(repository);
+ StringBuilder uri = new StringBuilder(SEGMENT_REPOS);
+ uri.append('/').append(repoId);
+ uri.append(SEGMENT_COMMENTS);
+ uri.append('/').append(commentId);
+ GitHubRequest request = createRequest();
+ request.setUri(uri);
+ request.setType(CommitComment.class);
+ return (CommitComment) client.get(request).getBody();
+ }
+
+ /**
+ * Add comment to given commit
+ *
+ * @param repository
+ * @param sha
+ * @param comment
+ * @return created comment
+ * @throws IOException
+ */
+ public CommitComment addComment(IRepositoryIdProvider repository,
+ String sha, CommitComment comment) throws IOException {
+ String id = getId(repository);
+ if (sha == null)
+ throw new IllegalArgumentException("Sha cannot be null"); //$NON-NLS-1$
+ if (sha.length() == 0)
+ throw new IllegalArgumentException("Sha cannot be empty"); //$NON-NLS-1$
+
+ StringBuilder uri = new StringBuilder(SEGMENT_REPOS);
+ uri.append('/').append(id);
+ uri.append(SEGMENT_COMMITS);
+ uri.append('/').append(sha);
+ uri.append(SEGMENT_COMMENTS);
+ return client.post(uri.toString(), comment, CommitComment.class);
+ }
+
+ /**
+ * Edit given comment
+ *
+ * @param repository
+ * @param comment
+ * @return edited comment
+ * @throws IOException
+ */
+ public CommitComment editComment(IRepositoryIdProvider repository,
+ CommitComment comment) throws IOException {
+ String id = getId(repository);
+ if (comment == null)
+ throw new IllegalArgumentException("Comment cannot be null"); //$NON-NLS-1$
+
+ StringBuilder uri = new StringBuilder(SEGMENT_REPOS);
+ uri.append('/').append(id);
+ uri.append(SEGMENT_COMMENTS);
+ uri.append('/').append(comment.getId());
+ return client.post(uri.toString(), comment, CommitComment.class);
+ }
+
+ /**
+ * Delete commit comment with given id from given repository
+ *
+ * @param repository
+ * @param commentId
+ * @throws IOException
+ */
+ public void deleteComment(IRepositoryIdProvider repository, long commentId)
+ throws IOException {
+ String id = getId(repository);
+ StringBuilder uri = new StringBuilder(SEGMENT_REPOS);
+ uri.append('/').append(id);
+ uri.append(SEGMENT_COMMENTS);
+ uri.append('/').append(commentId);
+ client.delete(uri.toString());
+ }
+
+ /**
+ * Compare base and head commits
+ *
+ * @param repository
+ * @param base
+ * @param head
+ * @return commit compare
+ * @throws IOException
+ */
+ public RepositoryCommitCompare compare(IRepositoryIdProvider repository,
+ String base, String head) throws IOException {
+ String id = getId(repository);
+ if (base == null)
+ throw new IllegalArgumentException("Base cannot be null"); //$NON-NLS-1$
+ if (base.length() == 0)
+ throw new IllegalArgumentException("Base cannot be empty"); //$NON-NLS-1$
+
+ if (head == null)
+ throw new IllegalArgumentException("Head cannot be null"); //$NON-NLS-1$
+ if (head.length() == 0)
+ throw new IllegalArgumentException("Head cannot be empty"); //$NON-NLS-1$
+
+ StringBuilder uri = new StringBuilder(SEGMENT_REPOS);
+ uri.append('/').append(id);
+ uri.append(SEGMENT_COMPARE);
+ uri.append('/').append(base).append("...").append(head); //$NON-NLS-1$
+ GitHubRequest request = createRequest();
+ request.setType(RepositoryCommitCompare.class);
+ request.setUri(uri);
+ return (RepositoryCommitCompare) client.get(request).getBody();
+ }
+
+ /**
+ * Get statuses for commit SHA-1
+ *
+ * @param repository
+ * @param sha
+ * @return list of statuses
+ * @throws IOException
+ */
+ public List getStatuses(IRepositoryIdProvider repository,
+ String sha) throws IOException {
+ String id = getId(repository);
+ if (sha == null)
+ throw new IllegalArgumentException("SHA-1 cannot be null"); //$NON-NLS-1$
+ if (sha.length() == 0)
+ throw new IllegalArgumentException("SHA-1 cannot be empty"); //$NON-NLS-1$
+
+ StringBuilder uri = new StringBuilder(SEGMENT_REPOS);
+ uri.append('/').append(id);
+ uri.append(SEGMENT_STATUSES);
+ uri.append('/').append(sha);
+ PagedRequest