/* * FindBugs - Find Bugs in Java programs * Copyright (C) 2003-2008 University of Maryland * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package edu.umd.cs.findbugs.cloud; import java.io.IOException; import java.io.PrintWriter; import java.net.URL; import java.util.Collection; import java.util.Date; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; import java.util.Set; import java.util.concurrent.TimeUnit; import javax.annotation.CheckForNull; import edu.umd.cs.findbugs.BugCollection; import edu.umd.cs.findbugs.BugDesignation; import edu.umd.cs.findbugs.BugInstance; import edu.umd.cs.findbugs.IGuiCallback; import edu.umd.cs.findbugs.SystemProperties; /** * An interface for describing how a bug collection interacts with the FindBugs * Cloud. * * Each Cloud instance is associated with a BugCollection. */ public interface Cloud { CloudPlugin getPlugin(); String getCloudName(); BugCollection getBugCollection(); IGuiCallback getGuiCallback(); /** * Get a status message for the cloud; information about any errors, and * information about database synchronization */ String getStatusMsg(); public void printCloudSummary(PrintWriter w, Iterable<BugInstance> bugs, String[] packagePrefixes); public void addListener(CloudListener listener); public void removeListener(CloudListener listener); public void addStatusListener(CloudStatusListener cloudStatusListener); public void removeStatusListener(CloudStatusListener cloudStatusListener); // ========================== initialization // ==================================== /** * Do we have the configuration information needed to try initializing the * cloud; calling this method should have no side effects and not display * any dialogs or make any network connections. * * @return true if we have the needed information */ public boolean availableForInitialization(); /** * Attempt to initialize the cloud * * @return true if successful */ public boolean initialize() throws IOException; /** Return true if the cloud has been successfully initialized */ public boolean isInitialized(); /** * Waits until all new issues have been uploaded */ public void waitUntilNewIssuesUploaded(); public boolean waitUntilNewIssuesUploaded(long timeout, TimeUnit unit) throws InterruptedException; /** * Waits until all data about this bug collection has been received from the * cloud. */ public void waitUntilIssueDataDownloaded(); public boolean waitUntilIssueDataDownloaded(long timeout, TimeUnit unit) throws InterruptedException; /** * Returns true if communication has already been initiated (and perhaps completed). * */ public boolean communicationInitiated(); /** * Called after the bugs in the bug collection are loaded; bugs should not * be synchronized before this method is called */ public void bugsPopulated(); /** * Initiate communication with the cloud. Clouds can implement lazy * communication, where they don't initiate communication with the cloud * until a request for cloud data is seen, or a call is made to * {@link #waitUntilIssueDataDownloaded()}. A call to this method forces * eager initiation of communication. */ public void initiateCommunication(); /** Shutdown the cloud, note termination of session, close connections */ public void shutdown(); // ================ signin / signout ================= String getUser(); SigninState getSigninState(); /** * Whether the cloud should save login information, session ID's, etc. If * disabled, the user will need to re-authenticate each session. */ void setSaveSignInInformation(boolean save); boolean isSavingSignInInformationEnabled(); void signIn() throws IOException; void signOut(); // ================================= misc settings // ============================= /** Get voting mode */ Mode getMode(); /** Set voting mode */ void setMode(Mode m); /** Does the cloud support source lines (e.g., to FishEye) */ boolean supportsSourceLinks(); /** Supports links to a bug database */ boolean supportsBugLinks(); /** Supports textual summaries about the status of a bug */ boolean supportsCloudReports(); /** Supports allowing users to claim a bug */ boolean supportsClaims(); boolean supportsCloudSummaries(); /** * Get a list of names of FB projects that the given class * "may be a part of." Used for filing bugs. */ Collection<String> getProjects(String className); // ===================================== bug instances // ============================= /** * returns whether the bug is stored remotely or not. for bug collection * storage, always returns true */ boolean isInCloud(BugInstance b); boolean isOnlineCloud(); /** has the user said they will fix this bug */ boolean getIWillFix(BugInstance b); /** Tool tip text for "view source" button */ String getSourceLinkToolTip(@CheckForNull BugInstance b); /** URL to view the source for a bug instance */ URL getSourceLink(BugInstance b); /** get the bug filing status for a bug instance */ BugFilingStatus getBugLinkStatus(BugInstance b); /** * A textual description of the bug status (e.g., FIX_LATER, ASSIGNED, * OBSOLETE, WILL_NOT_FIX) */ String getBugStatus(BugInstance b); /** has the issue been marked "will not be fixed" in a bug tracker */ boolean getWillNotBeFixed(BugInstance b); /** does the issue have an unassigned issue in the bug tracker */ boolean getBugIsUnassigned(BugInstance b); /** Get link for bug, either to file one or to view it */ URL getBugLink(BugInstance b); String getBugLinkType(BugInstance instance); URL fileBug(BugInstance b); void setBugLinkOnCloudAndStoreIssueDetails(BugInstance b, String viewUrl, String linkType) throws IOException, SignInCancelledException; /** Updates the local cache of bug reporting status. Does not modify server code. */ void updateBugStatusCache(BugInstance b, String status); /** * Note that we've initiated or completed a request to file a bug; * * @param b * bug against which bug was filed * @param bugLink * if we have any information about the result of filing the bug, * it should go here */ void bugFiled(BugInstance b, @CheckForNull Object bugLink); String getCloudReport(BugInstance b); String getCloudReportWithoutMe(BugInstance b); /** Get the user who has claimed a bug; null if no one has */ @CheckForNull String claimedBy(BugInstance b); /** * Claim the bug * * @return true if no one else has already done so */ boolean claim(BugInstance b); /** Return the time the user last changed their evaluation of this bug */ long getUserTimestamp(BugInstance b); Date getUserDate(BugInstance b); /** Get the most recent BugDesignation from the current user */ BugDesignation getPrimaryDesignation(BugInstance b); /** Get the user's designation for the bug */ UserDesignation getUserDesignation(BugInstance b); /** Get free text evaluation of the bug */ String getUserEvaluation(BugInstance b); double getClassificationScore(BugInstance b); double getClassificationVariance(BugInstance b); double getClassificationDisagreement(BugInstance b); double getPortionObsoleteClassifications(BugInstance b); int getNumberReviewers(BugInstance b); Set<String> getReviewers(BugInstance b); long getFirstSeen(BugInstance b); void addDateSeen(BugInstance b, long when); /** * @return {@link UserDesignation#UNCLASSIFIED} if no consensus has been reached */ UserDesignation getConsensusDesignation(BugInstance b); boolean overallClassificationIsNotAProblem(BugInstance b); // =========================== mutators =========================== /** * Is this bug one that gets persisted to the cloud? We may decide that we * don't persist low confidence issues to the database to avoid overloading * it */ boolean canStoreUserAnnotation(BugInstance bugInstance); /** * Update user designation and evaluation from information in bug instance * and push to database */ void storeUserAnnotation(BugInstance bugInstance); // ========================= statistics =========================== interface CloudListener { void issueUpdated(BugInstance bug); void statusUpdated(); void taskStarted(CloudTask task); } interface CloudStatusListener { void handleIssueDataDownloadedEvent(); void handleStateChange(SigninState oldState, SigninState state); } interface CloudTask { String getName(); String getStatusLine(); double getPercentCompleted(); void addListener(CloudTaskListener listener); void removeListener(CloudTaskListener listener); boolean isFinished(); void setUseDefaultListener(boolean enabled); } interface CloudTaskListener { void taskStatusUpdated(String statusLine, double percentCompleted); void taskFinished(); void taskFailed(String message); } enum SigninState { NO_SIGNIN_REQUIRED, UNAUTHENTICATED, SIGNING_IN, SIGNED_IN, SIGNIN_FAILED, SIGNIN_DECLINED, SIGNED_OUT, DISCONNECTED; /** Can download issues without asking to sign in */ public boolean canDownload() { switch (this) { case NO_SIGNIN_REQUIRED: case SIGNING_IN: case SIGNED_IN: case UNAUTHENTICATED: return true; default: return false; } } /** Can upload issues without asking to sign in */ public boolean canUpload() { switch (this) { case NO_SIGNIN_REQUIRED: case SIGNING_IN: case SIGNED_IN: return true; default: return false; } } /** Should ask to sign in if new issues to upload found */ public boolean shouldAskToSignIn() { switch (this) { case UNAUTHENTICATED: case SIGNED_OUT: case SIGNIN_FAILED: return true; default: return false; } } /** Could ask to sign in if new issues to upload found */ public boolean couldSignIn() { switch (this) { case UNAUTHENTICATED: case DISCONNECTED: case SIGNED_OUT: case SIGNIN_FAILED: case SIGNIN_DECLINED: return true; default: return false; } } @edu.umd.cs.findbugs.internalAnnotations.StaticConstant static final ResourceBundle names = ResourceBundle.getBundle(Cloud.class.getName(), Locale.getDefault()); @Override public String toString() { try { return names.getString(this.name()).trim(); } catch (MissingResourceException e) { return this.name(); } } } enum UserDesignation { UNCLASSIFIED, NEEDS_STUDY, BAD_ANALYSIS, NOT_A_BUG, MOSTLY_HARMLESS, SHOULD_FIX, MUST_FIX, I_WILL_FIX, OBSOLETE_CODE; public int score() { switch (this) { case BAD_ANALYSIS: return -3; case NOT_A_BUG: case OBSOLETE_CODE: return -2; case MOSTLY_HARMLESS: return -1; case SHOULD_FIX: return 1; case MUST_FIX: case I_WILL_FIX: return 2; default: return 0; } } /** * @return */ public boolean nonVoting() { return this == UserDesignation.OBSOLETE_CODE || this == UserDesignation.NEEDS_STUDY || this == UserDesignation.UNCLASSIFIED; } public boolean notAProblem() { return this.score() < 0; } public boolean shouldFix() { return this.score() > 0; } } enum Mode { COMMUNAL, VOTING, SECRET } enum BugFilingStatus { /** No bug yet filed */ FILE_BUG(SystemProperties.getProperty("findbugs.filebug.label", "File bug")) { @Override public boolean bugIsFiled() { return false; } }, /** URL was sent to browser, but request has expired */ FILE_AGAIN("File again"), /** Sent a URL to a browser to file a bug, no information yet */ BUG_PENDING("Bug pending") { @Override public boolean linkEnabled() { return false; } }, /** synchronized bug instance with bug database */ VIEW_BUG(SystemProperties.getProperty("findbugs.viewbug.label", "View bug")), /* Not applicable, bug linking not supported */ NA("") { @Override public boolean linkEnabled() { return false; } @Override public boolean bugIsFiled() { return false; } }; final String displayName; public boolean bugIsFiled() { return true; } public boolean linkEnabled() { return true; } BugFilingStatus(String name) { this.displayName = name; } @Override public String toString() { return displayName; } } }