/* Copyright (c) 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package sample.projecthosting; import com.google.gdata.client.projecthosting.IssuesQuery; import com.google.gdata.client.projecthosting.ProjectHostingService; import com.google.gdata.data.HtmlTextConstruct; import com.google.gdata.data.Person; import com.google.gdata.data.TextContent; import com.google.gdata.data.projecthosting.BlockedOn; import com.google.gdata.data.projecthosting.BlockedOnUpdate; import com.google.gdata.data.projecthosting.Blocking; import com.google.gdata.data.projecthosting.Cc; import com.google.gdata.data.projecthosting.CcUpdate; import com.google.gdata.data.projecthosting.IssueCommentsEntry; import com.google.gdata.data.projecthosting.IssueCommentsFeed; import com.google.gdata.data.projecthosting.IssuesEntry; import com.google.gdata.data.projecthosting.IssuesFeed; import com.google.gdata.data.projecthosting.Label; import com.google.gdata.data.projecthosting.Owner; import com.google.gdata.data.projecthosting.Updates; import com.google.gdata.util.AuthenticationException; import com.google.gdata.util.ServiceException; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * This is a simple client that provides high-level operations on the Google * Code Project Hosting Issue Tracker Data API. It can also be used as a * command-line application to test out some of the features of the API. * * */ public class ProjectHostingClient { private ProjectHostingService service; private static final String FEED_URI_BASE = "https://code.google.com/feeds/issues"; private static final String PROJECTION = "/full"; // User-provided input private String project; private String username; private String password; /** Issues API base URL constructed from the given project name. */ private String issuesBaseUri; /** Default issues feed URL constructed from the given project name. */ private URL issuesFeedUrl; /** Group 1 of the regex will match the ID of the issue. */ private Pattern issueIdPattern; /** Group 1 of the regex will match the ID of the comment. */ private Pattern commentIdPattern; private static final String DIVIDER = "-----------------------------------------------------------------------"; /** * Constructs a new client. * * @throws AuthenticationException if authentication fails * @throws MalformedURLException if there's a problem with URL */ public ProjectHostingClient( ProjectHostingService service, String project, String username, String password) throws AuthenticationException, MalformedURLException { this.service = service; this.project = project; this.username = username; this.password = password; // Login via ClientLogin if ((username != null) && (password != null)) { service.setUserCredentials(username, password); } issuesBaseUri = FEED_URI_BASE + "/p/" + project + "/issues"; issuesFeedUrl = makeIssuesFeedUrl(project); String issuesBaseUriHttp = issuesBaseUri.replaceFirst("https", "http"); issueIdPattern = Pattern.compile( issuesBaseUriHttp + PROJECTION + "/(\\d+)$"); commentIdPattern = Pattern.compile( issuesBaseUriHttp + "/\\d+/comments" + PROJECTION + "/(\\d+)$"); } /** * Getter method for {@code issuesFeedUrl}. */ protected URL getIssuesFeedUrl() { return issuesFeedUrl; } /** * Setter method for {@code issuesFeedUrl}. */ protected void setIssuesFeedUrl(URL url) { issuesFeedUrl = url; } /** * Constructs issues feed URL. * * @param proj name of the project for the issues feed * @throws MalformedURLException if there's a problem with URL */ protected URL makeIssuesFeedUrl(String proj) throws MalformedURLException { return new URL(FEED_URI_BASE + "/p/" + proj + "/issues" + PROJECTION); } /** * Constructs issue entry URL. * * @param issueId ID number of an issue to construct the issue entry URL * @throws MalformedURLException if there's a problem with URL */ protected URL makeIssueEntryUrl(String issueId) throws MalformedURLException { return new URL(issuesBaseUri + PROJECTION + "/" + issueId); } /** * Constructs comments feed URL. * * @param issueId ID number of an issue to construct the comments URL * @throws MalformedURLException if there's a problem with URL */ protected URL makeCommentsFeedUrl(String issueId) throws MalformedURLException { return new URL(issuesBaseUri + "/" + issueId + "/comments" + PROJECTION); } /** * Constructs comment entry URL. * * @param issueId ID number of an issue the comment belongs to * @param commentId ID number of the comment * @throws MalformedURLException if there's a problem with URL */ protected URL makeCommentEntryUrl(String issueId, String commentId) throws MalformedURLException { return new URL(issuesBaseUri + "/" + issueId + "/comments" + PROJECTION + "/" + commentId); } /** * Retrieves issues feed. * * @param feedUrl feed URL of issues to retrieve * @throws IOException if there is a problem communicating with the server * @throws ServiceException if the service is unable to handle the request */ protected IssuesFeed getIssuesFeed(URL feedUrl) throws IOException, ServiceException { return service.getFeed(feedUrl, IssuesFeed.class); } /** * Retrieves a particular issue entry. * * @param issueId ID number of an issue to retrieve * @throws IOException if there is a problem communicating with the server * @throws ServiceException if the service is unable to handle the request */ protected IssuesEntry getIssueEntry(String issueId) throws IOException, ServiceException { return getIssueEntry(makeIssueEntryUrl(issueId)); } /** * Retrieves a particular issue entry. * * @param entryUrl URL of an issue entry to retrieve * @throws IOException if there is a problem communicating with the server * @throws ServiceException if the service is unable to handle the request */ protected IssuesEntry getIssueEntry(URL entryUrl) throws IOException, ServiceException { return service.getEntry(entryUrl, IssuesEntry.class); } /** * Retrieves comments feed. * * @param issueId ID number of an issue to retrieve comments from * @throws IOException if there is a problem communicating with the server * @throws ServiceException if the service is unable to handle the request */ protected IssueCommentsFeed getCommentsFeed(String issueId) throws IOException, ServiceException { return getCommentsFeed(makeCommentsFeedUrl(issueId)); } /** * Retrieves comments feed. * * @param feedUrl comments feed URL to retrieve * @throws IOException if there is a problem communicating with the server * @throws ServiceException if the service is unable to handle the request */ protected IssueCommentsFeed getCommentsFeed(URL feedUrl) throws IOException, ServiceException { return service.getFeed(feedUrl, IssueCommentsFeed.class); } /** * Retrieves a particular comment entry. * * @param issueId ID number of an issue the comment belongs to * @param commentId ID number of a comment to retrieve * @throws IOException if there is a problem communicating with the server * @throws ServiceException if the service is unable to handle the request */ protected IssueCommentsEntry getCommentEntry( String issueId, String commentId) throws IOException, ServiceException { return getCommentEntry(makeCommentEntryUrl(issueId, commentId)); } /** * Retrieves a particular comment entry. * * @param entryUrl comment entry URL to retrieve * @throws IOException if there is a problem communicating with the server * @throws ServiceException if the service is unable to handle the request */ protected IssueCommentsEntry getCommentEntry(URL entryUrl) throws IOException, ServiceException { return service.getEntry(entryUrl, IssueCommentsEntry.class); } /** * Inserts an issue entry to the issues feed. * * @param entry issue entry to insert * @throws IOException if there is a problem communicating with the server * @throws ServiceException if the service is unable to handle the request */ protected IssuesEntry insertIssue(IssuesEntry entry) throws IOException, ServiceException { return service.insert(issuesFeedUrl, entry); } /** * Inserts a comment entry to the comments feed. * * @param issueId ID number of an issue to insert the comment entry to * @param entry comment entry to insert * @throws IOException if there is a problem communicating with the server * @throws ServiceException if the service is unable to handle the request */ protected IssueCommentsEntry insertComment( String issueId, IssueCommentsEntry entry) throws IOException, ServiceException { return insertComment(makeCommentsFeedUrl(issueId), entry); } /** * Inserts a comment entry to the comments feed. * * @param commentsFeedUrl comments feed URL to insert the comment entry to * @param entry comment entry to insert * @throws IOException if there is a problem communicating with the server * @throws ServiceException if the service is unable to handle the request */ protected IssueCommentsEntry insertComment( URL commentsFeedUrl, IssueCommentsEntry entry) throws IOException, ServiceException { return service.insert(commentsFeedUrl, entry); } /** * Queries issues using the given query parameters. * * @param query issues query object with query parameters set * @throws IOException if there is a problem communicating with the server * @throws ServiceException if the service is unable to handle the request */ protected IssuesFeed queryIssues(IssuesQuery query) throws IOException, ServiceException { return service.query(query, IssuesFeed.class); } /** * Returns the numeric issue ID from the given {@code issueUrl}. * * @param issueUrl URI to find the issue ID from */ protected String getIssueId(String issueUrl) { Matcher matcher = issueIdPattern.matcher(issueUrl); return matcher.matches() ? matcher.group(1) : null; } /** * Returns the numeric commentId from the given {@code commentUrl}. * * @param commentUrl URI to find the comment ID from */ protected String getCommentId(String commentUrl) { Matcher matcher = commentIdPattern.matcher(commentUrl); return matcher.matches() ? matcher.group(1) : null; } /** * Prints issues in the given issues feed. * * @param issuesFeed issues feed to print */ protected void printIssues(IssuesFeed issuesFeed) { for (IssuesEntry issueEntry : issuesFeed.getEntries()) { printIssue(issueEntry); } } /** * Prints an issue entry in human-readable format. * * @param entry issue entry to print */ protected void printIssue(IssuesEntry entry) { System.out.println(DIVIDER); if (entry.getId() != null) { String issueId = getIssueId(entry.getId()); System.out.printf("Issue #%s:\t%s\n", issueId, entry.getId()); } else { System.out.println("Issue"); } if (entry.getTitle() != null) { System.out.println("\tSummary\n\t\t" + entry.getTitle().getPlainText()); } Person author = entry.getAuthors().get(0); printPerson("Reporter", author.getName(), author.getUri()); TextContent textContent = (TextContent) entry.getContent(); if ((textContent != null) && (textContent.getContent() != null)) { HtmlTextConstruct textConstruct = (HtmlTextConstruct) textContent.getContent(); System.out.println("\tDescription\n\t\t" + textConstruct.getHtml()); } if (entry.hasStatus()) { System.out.println("\tStatus\n\t\t" + entry.getStatus().getValue()); } if (entry.hasOwner()) { Owner owner = entry.getOwner(); printPerson( "Owner", owner.getUsername().getValue(), (owner.getUri() == null) ? null : owner.getUri().getValue()); } if (entry.getLabels().size() > 0) { System.out.println("\tLabel"); for (Label label : entry.getLabels()) { System.out.println("\t\t" + label.getValue()); } } if (entry.getCcs().size() > 0) { System.out.println("\tCC"); for (Cc cc : entry.getCcs()) { printPerson( null, cc.getUsername().getValue(), (cc.getUri() == null) ? null : cc.getUri().getValue()); } } if (entry.getBlockedOns().size() > 0) { System.out.println("\tBlockedOn"); for (BlockedOn blockedOn : entry.getBlockedOns()) { System.out.print("\t\t"); if (blockedOn.hasProject()) { System.out.print(blockedOn.getProject().getValue() + ":"); } System.out.println(blockedOn.getId().getValue()); } } if (entry.getBlockings().size() > 0) { System.out.println("\tBlocking"); for (Blocking blocking : entry.getBlockings()) { System.out.print("\t\t"); if (blocking.hasProject()) { System.out.print(blocking.getProject().getValue() + ":"); } System.out.println(blocking.getId().getValue()); } } if (entry.hasMergedInto()) { System.out.print("\tMergedInto\n\t\t"); if (entry.getMergedInto().hasProject()) { System.out.print(entry.getMergedInto().getProject().getValue() + ":"); } System.out.println(entry.getMergedInto().getId().getValue()); } } /** * Prints Username and URI associated with the labeled person. * * @param label label to print * @param name username of the person to print * @param uri uri of the person to print, usually the profile url */ protected void printPerson(String label, String name, String uri) { if (label != null) { System.out.printf("\t%s\n", label); } System.out.print("\t\t" + name); if (uri != null) { System.out.println("\t" + uri); } else { System.out.println(); } } /** * Prints issues and their comments. * * @param issuesFeed issues feed to print * @throws IOException if there is a problem communicating with the server * @throws ServiceException if the service is unable to handle the request */ protected void printIssuesAndComments(IssuesFeed issuesFeed) throws IOException, ServiceException { for (IssuesEntry issueEntry : issuesFeed.getEntries()) { printIssueAndComments(issueEntry); } } /** * Prints an issue and its comments. * * @param issueId ID number of an issue to print * @throws IOException if there is a problem communicating with the server * @throws ServiceException if the service is unable to handle the request */ protected void printIssueAndComments(String issueId) throws IOException, ServiceException { printIssueAndComments(getIssueEntry(issueId)); } /** * Prints an issue and its comments. * * @param issueUrl URL of an issue to print * @throws IOException if there is a problem communicating with the server * @throws ServiceException if the service is unable to handle the request */ protected void printIssueAndComments(URL issueUrl) throws IOException, ServiceException { printIssueAndComments(getIssueEntry(issueUrl)); } /** * Prints an issue and its comments. * * @param issue issue entry to print * @throws IOException if there is a problem communicating with the server * @throws ServiceException if the service is unable to handle the request */ protected void printIssueAndComments(IssuesEntry entry) throws IOException, ServiceException { printIssue(entry); String issueId = getIssueId(entry.getId()); IssueCommentsFeed commentsFeed = getCommentsFeed(issueId); printComments(commentsFeed); } /** * Prints comments in the given comments feed. * * @param commentsFeed comments feed to print */ protected void printComments(IssueCommentsFeed commentsFeed) { for (IssueCommentsEntry commentEntry : commentsFeed.getEntries()) { printComment(commentEntry); } } /** * Prints a comment entry in human-readable format. * * @param entry comment entry to print */ protected void printComment(IssueCommentsEntry entry) { System.out.println(DIVIDER); if (entry.getId() != null) { String commentId = getCommentId(entry.getId()); System.out.printf("Comment #%s:\t%s\n", commentId, entry.getId()); } else { System.out.println("Comment"); } Person author = entry.getAuthors().get(0); printPerson("Author", author.getName(), author.getUri()); TextContent textContent = (TextContent) entry.getContent(); if ((textContent != null) && (textContent.getContent() != null)) { HtmlTextConstruct textConstruct = (HtmlTextConstruct) textContent.getContent(); System.out.println("\tComment\n\t\t" + textConstruct.getHtml()); } if (entry.hasUpdates()) { Updates updates = entry.getUpdates(); if (updates.hasSummary()) { System.out.println("\tSummary\n\t\t" + updates.getSummary().getValue()); } if (updates.hasStatus()) { System.out.println("\tStatus\n\t\t" + updates.getStatus().getValue()); } if (updates.hasOwnerUpdate()) { System.out.println( "\tOwner\n\t\t" + updates.getOwnerUpdate().getValue()); } if (updates.getLabels().size() > 0) { System.out.println("\tLabel"); for (Label label : updates.getLabels()) { System.out.println("\t\t" + label.getValue()); } } if (updates.getCcUpdates().size() > 0) { System.out.println("\tCC"); for (CcUpdate cc : updates.getCcUpdates()) { System.out.println("\t\t" + cc.getValue()); } } if (updates.getBlockedOnUpdates().size() > 0) { System.out.println("\tBlockedOnUpdate"); for (BlockedOnUpdate blockedOnUpdate : updates.getBlockedOnUpdates()) { System.out.println("\t\t" + blockedOnUpdate.getValue()); } } if (updates.hasMergedIntoUpdate()) { System.out.println( "\tMergedIntoUpdate\n\t\t" + updates.getMergedIntoUpdate().getValue()); } } } }